2010-02-13 69 views
6

我有我的用户登记表上的一个方法,看起来像这样:Django级联保存?

def save(self): 
    user = User(
     username = self.cleaned_data['username'], 
     email = self.cleaned_data['email1'], 
     first_name = self.cleaned_data['first_name'], 
     last_name = self.cleaned_data['last_name'], 
    ) 
    user.set_password(self.cleaned_data['password1']) 
    user.profile = Profile(
     primary_phone = self.cleaned_data['phone'], 
    ) 
    user.profile.address = Address(
     country = self.cleaned_data['country'], 
     province = self.cleaned_data['province'], 
     city = self.cleaned_data['city'], 
     postal_code = self.cleaned_data['postal_code'], 
     street1 = self.cleaned_data['street1'], 
     street2 = self.cleaned_data['street2'], 
     street3 = self.cleaned_data['street3'], 
    ) 
    user.save() 
    return user 

问题是,当我打电话form.save()它创建如预期user对象,但不保存他的个人资料或地址。为什么它不会级联并保存所有的子模型?我怀疑我可以手动调用user.profile.save()user.profile.address.save(),但我想整个事情成功或失败在一起。什么是最好的方法来做到这一点?


目前的解决方案:

def save(self): 
    address = Address(
     country = self.cleaned_data['country'], 
     province = self.cleaned_data['province'], 
     city = self.cleaned_data['city'], 
     postal_code = self.cleaned_data['postal_code'], 
     street1 = self.cleaned_data['street1'], 
     street2 = self.cleaned_data['street2'], 
     street3 = self.cleaned_data['street3'], 
    ) 
    address.save() 

    user = User(
     username = self.cleaned_data['username'], 
     email = self.cleaned_data['email1'], 
     first_name = self.cleaned_data['first_name'], 
     last_name = self.cleaned_data['last_name'], 
    ) 
    user.set_password(self.cleaned_data['password1']) 
    user.save() 

    profile = Profile(
     primary_phone = self.cleaned_data['phone'], 
    ) 
    profile.address = address 
    profile.user = user 
    profile.save() 

我不得不做出profile “中央” 的对象。需要设置profile.user = user,而不是user.profile = profile,使其工作(我猜是因为关键是在剖面模型,而不是在用户模型)。


较新的解决方案:

我参加了一个暗示从this articlethis answer建议。

现在我已经分开我的模型形式和移动的逻辑到视图:

def register(request): 
    if request.POST: 
     account_type_form = forms.AccountTypeForm(request.POST) 
     user_form = forms.UserForm(request.POST) 
     profile_form = forms.ProfileForm(request.POST) 
     address_form = forms.AddressForm(request.POST) 

     if user_form.is_valid() and profile_form.is_valid() and address_form.is_valid(): 
      user = user_form.save() 
      address = address_form.save() 
      profile = profile_form.save(commit=False) 
      profile.user = user 
      profile.address = address 
      profile.save() 
      return HttpResponseRedirect('/thanks/') 
    else: 
     account_type_form = forms.AccountTypeForm() 
     user_form = forms.UserForm() 
     profile_form = forms.ProfileForm() 
     address_form = forms.AddressForm() 

    return render_to_response(
     'register.html', 
     {'account_type_form': account_type_form, 'user_form': user_form, 'address_form': address_form, 'profile_form': profile_form}, 
     context_instance=RequestContext(request) 
    ) 

我不是太喜欢快慰的观点,但我想我得到一个更灵活一点这办法?

+0

乔利好戏! – jathanism 2010-02-13 02:18:22

回答

5

它不会级联保存,因为它实际上并不知道是否需要保存其他对象需要

要做到这一点一气呵成,第一start a transaction

@transaction.commit_on_success 
def save(self): 
    .... 

然后保存子对象依次是:

user.profile.address.save() 
    user.profile.save() 
    user.save() 
+0

为什么不能弄清楚,如果他们需要被拯救?他们甚至没有'身份证'......这是一个简单的检查。真的有必要使用事务处理这么简单的事情吗?我遇到了各种交易掩盖其他错误的问题。 – mpen 2010-02-13 01:42:04

+0

拥有一组PK不足以确定不应保存一个对象。 PK可能被强制或者对象可能需要更新,这两者都需要调用'save()'。 – 2010-02-13 01:43:48

+0

噢...更大的问题是'profile.user_id'不能为空。 'profile.user_id'从未设置,即使'profile'是'user'的属性... – mpen 2010-02-13 01:46:05

1

的问题是,你想创建或在更新领域尚未存在的用户对象。所以其他字段并不真正更新,因为它们没有关联到子字段的任何主键。

你实例化一个新的模式场每一次,你必须确保你节省使得儿童模型场有一个id(主键)与关联。

你需要更多的东西是这样的:

def save(self): 
    user = User(
     username = self.cleaned_data['username'], 
     email = self.cleaned_data['email1'], 
     first_name = self.cleaned_data['first_name'], 
     last_name = self.cleaned_data['last_name'], 
    ) 
    ## save user so we get an id 
    user.save() 

    ## make sure we have a user.id 
    if user.id: 
     ## this doesn't save the password, just updates the working instance 
     user.set_password(self.cleaned_data['password1']) 
     user.profile = Profile(
      primary_phone = self.cleaned_data['phone'], 
     ) 
     ## save the profile so we get an id 
     user.profile.save() 

    ## make sure we have a profile.id 
    if user.profile.id: 
     user.profile.address = Address(
      country = self.cleaned_data['country'], 
      province = self.cleaned_data['province'], 
      city = self.cleaned_data['city'], 
      postal_code = self.cleaned_data['postal_code'], 
      street1 = self.cleaned_data['street1'], 
      street2 = self.cleaned_data['street2'], 
      street3 = self.cleaned_data['street3'], 
     ) 
     ## save the profile address 
     user.profile.address.save() 

    ## final save to commit password and profile changes 
    user.save() 
    return user 

这你怎么回事级联save()事情只是感觉不对。你容易途中有太多的错误,如果任何字段不保存,你会最终有一个完整的部分用户实例和posisbly与副本最终用户是否回去,然后再试一次。不好玩!

编辑:删除了后半部分,因为它不准确。

+0

它看起来更好,因为你没有发布UserFormSet的内部,你发布了视图方法,它看起来或多或少与我现在拥有的相同。我将不得不深入研究formset,看看它们是否是正确的选择。我一直认为它们被用来一次创建许多对象,创建一个对象的形式并不多。我的级联思想旨在通过一起保存或失败来防止重复,因此只有一次调用save()。 – mpen 2010-02-13 02:15:50

+0

是的,你说得对。我不知道我在想什么!我会从答案中删除后半部分。 – jathanism 2010-02-13 03:00:27