我需要制作一个表单,其中包含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',)
答案 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 = ...