Django 模型表单:如何为 CharField 显示 select?

Django Modelforms: how to show a select for a CharField?

我在 Django 中使用 ModelForms 创建表单。这很好用。但是,数据库中有一个字段是 charfield,但对于特定的表单,我想将用户可以输入的选项限制为特定项目的列表 - 理想情况下显示在 <select> 中。这样做的正确方法是什么?这是我的代码:

form = ModelForm(request.POST or None, instance=Street.objects.get(pk=1))

在我的模型中:

class Street(models.Model):
    name = models.CharField(max_length=255)

我试过以下方法:

from django import forms
CHOICES = [('1', 'First'), ('2', 'Second')]
choice_field = forms.ChoiceField(widget=forms.Select, choices=CHOICES)
form.fields["street"].widget = choice_field

出现此错误:

'ChoiceField' object has no attribute 'attrs'

使用 ModelForm 的首选方法是创建自己的 class:

class StreetForm(forms.ModelForm):
    class Meta:
        model = Street
        fields = '__all__'

这将创建一个用于编辑 Street 属性的表单,例如 name

如果你想创建一个街道为 select 的表格,你应该定义一个新表格 - 如果 Street 与另一个模型有关系,则另一个 ModelForm (即由 ForeignKey 定义),否则为普通 Form

假设第二种情况你应该定义:


class OtherForm(forms.Form):
    street = forms.ModelChoiceField(queryset=Street.objects.all())

要缩小街道,您有两种选择,具体取决于过滤条件。

对于静态过滤(标准在运行时不会改变),您可以通过定义过滤查询集来缩小记录范围:


class OtherForm(forms.Form):
    street = forms.ModelChoiceField(
        queryset=Street.objects.filter(name__ilike='a%')
    )

对于动态过滤,您可以在 __init__ 方法中覆盖字段的查询集实例:

class OtherForm(forms.Form):
    street = forms.ModelChoiceField(
        queryset=Street.objects.all()
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)  # must be called first
        self.fields["street"].queryset = Street.objects.filter(...)

ModelChoiceField 的默认小部件是 Select 小部件。

但是如果您需要为街道名称准备自定义选项,您应该定义一个选项列表(如您的示例所示)并重新定义 StreetFormname 字段:


STREET_NAME_CHOICES = [
   ('Street name 1', 'Street name 1'), 
   ('Street name 2', 'Street name 2')
]

class StreetForm(forms.ModelForm):
    name = forms.ChoiceField(
        widget=forms.Select,
        choices=STREET_NAME_CHOICES
    )
    class Meta:
        model = Street
        fields = '__all__'

如果您想修复此属性错误,您可以将您的选择作为 python 元组传递或设置 TypedChoiceField() 并为其赋予属性强制引用:

from django import forms
CHOICES = (("1", "First"), ("2", "Second"))
choice_field = forms.ChoiceField(widget=forms.Select, choices=CHOICES)
form.fields["street"].widget = choice_field