2010-07-23 85 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