Extension du modèle utilisateur avec des champs personnalisés dans Django

442

Quelle est la meilleure façon d'étendre le modèle utilisateur (fourni avec l'application d'authentification de Django) avec des champs personnalisés? Je voudrais également éventuellement utiliser l'e-mail comme nom d'utilisateur (à des fins d'authentification).

J'ai déjà vu quelques façons de le faire, mais je ne peux pas décider laquelle est la meilleure.

Farinha
la source
24
La plupart des réponses sont obsolètes / obsolètes. Veuillez consulter stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
utilisateur
@buffer - La première question à laquelle vous avez lié (stackoverflow.com/a/22856042/781695) a été supprimée. Les autres sont toujours valables.
Tony
3
Pour utiliser l'e-mail au lieu du nom d'utilisateur pour l'authentification, cet article est utile.
Je suis assez fasciné de voir que la question a été posée en 2008 avant la sortie de Django @Farinha
Tessaracter

Réponses:

253

La façon la moins pénible et même recommandée par Django de le faire est d'utiliser une OneToOneField(User)propriété.

Extension du modèle utilisateur existant

Si vous souhaitez stocker des informations liées à User, vous pouvez utiliser une relation un à un avec un modèle contenant les champs pour des informations supplémentaires. Ce modèle un-à-un est souvent appelé modèle de profil, car il peut stocker des informations non liées à l'authentification sur un utilisateur du site.

Cela dit, l'étendre django.contrib.auth.models.Useret le supplanter fonctionne aussi ...

Substitution d'un modèle utilisateur personnalisé

Certains types de projets peuvent avoir des exigences d'authentification pour lesquelles le Usermodèle intégré de Django n'est pas toujours approprié. Par exemple, sur certains sites, il est plus logique d'utiliser une adresse e-mail comme jeton d'identification au lieu d'un nom d'utilisateur.

[Ed: Deux avertissements et une notification suivent , mentionnant que c'est assez drastique .]

Je resterais certainement à l'écart de changer la classe utilisateur réelle dans votre arborescence source Django et / ou de copier et de modifier le module d'authentification.

Ryan Duffield
la source
51
Pour info, la nouvelle méthode (1.0+) recommandée est OneToOneField (User) docs.djangoproject.com/en/dev/topics/auth/…
Dave Forgac
2
Shawn Rider de PBS a donné de très bonnes raisons pour lesquelles vous ne devriez pas étendre django.contrib.auth.models.User. Utilisez plutôt OneToOneField (utilisateur).
pydanny
2
user = models.ForeignKey(User, unique=True)est-ce la même chose que user = models.OneToOneField(User)? Je pense que l'effet final est le même? Mais peut-être que l'implémentation dans le backend est différente.
Sam Stoelinga
7
Quelqu'un peut-il établir un lien avec l'argument / raisonnement de Shawn Riders pour cela?
Jeremy Blanchard
1
Voici quelques informations supplémentaires sur l'extension des modèles utilisateur à partir des django 1.7 docs
Derek Adair
226

Remarque: cette réponse est déconseillée. voir d'autres réponses si vous utilisez Django 1.7 ou une version ultérieure.

Voilà comment je le fais.

#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'

Cela créera un profil utilisateur chaque fois qu'un utilisateur est enregistré s'il est créé. Vous pouvez ensuite utiliser

  user.get_profile().whatever

Voici quelques informations supplémentaires de la documentation

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

Mise à jour: veuillez noter qu'elle AUTH_PROFILE_MODULEest obsolète depuis la version 1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module

Raisins secs
la source
6
Merci pour l'exemple clair, notez que def create_user .... ne fait pas partie de la classe UserProfile et doit être aligné à gauche.
PhoebeB
5
Avec cette solution, les autres modèles de ForeignKey vers User ou UserProfile devraient-ils?
andrewrk
9
Les autres modèles doivent utiliser user = models.ForeignKey( User )et récupérer l'objet de profil via user.get_profile(). N'oubliez pas from django.contrib.admin.models import User.
Craig Trader
1
En utilisant cette méthode, je dois me séparer lorsque je récupère les informations habituelles (nom, mot de passe) et personnalisées ou existe-t-il un moyen de le faire à la fois? Idem pour la création d'un nouvel utilisateur?
Martin Trigaux
5
Cette réponse et ces commentaires sont devenus obsolètes, par exemple AUTH_PROFILE_MODULE est obsolète,User
utilisateur
196

Eh bien, un certain temps s'est écoulé depuis 2008 et il est temps pour une nouvelle réponse. Depuis Django 1.5, vous pourrez créer une classe d'utilisateurs personnalisée. En fait, au moment où j'écris ceci, il a déjà été fusionné dans master, vous pouvez donc l'essayer.

Il y a des informations à ce sujet dans les documents ou si vous souhaitez approfondir, dans ce commit .

Tout ce que vous avez à faire est d'ajouter AUTH_USER_MODELaux paramètres avec le chemin vers la classe d'utilisateurs personnalisée, qui s'étend soit AbstractBaseUser(version plus personnalisable) ou AbstractUser(plus ou moins ancienne classe d'utilisateurs que vous pouvez étendre).

