Estendendo o modelo de usuário com campos personalizados no Django

442

Qual é a melhor maneira de estender o modelo de usuário (fornecido com o aplicativo de autenticação do Django) com campos personalizados? Também gostaria de usar o email como nome de usuário (para fins de autenticação).

Eu já vi algumas maneiras de fazer isso, mas não consigo decidir qual é a melhor.

Farinha
fonte
24
A maioria das respostas está desatualizada / descontinuada. Consulte stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
user
@buffer - A primeira pergunta à qual você vinculou (stackoverflow.com/a/22856042/781695) foi excluída. Os outros ainda são válidos.
19415 Tony
3
Para usar o email em vez do nome de usuário para autenticação, este artigo é útil.
Em
seguida
Estou muito fascinado por ver que a pergunta foi feita em 2008 antes do Django ser lançado @Farinha
Tessaracter

Respostas:

253

A maneira menos dolorosa e realmente recomendada pelo Django de fazer isso é através de uma OneToOneField(User)propriedade.

Estendendo o modelo de usuário existente

...

Se você deseja armazenar informações relacionadas User, pode usar um relacionamento individual para um modelo que contém os campos para obter informações adicionais. Esse modelo individual é geralmente chamado de modelo de perfil, pois pode armazenar informações não relacionadas à autenticação sobre um usuário do site.

Dito isto, estender django.contrib.auth.models.Usere suplantar também funciona ...

Substituindo um Modelo de Usuário Customizado

Alguns tipos de projetos podem ter requisitos de autenticação para os quais o modelo interno do Django Usernem sempre é apropriado. Por exemplo, em alguns sites, faz mais sentido usar um endereço de email como seu token de identificação em vez de um nome de usuário.

[Ed: dois avisos e uma notificação a seguir , mencionando que isso é bastante drástico .]

Eu definitivamente ficaria longe de mudar a classe User real na sua árvore de código-fonte do Django e / ou copiar e alterar o módulo de autenticação.

Ryan Duffield
fonte
50
Para sua informação, o novo método (1.0+) recomendado é o OneToOneField (User) docs.djangoproject.com/en/dev/topics/auth/…
Dave Forgac
2
Shawn Rider, da PBS, deu algumas boas razões para você não estender o django.contrib.auth.models.User. Use o OneToOneField (Usuário).
pydanny
2
user = models.ForeignKey(User, unique=True)é o mesmo que user = models.OneToOneField(User)? Eu acho que o efeito final é o mesmo? Mas talvez a implementação no back-end seja diferente.
Sam Stoelinga
7
Alguém pode se vincular ao argumento / raciocínio de Shawn Riders para isso?
Jeremy Blanchard
1
Aqui estão algumas informações adicionais sobre estendendo modelos de usuário como das Django 1.7 docs
Derek Adair
226

Nota: esta resposta está obsoleta. Veja outras respostas se você estiver usando o Django 1.7 ou posterior.

É assim que eu faço.

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

Isso criará um perfil de usuário sempre que um usuário for salvo, se ele for criado. Você pode então usar

  user.get_profile().whatever

Aqui estão mais algumas informações dos documentos

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

Atualização: observe que AUTH_PROFILE_MODULEestá obsoleta desde a v1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module

Passas de uva
fonte
6
Obrigado pelo exemplo claro, observe que def create_user .... não faz parte da classe UserProfile e deve ser alinhado à esquerda.
PhoebeB
5
Com esta solução, outros modelos devem usar o ForeignKey to User ou UserProfile?
22610 Andrewrk
9
Outros modelos devem usar user = models.ForeignKey( User )e recuperar o objeto de perfil via user.get_profile(). Lembre-se de from django.contrib.admin.models import User.
Craig Trader
1
Ao usar esse método, preciso separar quando recuperar informações usuais (nome, senha) e personalizadas ou existe uma maneira de fazer isso de uma só vez? O mesmo para a criação de um novo usuário?
Martin Trigaux
5
Esta resposta e comentários ficaram desatualizados, por exemplo, AUTH_PROFILE_MODULE está obsoleto,User
user
196

Bem, já passou algum tempo desde 2008 e é hora de receber uma nova resposta. Desde o Django 1.5, você poderá criar uma classe de usuário personalizada. Na verdade, no momento em que escrevo isso, ele já está mesclado no master, para que você possa experimentar.

Há algumas informações sobre isso nos documentos ou, se você quiser aprofundar, neste commit .

Tudo o que você precisa fazer é adicionar AUTH_USER_MODELàs configurações o caminho para a classe de usuário personalizada, que se estende AbstractBaseUser(versão mais personalizável) ou AbstractUser(classe de usuário mais ou menos antiga que você pode estender).

