Django级联保存?

时间:2010-02-13 01:27:42

标签: django django-models

我的用户注册表单上有一个方法如下:

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 article中建议的this 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)
    )

我不太喜欢将负担转移到视图上,但我想我会以这种方式获得更多灵活性?

2 个答案:

答案 0 :(得分:5)

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

要一次性完成,首先start a transaction

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

然后按顺序保存子对象:

  user.profile.address.save()
  user.profile.save()
  user.save()

答案 1 :(得分:2)

问题是您正在尝试创建或更新尚未存在的User对象中的字段。因此,其他字段并未真正更新,因为它们与子字段的任何主键都没有关联。

每次实例化新模型字段时,都必须确保保存,以便子模型字段具有要关联的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()你在这里发生的事情感觉不对劲。你可能会遇到太多的错误,如果任何字段没有保存,你最终会得到一个部分完整的用户实例,并且如果用户必须返回并再试一次,则最终会产生重复。不好玩!

修改:删除了下半部分,因为它不准确。