У меня есть метод на моей форме регистрации пользователя, которая похожа на это:
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)
)
Я не слишком люблю смещение нагрузки для представления, но я предполагаю, что получаю немного больше гибкости этот путь?
Он не выполняет каскадное сохранение, потому что на самом деле не знает, нужно ли сохранять другим объектам или нет.
Чтобы сделать это за один раз, сначала запустите транзакцию :
@transaction.commit_on_success
def save(self):
....
Затем сохраните подобъекты в следующем порядке:
user.profile.address.save()
user.profile.save()
user.save()
Проблема в том, что вы пытаетесь создать или обновить поля в объекте 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 ()
вещь, которая у вас здесь происходит, кажется неправильной. Вы подвержены слишком большому количеству ошибок, если какое-либо из полей не сохраняется, вы получите частично завершенный экземпляр пользователя и, возможно, получите дубликаты, если пользователю придется вернуться и попробовать еще раз. Не весело!
Редактировать: Вторая половина удалена, потому что она была неточной.