Pour les personnes qui sont paresseuses à cliquer, voici un exemple de code (tiré de documents ):

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
la source
La fonction create_user ne semble pas stocker le nom d'utilisateur, comment cela?!
Orca
6
Parce que dans cet exemple, emailc'est le nom d'utilisateur.
Ondrej Slinták
5
Vous devez ajouter unique=Trueau champ email pour laisser l' USERNAME_FIELDaccepter
Richard de Wit
Bonjour, j'essayais de créer un utilisateur personnalisé comme vous l'avez dit, mais je n'ai pas pu me connecter à l'aide de la messagerie électronique de l'utilisateur personnalisé. cela ne vous dérangerait pas de dire pourquoi?
Lionel
Je ne peux pas vraiment vous aider sans détails. Ce serait mieux si vous créiez une nouvelle question.
Ondrej Slinták du
47

Depuis Django 1.5, vous pouvez facilement étendre le modèle utilisateur et conserver une seule table dans la base de données.

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

Vous devez également le configurer en tant que classe d'utilisateurs actuelle dans votre fichier de paramètres

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

Si vous souhaitez ajouter un grand nombre de préférences des utilisateurs, l'option OneToOneField peut être un meilleur choix.

Une note pour les personnes développant des bibliothèques tierces: si vous avez besoin d'accéder à la classe d'utilisateurs, n'oubliez pas que les gens peuvent la changer. Utilisez l'aide officielle pour obtenir la bonne classe

from django.contrib.auth import get_user_model

User = get_user_model()
Riccardo Galli
la source
3
Si vous prévoyez d'utiliser django_social_auth, je vous recommande d'utiliser une relation OneToOne. N'UTILISEZ PAS cette méthode sinon elle gâcherait vos migrations.
Nimo
@Nimo: Pourriez-vous élaborer ou citer une référence
utilisateur
@buffer, c'était il y a longtemps, mais je pense que j'ai essayé de combiner le django_social_authplugin et de définir AUTH_USER_MODEL à l'utilisateur d'authentification sociale. Ensuite, lorsque j'ai exécuté la migration de manage.py, cela a gâché mon application. Lorsque, à la place, j'ai utilisé le modèle d'utilisateur d'authentification sociale en tant que relation OneToOne, décrit ici: stackoverflow.com/q/10638293/977116
Nimo
3
Cela a probablement à voir avec la 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 schemasource: docs.djangoproject.com/en/dev/topics/auth/customizing/…
utilisateur
23

Celui ci-dessous est une autre approche pour étendre un utilisateur. Je pense que c'est plus clair, facile, lisible puis au-dessus de deux approches.

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

En utilisant l'approche ci-dessus:

  1. vous n'avez pas besoin d'utiliser user.get_profile (). newattribute pour accéder aux informations supplémentaires relatives à l'utilisateur
  2. vous pouvez simplement accéder directement à de nouveaux attributs supplémentaires via user.newattribute
Rama Vadakattu
la source
1
J'aime beaucoup l'approche de Scott, basée sur l'héritage de l'objet User plutôt que directement sur le modèle. Quelqu'un peut-il dire si cette approche n'est pas judicieuse?
BozoJoe
1
@BozoJoe - Je viens de rencontrer ce problème lors de l'importation de données de vidage, ce qui semble être une conséquence de l'utilisation de cette méthode: stackoverflow.com/questions/8840068/…
Ben Regenspan
15

Vous pouvez simplement étendre le profil de l'utilisateur en créant une nouvelle entrée à chaque fois qu'un utilisateur est créé à l'aide des signaux de sauvegarde de Django

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

Cela créera automatiquement une instance d'employé lorsqu'un nouvel utilisateur est créé.

Si vous souhaitez étendre le modèle utilisateur et souhaitez ajouter des informations supplémentaires lors de la création d'un utilisateur, vous pouvez utiliser django-betterforms ( http://django-betterforms.readthedocs.io/en/latest/multiform.html ). Cela créera un formulaire d'ajout d'utilisateur avec tous les champs définis dans le modèle UserProfile.

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

forms.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
la source
Salut et merci pour cette réponse. J'ai toujours du mal à comprendre comment lier tout cela dans urls.py..any hints?
sal
@sal Exemple d'URL ajouté que vous pouvez vérifier maintenant
Atul Yadav
Merci!!. Cela m'aide beaucoup. Bon exemple
Sunny Chaudhari
@AtulYadav .. quelle est la version de Django que vous avez utilisée?
Rido
8

Extension du modèle utilisateur Django (UserProfile) comme un pro

J'ai trouvé cela très utile: lien

Un extrait:

depuis 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
la source
2
Terminé. Je ne comprends pas pourquoi un -1. Mieux vaut un montage qu'un downvote dans ce cas.
Massimo Variolo
3

Nouveau dans Django 1.5, vous pouvez maintenant créer votre propre modèle d'utilisateur personnalisé (ce qui semble être une bonne chose à faire dans le cas ci-dessus). Voir 'Personnalisation de l'authentification dans Django'

Probablement la nouvelle fonctionnalité la plus cool de la version 1.5.

chhantyal
la source
1
Oui en effet. Mais attention, il faut éviter cela sauf si nécessaire. La mise en œuvre de la vôtre pour la raison de cette question est parfaitement valable, même si vous êtes à l'aise avec les conséquences documentées. Pour ajouter simplement des champs, une relation avec le modèle utilisateur standard est recommandée .
gertvdijk
2

C'est ce que je fais et c'est à mon avis le moyen le plus simple de le faire. définir un gestionnaire d'objets pour votre nouveau modèle personnalisé puis définir votre modèle.

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

N'oubliez pas d'ajouter cette ligne de code dans votre settings.py:

AUTH_USER_MODEL = 'YourApp.User'

C'est ce que je fais et ça marche toujours.

Milad Khodabandehloo
la source
0

Une approche simple et efficace est models.py

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