2010-02-13 9 views
6

que tienen un método en mi formulario de registro de usuario que tiene este aspecto:Cascada Django guardar?

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 

El problema es que cuando llamo form.save() se crea el objeto user como se esperaba, pero no salva a su perfil o dirección. ¿Por qué no se conecta en cascada y guarda todos los submodelos? Sospecho que podría llamar al user.profile.save() y al user.profile.address.save() manualmente, pero quiero que todo funcione bien o falle al mismo tiempo. ¿Cuál es la mejor manera de hacer esto?


solución actual:

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

tuviera que hacer profile el objeto "central". Necesario para establecer profile.user = user en lugar de user.profile = profile para que funcione (supongo que porque la clave está en el modelo de perfil, no en el modelo de usuario). solución


reciente:

me dio un indicio de this article sugerido en this answer.

Ahora he separado de mis modelos de formularios y se trasladó la lógica a la vista:

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

no soy demasiado aficionado a trasladar la carga a la vista, pero creo que tengo un poco más de flexibilidad este ¿camino?

+0

¡Buen espectáculo! – jathanism

Respuesta

5

No se guarda en cascada porque en realidad no sabe si los otros objetos necesitan para guardarse o no.

de hacerlo de una sola vez, primero start a transaction:

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

continuación, guardar los objetos parciales en orden:

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

¿Por qué no puede averiguar si necesitan ser guardados?Ni siquiera tienen 'id's todavía ... es un cheque fácil. ¿Es realmente necesario usar transacciones para algo tan simple? He tenido todo tipo de problemas con las transacciones enmascarando otros errores. – mpen

+0

Tener un PK establecido no es suficiente para determinar que un objeto no se debe guardar. O el PK puede ser forzado o el objeto puede necesitar actualización, los cuales requieren una llamada a 'save()'. –

+0

Oh ... y el problema más grande es que 'profile.user_id' no puede ser nulo. 'profile.user_id' nunca se establece, a pesar de que' profile' es un atributo de 'user' ... – mpen

1

El problema es que usted está tratando de crear o actualizar campos en una Objeto de usuario que aún no existe. Por lo tanto, los otros campos no se actualizan realmente porque no están asociados a ninguna clave primaria de los campos secundarios.

Cada vez que crea una instancia de un nuevo campo de modelo, debe asegurarse de estar guardando para que un campo de modelo hijo tenga una identificación (clave principal) para asociar.

se necesita algo más a esto:

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 

Esta cosa en cascada save() que ha pasando aquí simplemente no se siente bien. Usted es propenso a demasiados errores allí, si alguno de los campos no se guarda, terminará con una instancia de usuario parcialmente completa y posiblemente terminará con duplicados si el usuario tiene que regresar e intentar nuevamente. ¡No es divertido!

Editar: Se ha eliminado la segunda mitad de esta porque no era precisa.

+0

Se ve mejor porque no ha publicado las partes internas del UserFormSet, ha publicado el método de vista, que se parece más o menos idéntico al que tengo ahora. Tendré que buscar más complejos para ver si son la elección correcta. Siempre pensé que se usaban para crear muchos objetos a la vez, no muchas formas para crear un objeto. Mi idea en cascada tenía como objetivo evitar duplicados haciendo que todos guardaran o fallaran juntos, por lo tanto, solo tenía una llamada a 'guardar()'. – mpen

+0

Sí, tienes razón sobre eso. ¡No sé lo que estaba pensando! Eliminaré esa última mitad de la respuesta. – jathanism

Cuestiones relacionadas