10 django django-models django-forms django-views
文档似乎非常坚定,确实如此......
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
我特别提到这一节:
当您的模型与另一个模型具有多对多关系时,可以看到使用commit = False的另一个副作用.如果模型具有多对多关系,并且在保存表单时指定commit = False,则Django无法立即保存多对多关系的表单数据.这是因为在实例存在于数据库中之前,无法为实例保存多对多数据.
要解决此问题,每次使用commit = False保存表单时,Django都会向您的ModelForm子类添加save_m2m()方法.手动保存表单生成的实例后,可以调用save_m2m()来保存多对多表单数据.
我对django很新,昨天偶然发现了这些信息.
但是,我有一个视图,我不调用save_m2m()方法,但实际上它保存m2m数据.
这是我的观点:
class SubscriberCreateView(AuthCreateView):
model = Subscriber
template_name = "forms/app.html"
form_class = SubscriberForm
success_url = "/app/subscribers/"
def get_form_kwargs(self):
kwargs = super(SubscriberCreateView, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
try:
self.object.full_clean()
except ValidationError:
form._errors["email"] = ErrorList([u"This subscriber email is already in your account."])
return super(SubscriberCreateView, self).form_invalid(form)
return super(SubscriberCreateView, self).form_valid(form)
Run Code Online (Sandbox Code Playgroud)
我的模特:
class Subscriber(models.Model):
STATUS_CHOICES = (
(1, ('Subscribed')),
(2, ('Unsubscribed')),
(3, ('Marked as Spam')),
(4, ('Bounced')),
(5, ('Blocked')),
(6, ('Disabled')),
)
user = models.ForeignKey(User)
status = models.IntegerField(('status'), choices=STATUS_CHOICES, default=1)
email = models.EmailField()
subscriber_list = models.ManyToManyField('SubscriberList')
first_name = models.CharField(max_length=70, blank=True)
last_name = models.CharField(max_length=70, blank=True)
phone = models.CharField(max_length=20, blank=True)
facebook_id = models.CharField(max_length=40, blank=True)
twitter_id = models.CharField(max_length=40, blank=True)
address1 = models.CharField(max_length=100, blank=True)
address2 = models.CharField(max_length=100, blank=True)
postcode = models.CharField(max_length=10, blank=True)
city = models.CharField(max_length=30, blank=True)
country = models.CharField(max_length=30, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
class Meta:
unique_together = (
('user', 'email',),
)
def __unicode__(self):
return self.email
Run Code Online (Sandbox Code Playgroud)
我的表格:
class SubscriberForm(ModelForm):
def __init__(self, user, *args, **kwargs):
super (SubscriberForm, self).__init__(*args, **kwargs)
self.fields['subscriber_list'].queryset = SubscriberList.objects.filter(user=user)
class Meta:
model = Subscriber
exclude = ('user', 'facebook_id', 'twitter_id')
Run Code Online (Sandbox Code Playgroud)
为什么我的观点有用呢?(意思是,表单中某个字段的m2m关系实际上是在处理表单时保存的.)
其中一个父类正在执行模型对象及其m2m关系的完全保存.我无法确定,因为我没有声明AuthCreateView,但命名约定表明它源于"CreateView".如果是这样,View的继承就像这样,SubscriberCreateView -> AuthCreateView -> CreateView -> BaseCreateView -> ModelFormMixin.ModelFormMixin有一个form_valid()(可能)调用的方法super().
这是Django 1.4 的整个方法:
def form_valid(self, form):
self.object = form.save()
return super(ModelFormMixin, self).form_valid(form)
Run Code Online (Sandbox Code Playgroud)
所以你有它.但是,让我指出一些潜在的混乱.@Wogan在指出你没有保存你的对象时很精明.代码的方式,您使用未保存的模型实例进行验证,然后因为ModelFormMixin 重新分配而被丢弃. self.object
self.object可能不是,如果你访问它以后你所期望的.self.object.user信息.(因为user是Model上的必填字段,你在表单中将其排除,我希望它save()会失败.所以父母AuthCreateView必须做某事.当然它可能是处理整个save()而且从不打击ModelFormMixin.)为避免这种混淆,只需将实例分配给self.object.也许:validate_obj = form.save(commit=False)
如果我们知道如何使用save(commit=False) ,这是一个简单的答案。
使用commit=False 的save方法不会更改您的数据库:
“如果您使用 commit=False 调用 save(),那么它将返回一个尚未保存到数据库的对象。在这种情况下,您需要对生成的模型实例调用 save()。这是如果您想在保存对象之前对其进行自定义处理,或者如果您想使用专门的模型保存选项之一,则此功能非常有用。”
因此,在多对多关系的情况下,如果不将对象保存到数据库,就不可能保存 m2m 数据。通常的 save( commit=False ) 可以在保存部分数据(不是分配给 m2m 关系的数据)时更改对象。你确实不能仅将 M2M 关系存储在内存中。
如果您想在save(commit=False)后使用模型对象的 m2m 数据,则需要 save_m2m() 。当您使用save(commit=False)时应该执行此操作,否则您可能会在 save(commit=False) 之后得到一个与您的数据库(或数据库模型)不正确对应的中间对象。有时这可能是正常的(如果你不接触模型 m2m 部分处理中涉及的数据)。要恢复一致性,请在每次调用 save(commit=False) 时调用 save_m2m。
查看save_m2m实现:
def save_m2m():
cleaned_data = form.cleaned_data
for f in opts.many_to_many:
if fields and f.name not in fields:
continue
if f.name in cleaned_data:
f.save_form_data(instance, cleaned_data[f.name])
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
12621 次 |
| 最近记录: |