Ampliación del modelo de usuario con campos personalizados en Django

442

¿Cuál es la mejor manera de extender el modelo de usuario (incluido con la aplicación de autenticación de Django) con campos personalizados? Posiblemente también me gustaría usar el correo electrónico como nombre de usuario (para fines de autenticación).

Ya he visto algunas formas de hacerlo, pero no puedo decidir cuál es la mejor.

Farinha
fuente
24
La mayoría de las respuestas están desactualizadas / en desuso. Consulte stackoverflow.com/a/22856042/781695 y stackoverflow.com/q/14104677/781695 y stackoverflow.com/q/16880461/781695
usuario
@buffer: la primera pregunta a la que se vinculó (stackoverflow.com/a/22856042/781695) ahora se ha eliminado. Los otros siguen siendo válidos.
Tony
3
Para usar el correo electrónico en lugar del nombre de usuario para la autenticación, este artículo es útil.
Estoy bastante fascinado de ver que la pregunta se hizo en 2008 antes de que Django fuera lanzado @Farinha
Tessaracter el

Respuestas:

253

La forma menos dolorosa y recomendada por Django de hacerlo es a través de una OneToOneField(User)propiedad.

Ampliación del modelo de usuario existente

...

Si desea almacenar información relacionada con User, puede usar una relación uno a uno con un modelo que contiene los campos para obtener información adicional. Este modelo uno a uno a menudo se denomina modelo de perfil, ya que podría almacenar información no relacionada con la autenticación sobre un usuario del sitio.

Dicho esto, extenderlo django.contrib.auth.models.Usery suplantarlo también funciona ...

Sustitución de un modelo de usuario personalizado

Algunos tipos de proyectos pueden tener requisitos de autenticación para los cuales Django está integrado User modelo no siempre es apropiado. Por ejemplo, en algunos sitios tiene más sentido usar una dirección de correo electrónico como token de identificación en lugar de un nombre de usuario.

[Ed: Siguen dos advertencias y una notificación , mencionando que esto es bastante drástico .]

Definitivamente, me mantendría alejado de cambiar la clase de usuario real en su árbol de origen de Django y / o copiar y alterar el módulo de autenticación.

Ryan Duffield
fuente
50
Para su información, el nuevo método recomendado (1.0+) es OneToOneField (Usuario) docs.djangoproject.com/en/dev/topics/auth/…
Dave Forgac
2
Shawn Rider de PBS dio algunas buenas razones por las que no debería extender django.contrib.auth.models.User. Utilice OneToOneField (Usuario) en su lugar.
pydanny
2
user = models.ForeignKey(User, unique=True)¿Es esto lo mismo que user = models.OneToOneField(User)? Creo que el efecto final es el mismo? Pero tal vez la implementación en el backend es diferente.
Sam Stoelinga
77
¿Alguien puede vincular a Shawn Riders argumento / razonamiento para eso?
Jeremy Blanchard
1
Aquí hay información adicional sobre la ampliación de los modelos de usuario a partir de los documentos de django 1.7
Derek Adair
226

Nota: esta respuesta está en desuso. vea otras respuestas si está utilizando Django 1.7 o posterior.

Así es como lo hago.

#in models.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save

class UserProfile(models.Model):  
    user = models.OneToOneField(User)  
    #other fields here

    def __str__(self):  
          return "%s's profile" % self.user  

def create_user_profile(sender, instance, created, **kwargs):  
    if created:  
       profile, created = UserProfile.objects.get_or_create(user=instance)  

post_save.connect(create_user_profile, sender=User) 

#in settings.py
AUTH_PROFILE_MODULE = 'YOURAPP.UserProfile'

Esto creará un perfil de usuario cada vez que se guarde un usuario si se crea. Entonces puedes usar

  user.get_profile().whatever

Aquí hay más información de los documentos

http://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users

Actualización: tenga en cuenta que AUTH_PROFILE_MODULEestá en desuso desde la v1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module

Pasas
fuente
66
Gracias por el claro ejemplo, tenga en cuenta que def create_user .... no es parte de la clase UserProfile y debe alinearse a la izquierda.
PhoebeB
55
Con esta solución, ¿deberían otros modelos ForeignKey to User o UserProfile?
andrewrk
99
Otros modelos deben usar user = models.ForeignKey( User )y recuperar el objeto de perfil a través de user.get_profile(). Recuerde from django.contrib.admin.models import User.
Craig Trader
1
Al usar este método, necesito separarme cuando recupero la información habitual (nombre, contraseña) y la personalizada o ¿hay alguna manera de hacerlo de una vez? Lo mismo para la creación de un nuevo usuario?
Martin Trigaux
55
Esta respuesta y comentarios han quedado obsoletos, por ejemplo, AUTH_PROFILE_MODULE está en desuso,User
usuario
196

Bueno, pasó un tiempo desde 2008 y es hora de una nueva respuesta. Desde Django 1.5 podrás crear una clase de usuario personalizada. En realidad, en el momento en que escribo esto, ya está fusionado en master, por lo que puedes probarlo.

Hay cierta información al respecto en documentos o si desea profundizar en él, en este compromiso .

