2010-02-13 66 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