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 заставить его работать (я предполагаю, потому что ключ находится на модели профиля, не на пользовательской модели).


Более новое решение:

Я понял намек от этой статьи, предложенной в этом ответе.

Теперь я разделил свои образцовые формы и переместил логику в представление:

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)
    )

Я не слишком люблю смещение нагрузки для представления, но я предполагаю, что получаю немного больше гибкости этот путь?

6
задан 4 revs 23 May 2017 в 12:34
поделиться

2 ответа

Он не выполняет каскадное сохранение, потому что на самом деле не знает, нужно ли сохранять другим объектам или нет.

Чтобы сделать это за один раз, сначала запустите транзакцию :

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

Затем сохраните подобъекты в следующем порядке:

  user.profile.address.save()
  user.profile.save()
  user.save()
5
ответ дан 17 December 2019 в 00:08
поделиться

Проблема в том, что вы пытаетесь создать или обновить поля в объекте User, который еще даже не существует. Таким образом, другие поля на самом деле не обновляются, потому что они не связаны ни с какими первичными ключами дочерних полей.

Каждый раз, когда вы создаете экземпляр нового поля модели, вы должны убедиться, что вы сохраняете, чтобы у дочернего поля модели был идентификатор (первичный ключ), с которым нужно связываться.

Вам нужно что-то вроде этого:

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 () вещь, которая у вас здесь происходит, кажется неправильной. Вы подвержены слишком большому количеству ошибок, если какое-либо из полей не сохраняется, вы получите частично завершенный экземпляр пользователя и, возможно, получите дубликаты, если пользователю придется вернуться и попробовать еще раз. Не весело!

Редактировать: Вторая половина удалена, потому что она была неточной.

2
ответ дан 17 December 2019 в 00:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: