Django es el entorno de desarrollo web para perfeccionistas con límites de tiempo

Utilizar un formulario para modificar dos modelos

Actualización/Update: Zack translated this post into english and it is available here.

A veces tenemos que modificar información relativa a dos o más modelos distintos desde un solo formulario HTML. Es algo común por ejemplo cuando definimos un perfil de usuario y necesitamos que se pueda modificar la información del modelo User del sistema de autenticación de Django y la información de nuestro modelo de perfil simultáneamente. Vamos a ver cómo puede hacerse. Para seguir este ejemplo necesitaremos tener django.contrib.auth entre las aplicaciones instaladas de nuestro proyecto (setting INSTALLED_APPS).

Suponemos un modelo Perfil como el siguiente definido en nuestro models.py:

from django.db import models

class Perfil(models.Model):
    user = models.OneToOneField(User, unique=True, related_name='perfil')
    telefono = models.PositiveIntegerField()
    direccion = models.TextField()

A continuación creamos un ModelForm para este modelo Perfil y otro para el modelo User del sistema de autenticación de Django. Más adelante usaremos conjuntamente ambos ModelForms en un único formulario HTML. Guardamos el siguiente código en forms.py:

from django import forms
from models import Perfil
from django.contrib.auth.models import User

class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ('username', 'first_name', 'last_name', 'email')

class PerfilForm(forms.ModelForm):
    class Meta:
        model = Perfil

Como podemos ver sólo queremos editar los campos username, first_name, last_name y email del modelo User.

Ahora creamos la vista que generará el formulario para editar el perfil de un usuario. Para ello necesitaremos crear una instancia de los dos ModelForms que hemos definido anteriormente. Nuestro views.py debe tener la siguiente pinta:

from django.shortcuts import render_to_response
from django.template import RequestContext, loader
from django.contrib.auth.decorators import login_required
from forms import PerfilForm, UserForm

@login_required
def editar_perfil(request):

    if request.method == 'POST':
        # formulario enviado
        user_form = UserForm(request.POST, instance=request.user)
        perfil_form = PerfilForm(request.POST, instance=request.user.perfil)

        if user_form.is_valid() and perfil_form.is_valid():
            # formulario validado correctamente
            user_form.save()
            perfil_form.save()
            return HttpResponseRedirect('/formulario-guardado/')

    else:
        # formulario inicial
        user_form = UserForm(instance=request.user)
        perfil_form = PerfilForm(instance=request.user.perfil)

    return render_to_response('editar_perfil.html', { 'user_form': user_form,  'perfil_form': perfil_form }, context_instance=RequestContext(request))

Utilizamos el decorator @login_required ya que nuestros usuarios deberán estar autenticados para poder modificar su perfil. Si la petición llega por el método POST significa que se ha enviado el formulario y creamos dos Forms, uno para el modelo de usuario y otro para el del perfil, a partir de los datos recibidos. Validamos ambos formularios para guardarlos al igual que hacemos normalmente con un sólo formulario. Utilizamos una plantilla llamada editar_perfil.html a la que le pasamos ambos formularios a través de las variables user_form y perfil_form. El formulario de nuestra plantilla HTML será así de sencillo:

<form action="" method="post">
    {{ user_form }}
    {{ perfil_form }}
    <input type="submit" value="Guardar cambios">
</form>

Con esto generaremos un formulario HTML que tiene primero los campos del modelo User seguidos por los campos del modelo Perfil asociado y que guardará correctamente cualquier cambio realizado en cualquiera de los dos objetos.

Publicado por Antonio Melé el Lunes 6 d Abril d 2009 Compártelo: Facebook: Twitter: | Categorías: auth, forms, trucos, tutorial

Entradas similares

Métodos para crear perfiles de usuario

En múltiples ocasiones nos gustaría extender el modelo User para que incluyera otros campos y funciones. La manera "oficial" de hacer esto (la mostrada ...


Crear una imagen de nuestros modelos con django-command-extensions

Algo interesante que nos aporta django-command-extensions es poder crear una representación gráfica de nuestros modelos (o por decirlo de otro modo nuestro esquema de ...


 
Modificar la QuerySet de un ModelChoiceField dinámicamente

El campo de formulario ModelChoiceField sirve para permitir la selección de un elemento entre los objetos resultantes de una QuerySet. La QuerySet inicial puede ...


Aplicar filtros a annotate() al usar Count()

A veces queremos utilizar la función de aggregación annotate() y aplicar filtros al modelo que se encuentra dentro de la misma, pero no es ...


 
 

7 comentarios:

El Martes 7 de Abril de 2009 aaloy dijo:

Sólo una puntualización: cuando los modelos/formularios tienen campos con el mismo nombre hay que añadir al form un prefijo para que cada formulario obtenga los campos que le corresponden. Por ejemplo:

user_form = UserForm(prefix="user", instance=request.user)

El Miércoles 19 de Agosto de 2009 Asinox dijo:

Interesante, aunque es facil pensar en esta solucion, pero como soy nuevo con Django, no me pasaba que podia usar inline fuera del admin...bueno cada dia va estoy mejorando :D

Saludos

El Miércoles 19 de Agosto de 2009 Mario dijo:

Interesante si

El Viernes 17 de Septiembre de 2010 Vero dijo:
Obtengo este error 'WSGIRequest' object has no attribute que puede ser???
El Jueves 28 de Abril de 2011 gaston dijo:
Una consulta respecto a esto: tengo una orden de compra en un modelo y luego, otro modelo con las recepciones de su mercadería models.py class ordenCompra(models.Model): numero = models.IntegerField(blank=False) cantidad_pedida = models.IntegerField(blank=False) cantidad_pendiente = models.IntegerField(blank=True,default=0) def __unicode__(self): return str(self.numero) class recepcion(models.Model): ordenCompra = models.ForeignKey(ordenCompra) cantidad_recibida = models.IntegerField(blank=False) fecha_recepcion = models.DateTimeField(default=datetime.datetime.now()) def __unicode__(self): return "%s %s" % (self.ordenCompra, self.cantidad_recibida) admin.py class Inlines: class recepcionAdminInline(admin.TabularInline): model = recepcion extra = 0 class ordenCompraAdmin(admin.ModelAdmin): inlines = [Inlines.recepcionAdminInline] Pregunta: Como hago usando la vista por default del admin para en un mismo metodo obtener el form de la orden de compra y los formularios de recepecion? La idea es poder validar que la suma de las cantidades recibidas del inline, no supere la cantidad pedida de la orden de compra. Espero tu rta ya que seguro la sabes responder. Un abrazo y gracias!
El Sábado 4 de Junio de 2011 richar dijo:
Oe alguien tiene un manual o algo para despegar Django con Apache y Nginx o Lighty en WIndows, lo necesito para entregar mi tesis, el cual debe funcionar tanto en Linux como en Windows mi correo es: gran_linux@hotmail.com
El Martes 3 de Abril de 2012 Allan dijo:
Hola! Soy nuevo en Python/Django. Me parece muy bien explicado, pero algo estoy haciendo mal. Mis modelos son User y PerfilUsuario. Éste es mi mensaje de error: http://dpaste.com/726319/ Gracias!

Escribe un comentario:

captcha