【问题标题】:Change django form value更改 django 表单值
【发布时间】:2011-03-20 06:45:43
【问题描述】:

我已经更改了模型表单中的 ForeignKey,改为使用 TextBox。 然后我重写 clean 方法以根据 name 字段(而不是 id 字段)返回对象

类 SongForm(forms.ModelForm):
艺术家 = forms.CharField(widget=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 值而不是 name 值。如何更改 django 表单的显示值?我认为覆盖 init 会做到,但找不到 value 属性在哪里

【问题讨论】:

    标签: django django-forms


    【解决方案1】:

    我刚刚编写了 Field 和 Widget 子类,它们解决了这个特殊问题,并且可以与 JS 自动完成一起使用,例如 - 并且是可重用的。尽管如此,它比您的解决方案需要更多的工作,我不确定您是否要使用我的解决方案。无论哪种方式 - 我希望我会得到一些支持 - 我花了很多时间和精力来写这篇文章......

    我建议不要像以前那样定义你的 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_ 方法每次你想使用类似的功能。

    我现在太累了,但我明天会尝试编辑这篇文章(和代码)。

    【讨论】:

      【解决方案2】:

      我刚刚得到它,最初的哈希是我所缺少的:

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

      【讨论】:

        猜你喜欢
        • 2013-12-01
        • 2013-09-03
        • 2011-12-14
        • 2014-07-19
        • 2017-02-28
        • 1970-01-01
        • 2011-12-05
        • 2012-01-24
        相关资源
        最近更新 更多