Django ModelForms:将ManyToMany字段显示为单选 [英] Django ModelForms: Display ManyToMany field as single-select
问题描述
在Django应用中,我有一个模型Bet,其中包含与Django的User模型的ManyToMany关系:
class Bet(models.Model):
...
参与者= models.ManyToManyField(用户)
用户应该可以使用表单开始新的投注。直到现在,投注只有两个参与者,其中一个是自己创建投注的用户。这意味着在新赌注的形式下,您必须选择一个参与者。在保存表单数据时,投注创建者被添加为参与者。
我正在使用ModelForm为我的 NewBetForm
:
class NewBetForm(forms.ModelForm):
class Meta:
model = Bet
widgets = {
'Particip':forms.Select()
}
def save(self,user):
...#将用户保存为参与者
注意参与者字段的重新定义的小部件,确保您只能选择一个参与者。 p>
但是,这给我一个验证错误:
输入一个值。
我不太确定这来自哪里。如果我在开发者工具中查看POST数据,似乎与使用默认窗口小部件并仅选择一个参与者完全相同。但是,似乎ManyToManyField的 to_python()
方法对此数据有问题。至少没有创建用户对象,如果我启用选择小部件。
我知道我可以解决这个问题,从表单中排除参与者字段并自己定义但是如果仍然可以使用ModelForm的容量(毕竟它只是一个小部件的更改),那将会更好一些。也许我可以以某种方式操纵传递的数据,如果我知道如何。
任何人都可以告诉我问题是什么,如果有一个很好的解决方法?
提前感谢!
修改
如意见中的建议:视图的(相关)代码。
def new_bet(request )
如果request.method =='POST':
form = NewBetForm(request.POST)
如果form.is_valid():
form.save(request.user )
...#成功消息和重定向
else:
form = NewBetForm()
return render(request,'bets / new.html',{'form' form})
在挖掘Django代码后,我可以回答我自己的问题。
问题是Django的ModelForm将模型中的 ManyToManyField
映射到 ModelMultipleChoiceField
s e形式。这种表单字段期望窗口小部件对象从其 value_from_datadict()
方法返回一个序列。 ModelMultipleChoiceField
(即 SelectMultiple
)的默认小部件覆盖 value_from_datadict()
从用户提供的数据返回一个列表。但是,如果我使用选择
小部件,则使用超类的默认 value_from_datadict()
方法,它只返回一个串。 ModelMultipleChoiceField
根本不喜欢,因此验证错误。
对于我可以想到的解决方案: / p>
- 覆盖
value_from_datadict()
选择
通过继承或一些类装饰器。 - 通过创建新的表单字段并调整
save()$ c来手动处理m2m字段code> ModelForm
的$ c>方法将其数据保存在m2m关系中。
秒解决方案似乎不那么冗长,所以这就是我将要做的。
In a Django app, I'm having a model Bet which contains a ManyToMany relation with the User model of Django:
class Bet(models.Model):
...
participants = models.ManyToManyField(User)
User should be able to start new bets using a form. Until now, bets have exactly two participants, one of which is the user who creates the bet himself. That means in the form for the new bet you have to chose exactly one participant. The bet creator is added as participant upon saving of the form data.
I'm using a ModelForm for my NewBetForm
:
class NewBetForm(forms.ModelForm):
class Meta:
model = Bet
widgets = {
'participants': forms.Select()
}
def save(self, user):
... # save user as participant
Notice the redefined widget for the participants field which makes sure you can only choose one participant.
However, this gives me a validation error:
Enter a list of values.
I'm not really sure where this comes from. If I look at the POST data in the developer tools, it seems to be exactly the same as if I use the default widget and choose only one participant. However, it seems like the to_python()
method of the ManyToManyField has its problems with this data. At least there is no User object created if I enable the Select widget.
I know I could work around this problem by excluding the participants field from the form and define it myself but it would be a lot nicer if the ModelForm's capacities could still be used (after all, it's only a widget change). Maybe I could manipulate the passed data in some way if I knew how.
Can anyone tell me what the problem is exactly and if there is a good way to solve it?
Thanks in advance!
Edit
As suggested in the comments: the (relevant) code of the view.
def new_bet(request):
if request.method == 'POST':
form = NewBetForm(request.POST)
if form.is_valid():
form.save(request.user)
... # success message and redirect
else:
form = NewBetForm()
return render(request, 'bets/new.html', {'form': form})
After digging in the Django code, I can answer my own question.
The problem is that Django's ModelForm maps ManyToManyField
s in the model to ModelMultipleChoiceField
s of the form. This kind of form field expects the widget object to return a sequence from its value_from_datadict()
method. The default widget for ModelMultipleChoiceField
(which is SelectMultiple
) overrides value_from_datadict()
to return a list from the user supplied data. But if I use the Select
widget, the default value_from_datadict()
method of the superclass is used, which simply returns a string. ModelMultipleChoiceField
doesn't like that at all, hence the validation error.
To solutions I could think of:
- Overriding the
value_from_datadict()
ofSelect
either via inheritance or some class decorator. - Handling the m2m field manually by creating a new form field and adjusting the
save()
method of theModelForm
to save its data in the m2m relation.
The seconds solution seems to be less verbose, so that's what I will be going with.
这篇关于Django ModelForms:将ManyToMany字段显示为单选的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!