Para as pessoas com preguiça de clicar, aqui está um exemplo de código (extraído 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
fonte
A função create_user não parece armazenar o nome de usuário, como assim ?!
Orca
6
Porque neste exemplo, emailé o nome de usuário.
Ondrej Slinták
5
Você precisa adicionar unique=Trueao campo email para deixar USERNAME_FIELDaceito-o
Richard de Wit
Olá, estava tentando criar um usuário personalizado, como você disse, mas não conseguiu fazer o login usando o email do email de usuário personalizado. você não se importaria em dizer o porquê?
Lionel
Eu realmente não posso ajudá-lo sem detalhes. Seria melhor se você criou uma nova pergunta.
Ondrej Slinták
47

Desde o Django 1.5, você pode facilmente estender o modelo do usuário e manter uma única tabela no banco de dados.

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

Você também deve configurá-lo como classe de usuário atual no seu arquivo de configurações

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

Se você deseja adicionar muitas preferências dos usuários, a opção OneToOneField pode ser uma opção melhor.

Uma observação para as pessoas que desenvolvem bibliotecas de terceiros: se você precisar acessar a classe de usuário, lembre-se de que as pessoas podem alterá-la. Use o ajudante oficial para obter a classe certa

from django.contrib.auth import get_user_model

User = get_user_model()
Riccardo Galli
fonte
3
Se você planeja usar django_social_auth, eu recomendo usar um relacionamento OneToOne. NÃO use esse método, pois isso atrapalha suas migrações.
Nimo
@Nimo: Você poderia elaborar ou citar uma referência
usuário
@ Bufer, demorou muito tempo, mas acho que tentei combinar o django_social_authplugin e definir o AUTH_USER_MODEL para o usuário social de autenticação. Então, quando executei a migração do manage.py, atrapalhei meu aplicativo. Ao invés disso eu usei o modelo de usuário auth social como uma relação OneToOne s descritos aqui: stackoverflow.com/q/10638293/977116
Nimo
3
Provavelmente tem a ver com 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 schemaFonte: docs.djangoproject.com/en/dev/topics/auth/customizing/…
user
23

O abaixo é outra abordagem para estender um usuário. Eu sinto que é mais claro, fácil, legível do que acima de duas abordagens.

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

Usando a abordagem acima:

  1. você não precisa usar user.get_profile (). newattribute para acessar as informações adicionais relacionadas ao usuário
  2. você pode acessar diretamente novos atributos adicionais diretamente por meio do usuário.newattribute
Rama Vadakattu
fonte
1
Eu gosto muito mais da abordagem de Scott, baseada na herança do objeto User, em vez de diretamente fora do modelo. Alguém pode dizer se essa abordagem não é sábia?
BozoJoe
1
@BozoJoe - Acabei de encontrar este problema importando dados de despejo, o que parece ser uma conseqüência do uso deste método: stackoverflow.com/questions/8840068/…
Ben Regenspan
15

Você pode simplesmente estender o perfil do usuário criando uma nova entrada sempre que um usuário for criado usando os sinais de pós-gravação do 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)

Isso criará automaticamente uma instância de funcionário quando um novo usuário for criado.

Se você deseja estender o modelo do usuário e deseja adicionar mais informações ao criar um usuário, pode usar o django-betterforms ( http://django-betterforms.readthedocs.io/en/latest/multiform.html ). Isso criará um formulário de adição do usuário com todos os campos definidos no modelo 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
fonte
Oi e obrigado por esta resposta. Ainda estou lutando para entender como vincular tudo isso em urls.py..qualquer sugestão?
Sal
@sal Adicionado urls exemplo, você pode verificar agora
Atul Yadav
Obrigado!!. Isso me ajuda muito. Bom exemplo
Sunny Chaudhari
@AtulYadav .. qual é a versão do Django que você usou?
Rido
8

Estendendo o Django User Model (UserProfile) como um Pro

Achei isso muito útil: link

Um extrato:

de 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
fonte
2
Feito. Não entendo por que -1. Melhor uma edição do que um voto negativo neste caso.
Massimo Variolo 13/04
3

Novo no Django 1.5, agora você pode criar seu próprio modelo de usuário personalizado (o que parece ser uma boa coisa a se fazer no caso acima). Consulte 'Personalizando a autenticação no Django'

Provavelmente, o novo recurso mais interessante da versão 1.5.

chhantyal
fonte
1
Sim, de fato. Mas tenha cuidado para evitar isso a menos que seja necessário. Implementar o seu próprio pelo motivo desta pergunta é perfeitamente válido, caso você esteja confortável com as conseqüências documentadas. Para campos simplesmente adicionando um relacionamento com o modelo de usuário regular é recomendado .
gertvdijk
2

Isto é o que eu faço e, na minha opinião, é a maneira mais simples de fazer isso. defina um gerenciador de objetos para seu novo modelo customizado e defina seu 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

Não se esqueça de adicionar esta linha de código no seu settings.py:

AUTH_USER_MODEL = 'YourApp.User'

É isso que eu faço e sempre funciona.

Milad Khodabandehloo
fonte
0

Uma abordagem simples e eficaz é models.py

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