2009-08-27 17 views
3

私はHourFieldというカスタムフィールドを持っています.1時間30分を表すために "1:30"を入力できます。 clean()メソッドは、モデル内のFloatFieldの1.5に変換します。これはすべて正常に動作します。今私は、フォームセットを作成したいときに、フィールドが "1:30"ではなく "1.5"としてレンダリングされるという問題があります。これは私が望むものです。フォームがdjangoのフィールドをウィジェットに出力する方法をカスタマイズする

clean()に類似した方法がありますか?

編集:別のタイプの保管方法を使用していることは疑問です。 1つのタイプのオブジェクトとして格納されているが、ユーザーによって文字列として入力されている、いくつかのカスタムフィールドがあります。私は上記の例をもっとも単純なものだったので、これを使用しました。

答えて

3

フィールドをレンダリングする方法をカスタマイズするには、ウィジェットも定義する必要があります。

  • フォームタグ(これの世話をするのTextInputスーパー)
  • 形式フォームフィールド内の表示の値をレンダリング:

    次のコードは、ジャンゴのウィジェットの一部の責任を示しています。 ここには問題があります。値のクラスは、フィールドからきれいになった値をレンダリングしたり、フォームPOSTから取り出された汚れていないデータをレンダリングしたりする可能性があります。したがって、_format_valueに数値があるかどうかをテストします。それが文字列の場合はそのままそのまま残してください。

  • 値が変更されたかどうかを、初期データと比較して確認します。デフォルト値で書式セットを使用する場合は非常に重要です。

ウィジェットのコードは、ウィジェットの影響を受けていることに注意してください。一貫性を維持するためにdjangoのソースコードからのTimeInput。

from django import forms 
from django.forms import widgets 
from django.forms.util import ValidationError 

class HourWidget(widgets.TextInput): 

    def _format_value(self, value): 
     if isinstance(value, float) or isinstance(value, int): 
      import math 

      hours = math.floor(value) 
      minutes = (value - hours) * 60 
      value = "%d:%02d" % (hours, minutes) 

     return value 

    def render(self, name, value, attrs=None): 
     value = self._format_value(value) 
     return super(HourWidget, self).render(name, value, attrs) 

    def _has_changed(self, initial, data): 
     return super(HourWidget, self)._has_changed(self._format_value(initial), data) 

class HourField(forms.Field): 
    widget = HourWidget 

    def clean(self, value): 
     super(HourField, self).clean(value) 

     import re 
     match = re.match("^([0-9]{1,2}):([0-9]{2})$", value) 
     if not match: 
      raise ValidationError("Please enter a valid hour (ex: 12:34)") 

     groups = match.groups() 
     hour = float(groups[0]) 
     minutes = float(groups[1]) 

     if minutes >= 60: 
      raise ValidationError("Invalid value for minutes") 

     return hour + minutes/60 

1:20が1:19になることに注意してください。これは、フロートの使用によって引き起こされる精度の低下によるものです。ある精度を失いたくない場合は、データ型を変更することができます。

+0

_format_value()の%1dは10,11,12のような時間を切り捨てませんか? –

+0

"1"は 'width'パラメータで、パディングにのみ使用され、切り捨てには使用されません。つまり、左に文字が追加され、削除されることはありません。 %演算子はsprintf(http://en.wikipedia.org/wiki/Printf#printf_format_placeholders)のように動作します。 このように数字の整数部分を切り捨てることはできません。精度パラメータ( "%.2f"%0.123 == "0.12")を使用して小数部分のみを切り捨てることができます。 精度パラメータを文字列形式で使用して切り捨てることができます。 "%.1s"%123 == "1" – vincent

+0

ところで、バグは導入されませんが、この '%1d'は役に立たない任意の数値は常に整数部分の文字を生成します:-)私は "%2d:%2d"で始まり、時間部分には1つの文字のみが必要であると考えたので、これを書いています。 – vincent

0

date template tagを試してみるか、独自のテンプレートタグを作成することをおすすめします。本当に問題は、datetimeモジュールを使ってPythonにその時間を格納する必要があることです。これは、それがうまくフォーマットされていることを保証します。

+0

彼は日付/時刻値を保存していませんが、彼は期間を保存しています。 –

+0

datetimeのタイム・デルタ・モジュールが完全に機能する理由は次のとおりです。http://docs.python.org/library/datetime.html#timedelta-objects – Vince

1

データがフォームフィールドにどのように表示されるかは、ウィジェットの責任です。小数フィールド値をとり、希望通りに出力するrender()メソッドを実装するカスタムウィジェットを定義する必要があるようです。

残念ながらカスタムウィジェットの作成に関するドキュメントはありませんが、その方法の例はdjango.forms.extras.widgetsのコードをご覧ください。

1
from django.forms.widgets import TextInput 

class TimeInput(TextInput): 
    def _format_value(self, value): 
     """Convert float to time""" 
     if not value: 
      return '' 
     fraction, integer = math.modf(value) 
     minutes = 0 if fraction == 0 else .6/fraction 
     return "%d:%02d" % (int(integer), int(minutes)) 

    def render(self, name, value, attrs=None): 
     value = self._format_value(value) 
     return super(TimeInput, self).render(name, value, attrs) 
+0

Textだけでなく、TextInputである必要があります。また、どのようにModelChoiceフィールドでこのテクニックを使用しますか?値は常にNoneと思われます... – priestc

+0

実際には、私はそれを取りません。それはただオブジェクトのpkです... – priestc

+0

ModelChoiceフィールドは、そのモデルインスタンスのpkと文字列表現に基づいてオプションを生成します。これはまだHourFieldに関連していますか?もしそうなら、あなたはテーブルの設定をより明確にするでしょうか? –

関連する問題