我使用commit = False来保存表单,因为我希望在保存之前在数据表中包含一些非表单数据(表单的作者)。在Django中设置commit = False时,文档中会提到副作用:
当您的模型与另一个模型具有多对多关系时,可以看到使用commit = False的另一个副作用。如果模型具有多对多关系,并且在保存表单时指定commit = False,则Django无法立即保存多对多关系的表单数据。这是因为在实例存在于数据库中之前,无法为实例保存多对多数据。
所以我尝试使用文档here中描述的save_m2m()方法,但无济于事,我收到以下错误。
class MinutesCreate(LoginRequiredMixin, View):
@method_decorator(permission_required('pd.add_agenda'))
def dispatch(self, *args, **kwargs):
return super(MinutesCreate, self).dispatch(*args, **kwargs)
def get(self, request, **kwargs):
minutes_form = MinutesForm()
return render(request, "pd/minutes_form.html", {"form": minutes_form, "kwargs": kwargs})
def post(self, request, **kwargs):
minutes_form = MinutesForm(request.POST)
if minutes_form.is_valid():
minutes = minutes_form.save(commit=False)
minutes.author = request.user
minutes.save()
minutes_form.save_m2m()
return redirect('pd:agenda_list')
return render(request, 'pd/minutes_form.html', {'form': minutes_form})
class MinutesForm(BetterModelForm):
def __init__(self, *args, **kwargs):
super(MinutesForm, self).__init__(*args, **kwargs)
self.fields['participants'].required = False
participants = UserModelMultipleChoiceField(queryset=UserProfile.objects.all(), widget=forms.CheckboxSelectMultiple())
class Meta:
model = Minutes
fieldsets = (
("Minutes", {"fields": ["participants","minutes"]}),
)
class Minutes(models.Model):
agenda = models.OneToOneField(
Agenda,
on_delete=models.CASCADE,
primary_key=True,
)
published = models.DateTimeField(verbose_name='Minutes Published', auto_now_add=True)
edited = models.DateTimeField(verbose_name='Last Modified', auto_now=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="minutes_author", db_index=True, blank=True)
participants = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="participants")
minutes = models.TextField(blank=True)
Environment:
Request Method: POST
Request URL: http://localhost:8000/committee/agenda/2016/6/22/minutes/add
Django Version: 1.8
Python Version: 2.7.11
Installed Applications:
('flat',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pdpauth',
'pd',
'bootstrap3',
'recurrence',
'mail_templated',
'django_navtag',
'debug_toolbar')
Installed Middleware:
(u'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'rollbar.contrib.django.middleware.RollbarNotifierMiddleware')
Traceback:
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
22. return view_func(request, *args, **kwargs)
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/views/generic/base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
34. return bound_func(*args, **kwargs)
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
22. return view_func(request, *args, **kwargs)
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
30. return func.__get__(self, type(self))(*args2, **kwargs2)
File "/Users/ryancastner/Code/pdpsite/pd/views.py" in dispatch
410. return super(MinutesCreate, self).dispatch(*args, **kwargs)
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/views/generic/base.py" in dispatch
89. return handler(request, *args, **kwargs)
File "/Users/ryancastner/Code/pdpsite/pd/views.py" in post
422. minutes_form.save_m2m()
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/forms/models.py" in save_m2m
102. f.save_form_data(instance, cleaned_data[f.name])
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/db/models/fields/related.py" in save_form_data
2576. setattr(instance, self.attname, data)
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/db/models/fields/related.py" in __set__
1259. manager = self.__get__(instance)
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/db/models/fields/related.py" in __get__
1242. through=self.field.rel.through,
File "/Users/ryancastner/Code/pdpsite/venv/lib/python2.7/site-packages/django/db/models/fields/related.py" in __init__
874. (instance, source_field_name))
Exception Type: ValueError at /committee/agenda/2016/6/22/minutes/add
Exception Value: "<Minutes: Minutes object>" needs to have a value for field "minutes" before this many-to-many relationship can be used.
请求信息
GET
No GET data
POST
Variable Value
csrfmiddlewaretoken u'W7tWohIGT1YsMR45pbGhuK5I6VbdIv9m'
minutes u'<p>testing this out</p>'
participants u'17'
[23/Jun/2016 13:08:04]"GET /committee/agenda/2016/6/22/minutes/add HTTP/1.1" 200 25470
minutes object's minutes:
<p>testinga askdlj;slkdje</p>
[23/Jun/2016 13:08:14]"POST /committee/agenda/2016/6/22/minutes/add HTTP/1.1" 500 148995
class MinutesCreate(LoginRequiredMixin, View):
@method_decorator(permission_required('pd.add_agenda'))
def dispatch(self, *args, **kwargs):
return super(MinutesCreate, self).dispatch(*args, **kwargs)
def get(self, request, **kwargs):
minutes_form = MinutesForm()
return render(request, "pd/minutes_form.html", {"form": minutes_form, "kwargs": kwargs})
def post(self, request, **kwargs):
minutes_form = MinutesForm(request.POST)
if minutes_form.is_valid():
minutes = minutes_form.save(commit=False)
minutes.author = request.user
print "minutes object's minutes:\n", minutes.minutes # new print that is shown above
minutes.save()
minutes_form.save_m2m()
return redirect('pd:agenda_list')
return render(request, 'pd/minutes_form.html', {'form': minutes_form})
答案 0 :(得分:1)
问题是由于我使用BetterModelForm
Django包。 BetterModelForm
将表单划分为fieldsets
,以允许在同一页面上显示可重复或多个表单。因此,save_m2m()
的方法没有在form
上传播,而是在formset
传播,这需要一种不同的方式来调用save_m2m()
。
我通过将BetterModelForm
类转换为标准forms.ModelForm
来解决此问题,并且当fieldsets
被删除后,所有内容都按预期工作。
注意**我还在为表单提供UserProfile
查询集,我的模型接受了User
个对象。我更新了代码,以便为我的表单提供正确的查询集。
更新后的代码如下:
class AgendaForm(forms.ModelForm):
time_fmt = ["%I:%M %p"]
date_fmt = ["%Y/%m/%d"]
start = forms.SplitDateTimeField(label="Meeting Start", input_time_formats=time_fmt,
input_date_formats=date_fmt,
widget=forms.widgets.SplitDateTimeWidget(date_format=date_fmt[0], time_format=time_fmt[0]))
end = forms.SplitDateTimeField(label="Meeting End", input_time_formats=time_fmt,
input_date_formats=date_fmt,
widget=forms.widgets.SplitDateTimeWidget(date_format=date_fmt[0], time_format=time_fmt[0]))
location = forms.CharField(initial='TBD')
class Meta:
model = Agenda
fields = ["start", "end", "location", "announcements", "agenda"]
class MinutesForm(forms.ModelForm):
participants = UserModelMultipleChoiceField(queryset=User.objects.all(), widget=forms.CheckboxSelectMultiple())
class Meta:
model = Minutes
fields = ["participants","minutes"]
class MinutesCreate(LoginRequiredMixin, View):
@method_decorator(permission_required('pd.add_agenda'))
def dispatch(self, *args, **kwargs):
return super(MinutesCreate, self).dispatch(*args, **kwargs)
def get(self, request, **kwargs):
minutes_form = MinutesForm()
return render(request, "pd/minutes_form.html", {"form": minutes_form, "kwargs": kwargs})
def post(self, request, **kwargs):
minutes_form = MinutesForm(request.POST)
if minutes_form.is_valid():
minutes = minutes_form.save(commit=False)
minutes.author = request.user
ag = get_object_or_404(Agenda, start__year=kwargs.get('year'),
start__month=kwargs.get('month'),
start__day=kwargs.get('day'))
minutes.agenda = ag
minutes.save()
minutes_form.save_m2m()
return redirect('pd:agenda_detail', **kwargs)
return render(request, 'pd/minutes_form.html', {'form': minutes_form})