Todo lo que tiene que hacer es agregar AUTH_USER_MODELa la configuración con la ruta a la clase de usuario personalizada, que se extiende AbstractBaseUser(versión más personalizable) oAbstractUser (clase de usuario más o menos antigua que puede extender).

Para las personas que son perezosas para hacer clic, aquí hay un ejemplo de código (tomado de documentos ):

from django.db import models
from django.contrib.auth.models import (
    BaseUserManager, AbstractBaseUser
)


class MyUserManager(BaseUserManager):
    def create_user(self, email, date_of_birth, password=None):
        """
        Creates and saves a User with the given email, date of
        birth and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            email=MyUserManager.normalize_email(email),
            date_of_birth=date_of_birth,
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, date_of_birth, password):
        """
        Creates and saves a superuser with the given email, date of
        birth and password.
        """
        u = self.create_user(username,
                        password=password,
                        date_of_birth=date_of_birth
                    )
        u.is_admin = True
        u.save(using=self._db)
        return u


class MyUser(AbstractBaseUser):
    email = models.EmailField(
                        verbose_name='email address',
                        max_length=255,
                        unique=True,
                    )
    date_of_birth = models.DateField()
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = MyUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['date_of_birth']

    def get_full_name(self):
        # The user is identified by their email address
        return self.email

    def get_short_name(self):
        # The user is identified by their email address
        return self.email

    def __unicode__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        # Simplest possible answer: All admins are staff
        return self.is_admin
Ondrej Slinták
fuente
La función create_user no parece almacenar el nombre de usuario, ¿cómo es eso?
Orca
66
Porque en este ejemplo, emailes el nombre de usuario.
Ondrej Slinták
55
Debe agregar unique=Trueal campo de correo electrónico para dejar de USERNAME_FIELDaceptarlo
Richard de Wit
Hola, estaba tratando de crear un usuario personalizado como dijiste, pero no pude iniciar sesión usando el correo electrónico del usuario personalizado. ¿no te importaría decir por qué?
Lionel
Realmente no puedo ayudarte sin detalles. Sería mejor si crearas una nueva pregunta.
Ondrej Slinták
47

Desde Django 1.5, puede ampliar fácilmente el modelo de usuario y mantener una sola tabla en la base de datos.

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import ugettext_lazy as _

class UserProfile(AbstractUser):
    age = models.PositiveIntegerField(_("age"))

También debe configurarlo como clase de usuario actual en su archivo de configuración

# supposing you put it in apps/profiles/models.py
AUTH_USER_MODEL = "profiles.UserProfile"

Si desea agregar muchas preferencias de usuarios, la opción OneToOneField puede ser una mejor opción.

Una nota para las personas que desarrollan bibliotecas de terceros: si necesita acceder a la clase de usuario, recuerde que las personas pueden cambiarla. Usa el ayudante oficial para obtener la clase correcta

from django.contrib.auth import get_user_model

User = get_user_model()
Riccardo Galli
fuente
3
Si planea usar django_social_auth, le recomiendo usar una relación OneToOne. NO use este método o arruinará sus migraciones.
Nimo
@Nimo: ¿Podría elaborar o citar una referencia
Usuario
@buffer, fue hace mucho tiempo, pero creo que intenté combinar el django_social_authcomplemento y definir AUTH_USER_MODEL para el usuario de autenticación social. Luego, cuando ejecuté manage.py migrate, estropeó mi aplicación. Cuando, en cambio, utilicé el modelo de usuario de autenticación social como una relación OneToOne descrita aquí: stackoverflow.com/q/10638293/977116
Nimo
3
Probablemente tenga que ver con Changing this setting after you have tables created is not supported by makemigrations and will result in you having to manually write a set of migrations to fix your schemaFuente: docs.djangoproject.com/en/dev/topics/auth/customizing/…
usuario
23

El siguiente es otro enfoque para extender un usuario. Siento que es más claro, fácil, legible que por encima de dos enfoques.

http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/

Usando el enfoque anterior:

  1. no necesita usar user.get_profile (). newattribute para acceder a la información adicional relacionada con el usuario
  2. puedes acceder directamente a nuevos atributos adicionales a través de user.newattribute
Rama Vadakattu
fuente
1
Me gusta mucho el enfoque de Scott, basado en la herencia del objeto Usuario en lugar de directamente del modelo. ¿Alguien puede decir si este enfoque no es sabio?
BozoJoe
1
@BozoJoe: acabo de encontrar este problema al importar datos de volcado, lo que parece ser una consecuencia del uso de este método: stackoverflow.com/questions/8840068/…
Ben Regenspan
15

Simplemente puede ampliar el perfil de usuario creando una nueva entrada cada vez que se crea un usuario utilizando las señales de guardado de Django

modelos.py

from django.db.models.signals import *
from __future__ import unicode_literals

class UserProfile(models.Model):

    user_name = models.OneToOneField(User, related_name='profile')
    city = models.CharField(max_length=100, null=True)

    def __unicode__(self):  # __str__
        return unicode(self.user_name)

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        userProfile.objects.create(user_name=instance)

post_save.connect(create_user_profile, sender=User)

Esto creará automáticamente una instancia de empleado cuando se cree un nuevo usuario.

Si desea ampliar el modelo de usuario y desea agregar más información al crear un usuario, puede usar django-betterforms ( http://django-betterforms.readthedocs.io/en/latest/multiform.html ). Esto creará un formulario para agregar usuarios con todos los campos definidos en el modelo de perfil de usuario.

modelos.py

from django.db.models.signals import *
from __future__ import unicode_literals

class UserProfile(models.Model):

    user_name = models.OneToOneField(User)
    city = models.CharField(max_length=100)

    def __unicode__(self):  # __str__
        return unicode(self.user_name)

formas.py

from django import forms
from django.forms import ModelForm
from betterforms.multiform import MultiModelForm
from django.contrib.auth.forms import UserCreationForm
from .models import *

class ProfileForm(ModelForm):

    class Meta:
        model = Employee
        exclude = ('user_name',)


class addUserMultiForm(MultiModelForm):
    form_classes = {
        'user':UserCreationForm,
        'profile':ProfileForm,
    }

views.py

from django.shortcuts import redirect
from .models import *
from .forms import *
from django.views.generic import CreateView

class AddUser(CreateView):
    form_class = AddUserMultiForm
    template_name = "add-user.html"
    success_url = '/your-url-after-user-created'

    def form_valid(self, form):
        user = form['user'].save()
        profile = form['profile'].save(commit=False)
        profile.user_name = User.objects.get(username= user.username)
        profile.save()
        return redirect(self.success_url)

addUser.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="." method="post">
            {% csrf_token %}
            {{ form }}     
            <button type="submit">Add</button>
        </form>
     </body>
</html>

urls.py

from django.conf.urls import url, include
from appName.views import *
urlpatterns = [
    url(r'^add-user/$', AddUser.as_view(), name='add-user'),
]
Atul Yadav
fuente
Hola y gracias por esta respuesta. Todavía estoy luchando por entender cómo vincularlo todo en urls.py ... ¿alguna pista?
sal
@sal Agregado URL de ejemplo se puede comprobar ahora
Atul Yadav
¡¡Gracias!!. Me ayuda mucho. Buen ejemplo
Sunny Chaudhari,
@AtulYadav .. ¿Cuál es la versión de Django que usaste?
Rido
8

Extender el modelo de usuario de Django (UserProfile) como un profesional

Esto me ha resultado muy útil: enlace

Un extracto:

desde django.contrib.auth.models import User

class Employee(models.Model):
    user = models.OneToOneField(User)
    department = models.CharField(max_length=100)

>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
Massimo Variolo
fuente
2
Hecho. No entiendo por qué un -1. Mejor una edición que un voto negativo en este caso.
Massimo Variolo
3

Nuevo en Django 1.5, ahora puede crear su propio modelo de usuario personalizado (que parece ser algo bueno en el caso anterior). Consulte 'Personalización de autenticación en Django'

Probablemente la nueva característica más genial en la versión 1.5.

chhantyal
fuente
1
Si, de hecho. Pero tenga cuidado de que uno debe evitar esto a menos que sea necesario. Implementar el suyo propio por el motivo de esta pregunta es perfectamente válido, aunque en caso de que se sienta cómodo con las consecuencias documentadas. Para simplemente agregar campos, se recomienda una relación con el modelo de usuario normal .
gertvdijk
2

Esto es lo que hago y es, en mi opinión, la forma más sencilla de hacerlo. defina un administrador de objetos para su nuevo modelo personalizado y luego defina su modelo.

from django.db import models
from django.contrib.auth.models import PermissionsMixin, AbstractBaseUser, BaseUserManager

class User_manager(BaseUserManager):
    def create_user(self, username, email, gender, nickname, password):
        email = self.normalize_email(email)
        user = self.model(username=username, email=email, gender=gender, nickname=nickname)
        user.set_password(password)
        user.save(using=self.db)
        return user

    def create_superuser(self, username, email, gender, password, nickname=None):
        user = self.create_user(username=username, email=email, gender=gender, nickname=nickname, password=password)
        user.is_superuser = True
        user.is_staff = True
        user.save()
        return user



  class User(PermissionsMixin, AbstractBaseUser):
    username = models.CharField(max_length=32, unique=True, )
    email = models.EmailField(max_length=32)
    gender_choices = [("M", "Male"), ("F", "Female"), ("O", "Others")]
    gender = models.CharField(choices=gender_choices, default="M", max_length=1)
    nickname = models.CharField(max_length=32, blank=True, null=True)

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    REQUIRED_FIELDS = ["email", "gender"]
    USERNAME_FIELD = "username"
    objects = User_manager()

    def __str__(self):
        return self.username

No olvide agregar esta línea de código en su settings.py:

AUTH_USER_MODEL = 'YourApp.User'

Esto es lo que hago y siempre funciona.

Milad Khodabandehloo
fuente
0

Enfoque simple y efectivo es models.py

from django.contrib.auth.models import User
class CustomUser(User):
     profile_pic = models.ImageField(upload_to='...')
     other_field = models.CharField()
NeerajSahani
fuente