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

时间:2011-02-24 11:54:01

标签: django modelform

我需要制作一个表单,其中包含1个选择和1个文本输入。必须从数据库中选择。 模型看起来像这样:

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

    def __unicode__(self):
        return self.name

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

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

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

但它不起作用。 select标签不会以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 个答案:

答案 0 :(得分:12)

阅读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 )),
        }

答案 1 :(得分:6)

ModelForm可满足您的所有需求(另请查看Conversion List

型号:

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

答案 2 :(得分: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,))

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

答案 3 :(得分:0)

maersu Yuji'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 = ...