2009-08-27 82 views
3

我有一個名爲HourField的自定義字段,它可讓您輸入「1:30」來表示一個半小時。方法中的clean()方法將其變爲1.5,以用於模型中的FloatField。這一切工作正常。現在我遇到了一個問題,當我想創建一個formset時,該字段被渲染爲「1.5」而不是「1:30」,這正是我想要的。自定義表單如何在django中向窗口小部件輸出字段

是否有某種類似於clean()的方法,但反過來呢?

編輯:使用另一種類型的存儲方法是不可能的。我有一些其他的自定義字段,它們被存儲爲一種類型的對象,但是由用戶以字符串形式輸入。我使用了上面的例子,因爲它是最直接的。

回答

3

要定製字段呈現的方式,您還需要定義一個小部件。

下面的代碼顯示窗口小部件的一些職責在Django:

  • 呈現形式標籤(超類的TextInput照顧的這個)
  • 格式表單字段內顯示的值。 這裏有一個問題:值的類可能會有所不同,因爲您可能會呈現來自該字段的清理值或來自表單POST的未清理數據。因此,測試在_format_value中是否有數值;如果它是一個字符串,就把它保持原樣。
  • 確定該值是否與初始數據相比發生了變化。使用默認值的表單集非常重要。

請注意,widget的代碼受django源代碼中widgets.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那樣截斷小時嗎? – 2009-08-31 14:01:54

+0

「1」是'width'參數,它僅用於填充,不用於截斷;也就是說,字符被添加到左側,從未刪除。 %運算符的行爲與sprintf相似(http://en.wikipedia.org/wiki/Printf#printf_format_placeholders)。 您不能用這種方法截斷數字的整數部分。只能使用精度參數截斷小數部分(「%.2f」%0.123 ==「0.12」)。 使用字符串格式的精度參數可能會發生截斷:「%.1s」%123 ==「1」 – vincent 2009-08-31 14:23:00

+0

順便說一下,雖然它沒有引入錯誤,但我同意'%1d'是無用的,因爲任何數字總會在整數部分產生至少一個字符:-)我寫這個是因爲我以「%2d:%2d」開頭,並且在小時部分只需要1個字符。 – vincent 2009-08-31 21:17:47

0

我想你可能想試試date template tag或者可能創建自己的模板標籤。儘管這是一種創可貼 - 真正的問題是您應該使用datetime模塊將時間存儲在Python中。這將保證格式良好。

+0

雖然他沒有存儲日期/時間值,但他正在存儲持續時間。 – 2009-08-27 07:59:03

+0

這就是爲什麼datetime的時間增量模塊應該完美工作的原因http://docs.python.org/library/datetime.html#timedelta-objects – Vince 2009-08-27 15:26:22

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

它應該是TextInput,而不僅僅是Text。另外,如何在ModelChoice字段上使用此技術?值似乎總是沒有... – priestc 2009-08-30 03:58:17

+0

實際上,我回過頭來,它不是沒有,它只是對象的pk ... – priestc 2009-08-30 04:05:21

+0

ModelChoice字段基於該模型實例的pk和字符串表示生成選項。這仍然與HourField有關嗎?如果是的話,你會澄清桌面設置更多? – 2009-08-30 13:03:25

相關問題