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

Entradas sobre "auth":

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 | 7 comentarios | Categorías: auth, forms, trucos, tutorial

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 en la documentación de Django) es creando un modelo para el perfil de usuario que incluya un campo con una ForeignKey apuntando a la clase User. Vamos a ver cómo se puede hacer de la forma oficial, con herencia y utilizando un método alternativo añadiendo atributos a la clase User con add_to_class.

Método "oficial"

from django.contrib.auth.models import User

class Perfil(models.Model):
    user = models.ForeignKey(User, unique=True)
    direccion = models.CharField(max_length=250, blank=True)
    telefono = models.PositiveIntegerField(null=True, blank=True)

Utilizamos unique=True en el campo user para asegurarnos de que sólo pueda haber un único perfil por cada usuario. Luego añadimos AUTH_PROFILE_MODULE a nuestro archivo settings.py:

AUTH_PROFILE_MODULE = 'mi_aplicacion.Perfil'

A partir de ese momento podemos acceder al perfil del usuario con user.get_profile(). Esta es la forma "oficial" de hacerlo pero presenta desventajas: Algunos atributos del usuario se acceden como user.atributo y otros como user.get_profile().atributo. Y esto se complica más aún cuando tenemos que establecer relaciones entre usuarios: ¿Ponemos la relación entre perfiles? ¿Entre Perfil y User? Generalmente es recomendable que la relación sea entre perfiles:

from django.contrib.auth.models import User

class Perfil(models.Model):
    user = models.ForeignKey(User, unique=True)
    direccion = models.CharField(max_length=250, blank=True)
    telefono = models.PositiveIntegerField(null=True, blank=True)
    amigos = models.ManyToManyField('self', symmetrical=True,  blank=True)

Aún así resulta molesto tener que acceder a los amigos de un usuario de esta manera:

for perfil in user.get_profile().amigos:
    print perfil.user # mostrar el usuario

Nos gustaría que fuese tan sencillo como:

for usuario in user.amigos:
    print usuario

Método mediante herencia

Otra forma de hacerlo es creando una clase que herede de la clase User:

from django.contrib.auth.models import User

class Usuario(User):
    direccion = models.CharField(max_length=250, blank=True)
    telefono = models.PositiveIntegerField(null=True, blank=True)
    amigos = models.ManyToManyField('self', symmetrical=True,  blank=True)

De esta manera el problema se nos presenta al no poder acceder directamente a los datos del perfil con request.user en nuestras vistas.

Método usando add_to_class

Vamos a ver un método menos "limpio" que los anteriores pero más eficiente. Se trata de añadir atributos y métodos a la clase User antes de haber creado la tabla de usuario mediante el comando syncdb. En el models.py de cualquier aplicación del proyecto importamos la clase User y utilizamos add_to_class para añadir los atributos y las funciones que queramos a la clase User antes de crear su correspondiente tabla en la base de datos:

from django.contrib.auth.models import User

User.add_to_class('direccion', models.FloatField(null=True,blank=True))
User.add_to_class('telefono', models.PositiveIntegerField(null=True,blank=True))
User.add_to_class('amigos', models.ManyToManyField('self', symmetrical=True,  blank=True))

Ya podemos acceder a todos los atributos del usuario mediante user.atributo (p. ej: user.telefono). Crear un método propio para la clase User es igual de sencillo:

def es_popular(self):
    # devuelve True si tiene más de 100 amigos
    if self.amigos.all.count() > 100:
        return True
    else:
        return False

User.add_to_class('es_popular', es_popular)
# ahora podemos acceder al método mediante user.es_popular()

Ventajas de este método:

Se crea una única tabla para el usuario y su perfil: Esto es más eficiente si en las vistas vamos a necesitar información del usuario e información de su perfil constantemente. Con este método también podemos extender la clase User con métodos propios, y accedemos a todos los atributos del usuario mediante user.atributo.

Importante: Al crear un usuario deberás dar un valor siempre a los campos del perfil que no permitan valores nulos.

Publicado por Antonio Melé el Jueves 27 d Noviembre d 2008 | 3 comentarios | Categorías: auth, modelos, trucos