[英]Django won't save ManyToMany field on ModelForm
I've got a model similar to the following (this is a condensed version of a long form): 我有一个类似于以下的模型(这是一个长格式的精简版本):
class UserProfile(models.Model):
display_name = models.CharField()
nationality = models.ForeignKey(Nationality)
religion = models.ForeignKey(Religion)
partner_nationality = models.ManyToManyField(
Nationality,
related_name='partner_nationality',
blank=True)
partner_religion = models.ManyToManyField(
Religion,
related_name='partner_religion',
blank=True)
And the following model form: 以及以下模型形式:
class UserProfilePartnerPreferencesForm(ModelForm):
partner_nationality = ModelChoiceField(
queryset=Nationality.objects.order_by('name'),
widget=CheckboxSelectMultiple,
empty_label=None,
required=False,
to_field_name='id')
class Meta:
model = UserProfile
fields = [
'partner_religion',
'partner_nationality',
]
widgets = {
'partner_religion': CheckboxSelectMultiple(),
}
And this generic view: 这个通用视图:
class UserProfilePartnerPreferencesUpdateView(LoginRequiredMixin,
UpdateView):
model = UserProfile
form_class = UserProfilePartnerPreferencesForm
def get(self, request, *args, **kwargs):
"""
Force the PK of the object to edit to the current user's profile
"""
self.kwargs[self.pk_url_kwarg] = request.user.profile.id
return super(UserProfilePartnerPreferencesUpdateView, self).get(
request, *args, **kwargs)
def post(self, request, *args, **kwargs):
"""
Force the PK of the object to edit to the current user's profile
"""
self.kwargs[self.pk_url_kwarg] = request.user.profile.id
return super(UserProfilePartnerPreferencesUpdateView, self).post(
request, *args, **kwargs)
So what I'm doing in the model form is: 因此,我在模型表单中所做的是:
display_name
) display_name
) This form displays OK but the field where I've passed a custom queryset won't save. 此表单显示“确定”,但是我通过自定义查询集的字段将不会保存。 If I don't give any values for it, django complains with
'NoneType' object is not iterable
while trying to save the field ( partner_nationality
in this example). 如果我没有提供任何值,django会在尝试保存字段时抱怨
'NoneType' object is not iterable
(在此示例中为partner_nationality
)。 If I give it a valid value, it says that the value isn't valid. 如果我给它一个有效值,则表明该值无效。
So it seems like fields where I supply a custom queryset aren't being applied correctly when the form is saved. 因此,保存表单时,似乎无法正确应用我提供自定义查询集的字段。 If I comment out the customisation (ie
partner_nationality
in the ModelForm), it saves correctly with the default settings. 如果我注释掉该自定义项(即ModelForm中的
partner_nationality
),它将使用默认设置正确保存。
How can I pass a customised queryset and change the widget for a model's ManyToMany field? 如何传递自定义查询集并更改模型的ManyToMany字段的小部件? Also, bonus points if there's a simpler way (something like passing a parameter to the
widgets
dict (where partner_religion
is defined). 另外,如果有更简单的方法(例如,将参数传递给
widgets
dict(定义了partner_religion
),则可以加分)。
I'm using Django 1.11.1. 我正在使用Django 1.11.1。
Update 更新
Full traceback is as follows when I don't select any options: 当我不选择任何选项时,完全回溯如下:
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/user-profile/edit/preferences
Django Version: 1.11.1
Python Version: 3.5.1
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'dating.dating',
'dating_site']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/venv/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
41. response = get_response(request)
File "/venv/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/venv/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/venv/lib/python3.5/site-packages/django/views/generic/base.py" in view
68. return self.dispatch(request, *args, **kwargs)
File "/venv/lib/python3.5/site-packages/django/contrib/auth/mixins.py" in dispatch
56. return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
File "/venv/lib/python3.5/site-packages/django/contrib/auth/mixins.py" in dispatch
116. return super(UserPassesTestMixin, self).dispatch(request, *args, **kwargs)
File "/venv/lib/python3.5/site-packages/django/views/generic/base.py" in dispatch
88. return handler(request, *args, **kwargs)
File "/dating/dating/dating/views/user_profile.py" in post
99. return super(UserProfilePartnerPreferencesUpdateView, self).post(request, *args, **kwargs)
File "/venv/lib/python3.5/site-packages/django/views/generic/edit.py" in post
240. return super(BaseUpdateView, self).post(request, *args, **kwargs)
File "/venv/lib/python3.5/site-packages/django/views/generic/edit.py" in post
183. return self.form_valid(form)
File "/venv/lib/python3.5/site-packages/django/views/generic/edit.py" in form_valid
162. self.object = form.save()
File "/venv/lib/python3.5/site-packages/django/forms/models.py" in save
452. self._save_m2m()
File "/venv/lib/python3.5/site-packages/django/forms/models.py" in _save_m2m
434. f.save_form_data(self.instance, cleaned_data[f.name])
File "/venv/lib/python3.5/site-packages/django/db/models/fields/related.py" in save_form_data
1686. getattr(instance, self.attname).set(data)
File "/venv/lib/python3.5/site-packages/django/db/models/fields/related_descriptors.py" in set
982. objs = tuple(objs)
Exception Type: TypeError at /events/dating/user-profile/edit/preferences
Exception Value: 'NoneType' object is not iterable
In your model, partner_nationality
is a ManyToManyField
. 在您的模型中,
partner_nationality
是ManyToManyField
。 Therefore you should use a ModelMultipleChoiceField
in your form, not a ModelChoiceField
. 因此,您应该在表单中使用
ModelMultipleChoiceField
,而不是ModelChoiceField
。
class UserProfilePartnerPreferencesForm(ModelForm):
partner_nationality = ModelMultipleChoiceField(
queryset=Nationality.objects.order_by('name'),
widget=CheckboxSelectMultiple,
)
If you define the widget in the field definition, you don't have to set it in widgets
as well. 如果您在字段定义中定义窗口小部件,则不必在
widgets
中也进行设置。
Another approach is to change the field's attributes in the __init__
method. 另一种方法是在
__init__
方法中更改字段的属性。 For example, you can change the queryset with the following: 例如,您可以使用以下命令更改查询集:
class UserProfilePartnerPreferencesForm(ModelForm):
def __init__(self, *args, **kwargs):
super(UserProfilePartnerPreferencesForm, self).__init__(*args, **kwargs)
self.fields['partner_nationality'].queryset = Nationality.objects.order_by('name')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.