更改django表单值

我在模型表单中更改了一个ForeignKey,改为使用TextBox . 然后我重写clean方法,根据名称字段返回对象(而不是id字段)

class SongForm(forms.ModelForm):
artist = 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值而不是名称值 . 如何更改django表单的显示值?我认为重写 init 会做,但找不到value属性的位置

回答(2)

2 years ago

我刚刚编写了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 years ago

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

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