字段选项()作为查询集?

17

我需要制作一个表单,其中包括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个回答

20

阅读Maersu的答案,以获取有效的方法。

如果您想进行自定义,请注意,choices接受元组列表,例如(('val','display_val'), (...), ...)

choices文档:

  

用作此字段选项的可迭代对象(例如列表或元组)的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 )),
        }

嗨@yuij,我没有使用ModelForm,但我有一个类似的要求,在模型本身的charfield中需要从queryset中选择。我尝试了你建议的方法,但在我的情况下,它给我带来了错误,例如“django.core.exceptions.AppRegistryNotReady:Models aren't loaded yet.”,其中它说模型还没有加载。所以有什么办法可以在模型中实现它? - CrazyGeek
@CrazyGeek 无论你在哪里注入它,如果它在模型类初始化时被调用(即在应用程序准备好之前),它将是一个静态值... 直到重新启动才会发生变化。你应该确保它在ModelForm中,或者在每个请求执行的相关视图代码中。 - Yuji 'Tomita' Tomita
我没有使用ModelForm,因为我只是在Django中使用REST API。 - CrazyGeek
5
不进行小部件定制,CHOICES = Province.objects.values_list('id', 'name') 可以工作。 - eugene
我怀疑这个方法是否会按预期工作。如果我添加一个新的省份而不重启服务器,那么新的省份是否会出现在选项中,而无需重启服务器? - Mohammed Shareef C
显示剩余2条评论

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() 

6
+1: 但是不应该设置为 'commit=False',否则你将会对数据库进行两次操作。 - mo.

4
如果您需要在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,))

查询数据库如果用户量很大,会对系统性能造成压力,因此建议未来使用缓存功能。

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 = ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接