2010-02-13 4 views
6

У меня есть метод в моей форме регистрации пользователя, который выглядит следующим образом:Джанго каскад сохранить?

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

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

+0

Весельное шоу! – jathanism

ответ

5

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

Чтобы сделать это на одном дыхании, первый start a transaction:

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

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

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

Почему он не может понять, нужно ли их спасти?У них даже нет 'id' еще ... это простая проверка. Действительно ли необходимо использовать транзакции для чего-то такого простого? У меня были всевозможные проблемы с транзакциями, маскирующими другие ошибки. – mpen

+0

Наличие набора PK недостаточно для определения того, что объект не должен сохраняться. Либо ПК может быть принудительно, либо объект может нуждаться в обновлении, для чего требуется вызов 'save()'. –

+0

Ох ... и большая проблема заключается в том, что 'profile.user_id' не может быть нулевым. 'profile.user_id' никогда не устанавливается, хотя' profile' является атрибутом 'user' ... – mpen

1

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

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

Вам нужно что-то более, как это:

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

Редактировать: Убрали вторую половину этого, потому что это было неточно.

+0

Это выглядит лучше, потому что вы не публиковали внутренности UserFormSet, вы разместили метод представления, который выглядит более или менее идентичным тому, что у меня есть сейчас. Мне придется глубже изучить формы, чтобы убедиться, что они правильный выбор. Я всегда думал о том, что они используются для создания множества объектов одновременно, а не для многих форм для создания одного объекта. Моя каскадная идея была направлена ​​на предотвращение дублирования, поскольку все это спасти или сработало вместе, поэтому есть только один вызов 'save()'. – mpen

+0

Да, ты прав. Я не знаю, о чем я думал! Я удалю эту вторую половину из ответа. – jathanism

Смежные вопросы