14

我需要制作一个表单,它有 1 个选择和 1 个文本输入。选择必须取自数据库。模型看起来像这样:

class Province(models.Model):
    name = models.CharField(max_length=30)
    slug = models.SlugField(max_length=30)

    def __unicode__(self):
        return self.name

它的行仅由管理员添加,但所有用户都可以在表单中看到它。我想从中制作一个 ModelForm 。我做了这样的事情:

class ProvinceForm(ModelForm):
    class Meta:
        CHOICES = Province.objects.all()

        model = Province
        fields = ('name',)
        widgets = {
            'name': Select(choices=CHOICES),
        }

但它不起作用。选择标签不显示在 html 中。我做错了什么?

更新:

该解决方案可以正常工作,因为我希望它可以正常工作:

class ProvinceForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ProvinceForm, self).__init__(*args, **kwargs)
        user_provinces = UserProvince.objects.select_related().filter(user__exact=self.instance.id).values_list('province')
        self.fields['name'].queryset = Province.objects.exclude(id__in=user_provinces).only('id', 'name')

    name = forms.ModelChoiceField(queryset=None, empty_label=None)

    class Meta:
        model = Province
        fields = ('name',)
4

4 回答 4

19

阅读 Maersu 对“有效”的方法的回答。

如果你想自定义,知道选择需要一个元组列表,即 (('val','display_val'), (...), ...)

选择文档:

2 元组的可迭代(例如,列表或元组)用作该字段的选择。

from django.forms.widgets import Select


class ProvinceForm(ModelForm):
    class Meta:
        CHOICES = Province.objects.all()

        model = Province
        fields = ('name',)
        widgets = {
            'name': Select(choices=( (x.id, x.name) for x in CHOICES )),
        }
于 2011-02-24T13:08:39.743 回答
6

ModelForm满足您的所有需求(另请查看转换列表

模型:

class UserProvince(models.Model):
    user = models.ForeignKey(User)
    province = models.ForeignKey(Province)

形式:

class ProvinceForm(ModelForm):
    class Meta:
        model = UserProvince
        fields = ('province',)

看法:

   if request.POST:
        form = ProvinceForm(request.POST)
        if form.is_valid():
            obj = form.save(commit=True)
            obj.user = request.user
            obj.save()
   else:
        form = ProvinceForm() 
于 2011-02-24T12:50:05.000 回答
3

如果您需要使用查询,choices那么您需要覆盖__init__表单的方法。

您的第一个猜测可能是将其保存为字段列表之前的变量,但您不应该这样做,因为您希望每次访问表单时都更新查询。您会看到,一旦您运行服务器,就会生成选择,并且在您下次重新启动服务器之前不会改变。这意味着您的查询将只执行一次并且永远保持平静。

# Don't do this
class MyForm(forms.Form):
    # Making the query
    MYQUERY = User.objects.values_list('id', 'last_name')
    myfield = forms.ChoiceField(choices=(*MYQUERY,))

    class Meta:
        fields = ('myfield',)

这里的解决方案是利用__init__在每次表单加载时调用的方法。这样,您的查询结果将始终更新。

# Do this instead
class MyForm(forms.Form):
    class Meta:
        fields = ('myfield',)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Make the query here
        MYQUERY = User.objects.values_list('id', 'last_name')
        self.fields['myfield'] = forms.ChoiceField(choices=(*MYQUERY,))

如果您有很多用户,那么查询您的数据库可能会很繁重,因此我建议将来进行一些缓存可能会有用。

于 2020-04-13T07:57:07.413 回答
1

maersuYuji 'Tomita' Tomita给出的两个解决方案完美地工作,但是在某些情况下不能使用ModelForm(django3 链接),即表单需要来自多个模型的源/是 ModelForm 类的子类并且想要添加一个额外的字段,包含来自另一个模型的选项等。

ChoiceField在我看来是一种更通用的方式来满足需求。

下面的示例提供了来自两个模型的两个选项字段和每个模型的空白选项:

class MixedForm(forms.Form):
    speaker = forms.ChoiceField(choices=([['','-'*10]]+[[x.id, x.__str__()] for x in Speakers.objects.all()]))
    event = forms.ChoiceField(choices=( [['','-'*10]]+[[x.id, x.__str__()] for x in Events.objects.all()]))

如果不需要空白字段,或者不需要为选择标签使用函数而是模型字段或属性,它可以更优雅一点,正如eugene建议的那样:

class MixedForm(forms.Form):
    speaker = forms.ChoiceField(choices=((x.id, x.__str__()) for x in Speakers.objects.all()))
    event = forms.ChoiceField(choices=(Events.objects.values_list('id', 'name')))

使用 values_list() 和一个空白字段:

    event = forms.ChoiceField(choices=([['','-------------']] + list(Events.objects.values_list('id', 'name'))))

作为 ModelForm 的子类,使用robos85问题之一:

class MixedForm(ProvinceForm):
    speaker = ...
于 2020-04-05T10:15:01.620 回答