2010-07-23 89 views
1

我已經在模型表單中更改了一個ForeignKey,而不是使用TextBox。 然後我重寫清潔方法返回基於名稱字段(而不是id字段)更改django表單值

類SongForm(forms.ModelForm)對象:
藝術家= forms.CharField(小部件= forms.TextInput())

def clean_artist(self): 
    data = self.cleaned_data['artist'] 
    artist = Artist.objects.get(name=data) 
    self.cleaned_data['artist_id'] = artist.id 
    return artist 

class Meta: 
    model = Song 

它正確保存表格,如何再次呈現時顯示id值而不是名稱值。我該如何改變django表單的顯示值?我想重寫初始化將做到這一點,卻找不到哪裏是價值屬性

回答

2

我只是寫場和Widget子類,它們可以解決這個特殊的問題,並且可以和JS自動完成一起使用,並且可以重用。不過,它需要比解決方案更多的工作,而且我不確定是否要使用我的解決方案。無論哪種方式 - 我希望我會得到一些upvotes - 我花了相當長的一段時間和精力寫這...

相反的定義就像你你的ModelForm沒有與clean_搞亂我建議這樣的事情:

class SongForm(forms.ModelForm): 
    artist = CustomModelChoiceField(queryset = Artist.objects.all(), query_field = "name") 

    class Meta: 
     model = Song 

現在,CustomModelChoiceField(我想不出更好的名稱)是ModelChoiceField的子類,這很好,因爲我們可以使用queryset參數來縮小可接受的選擇範圍。如果widget參數不存在(如上所述),則會使用此字段的默認值(稍後會詳細介紹)。 query_field是可選的,默認爲"pk"。所以,這裏是域代碼:

class CustomModelChoiceField(forms.ModelChoiceField): 
    def __init__(self, queryset, query_field = "pk", **kwargs): 
     if "widget" not in kwargs: 
      kwargs["widget"] = ModelTextInput(model_class = queryset.model, query_field = query_field) 
     super(CustomModelChoiceField, self).__init__(queryset, **kwargs) 

    def to_python(self, value): 
     try: 
      int(value) 
     except: 
      from django.core.exceptions import ValidationError 
      raise ValidationError(self.error_messages['invalid_choice']) 
     return super(CustomModelChoiceField, self).to_python(value) 

什麼身體__init__手段是創造CustomModelChoiceField過程中設置widget = None給了我們普通ModelChoiceField(這是非常有益的,而調試...)。現在,實際的工作在ModelTextInput部件進行:

class ModelTextInput(forms.TextInput): 
    def __init__(self, model_class, query_field, attrs = None ): 
     self.model_class = model_class 
     self.query_field = query_field 
     super(ModelTextInput, self).__init__(attrs) 

    def render(self, name, value, attrs = None): 
     try: 
      obj = self.model_class.objects.get(pk = value) 
      value = getattr(obj, self.query_field) 
     except: 
      pass 
     return super(ModelTextInput, self).render(name, value, attrs) 

    def value_from_datadict(self, data, files, name): 
     try: 
      return self.model_class.objects.get(**{ self.query_field : data[name] }).id 
     except: 
      return data[name] 

它本質的TextInput,就是知道的兩個額外的東西 - 這模型它代表哪個屬性。 (model_class應替換爲queryset,以便將實際可能的選擇縮小至實際工作狀態,稍後我會予以修復)。查看value_from_datadict的實現,很容易發現爲什麼to_python必須被重寫 - 它期望值爲int,但不檢查它是否爲真 - 並僅將值傳遞給關聯的模型,該模型因醜陋的異常而失敗。

我測試了這一段時間,它的工作原理 - 你可以指定由形成場會嘗試不同的模型領域找到自己的artist,形狀誤差處理是由基類自動完成的,不需要編寫自定義的clean_方法每次你想使用類似的功能。

我現在太累了,但我會盡力編輯明天的這篇文章(和代碼)。

+0

還沒有試過,但看起來很有趣 – juanefren 2010-07-26 23:33:00

2

我剛剛得到它,初始哈希是我失蹤:

if self.instance.id: 
    val = self.initial['artist'] 
    self.initial['artist'] = Artist.objects.get(id=val).name