Estensione del modello utente con campi personalizzati in Django


442

Qual è il modo migliore per estendere il modello utente (in bundle con l'app di autenticazione di Django) con campi personalizzati? Vorrei anche usare l'e-mail come nome utente (a fini di autenticazione).

Ho già visto alcuni modi per farlo, ma non riesco a decidere quale sia il migliore.


24
La maggior parte delle risposte è obsoleta / obsoleta. Si prega di consultare stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
utente

@buffer - La prima domanda a cui hai collegato (stackoverflow.com/a/22856042/781695) è stata ora eliminata. Gli altri sono ancora validi.
Tony,

3
Per utilizzare l'e-mail anziché il nome utente per l'autenticazione, questo articolo è utile.


Sono abbastanza affascinato dal vedere che la domanda è stata posta nel 2008 prima che Django fosse rilasciato @Farinha
Tessaracter,

Risposte:


253

Il modo meno doloroso e in effetti raccomandato da Django per farlo è attraverso una OneToOneField(User)proprietà.

Estensione del modello utente esistente

...

Se si desidera archiviare informazioni correlate User, è possibile utilizzare una relazione uno a uno con un modello contenente i campi per ulteriori informazioni. Questo modello one-to-one è spesso chiamato modello di profilo, poiché potrebbe archiviare informazioni non relative all'autorizzazione relative a un utente del sito.

Detto questo, l'estensione django.contrib.auth.models.Usere la soppressione funziona anche ...

Sostituzione di un modello utente personalizzato

Alcuni tipi di progetti possono avere requisiti di autenticazione per i quali il Usermodello integrato di Django non è sempre appropriato. Ad esempio, su alcuni siti ha più senso utilizzare un indirizzo e-mail come token di identificazione anziché un nome utente.

[Ed: seguono due avvertimenti e una notifica , menzionando che questo è piuttosto drastico .]

Sicuramente starei lontano dal cambiare la classe utente reale nel tuo albero dei sorgenti di Django e / o copiare e alterare il modulo auth.


51
Cordiali saluti, il nuovo metodo consigliato ( 1.0+
Dave Forgac,

2
Shawn Rider di PBS ha fornito alcuni ottimi motivi per cui non dovresti estendere django.contrib.auth.models.User. Utilizzare invece OneToOneField (Utente).
Pydanny,

2
user = models.ForeignKey(User, unique=True)è lo stesso di user = models.OneToOneField(User)? Penserei che l'effetto finale sia lo stesso? Ma forse l'implementazione nel backend è diversa.
Sam Stoelinga,

7
Qualcuno può collegarsi all'argomento / ragionamento di Shawn Riders per questo?
Jeremy Blanchard,

1
Ecco alcune informazioni aggiuntive sull'estensione dei modelli utente a partire dai documenti di django 1.7
Derek Adair,

226

Nota: questa risposta è obsoleta. vedi altre risposte se stai usando Django 1.7 o successivo.

Questo è come lo faccio.

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

Ciò creerà un profilo utente ogni volta che un utente viene salvato se viene creato. È quindi possibile utilizzare

  user.get_profile().whatever

Ecco alcune altre informazioni dai documenti

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

Aggiornamento: si noti che AUTH_PROFILE_MODULEè obsoleto dalla v1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module


6
Grazie per il chiaro esempio, nota che def create_user .... non fa parte della classe UserProfile e dovrebbe essere allineato a sinistra.
PhoebeB,

5
Con questa soluzione, altri modelli dovrebbero essere estranei all'utente o al profilo utente?
Andrewrk,

9
Altri modelli dovrebbero utilizzare user = models.ForeignKey( User )e recuperare l'oggetto profilo tramite user.get_profile(). Ricordati di from django.contrib.admin.models import User.
Craig Trader,

1
Usando questo metodo, devo separare quando recupero informazioni normali (nome, password) e una personalizzata o c'è un modo per farlo in una volta? Lo stesso per la creazione di un nuovo utente?
Martin Trigaux,

5
Questa risposta e i commenti sono diventati obsoleti, ad esempio AUTH_PROFILE_MODULE è obsoleto,User
utente

196

Bene, è passato del tempo dal 2008 ed è tempo di dare una nuova risposta. Da Django 1.5 sarai in grado di creare classi utente personalizzate. In realtà, al momento sto scrivendo questo, è già unito a master, quindi puoi provarlo.

Ci sono alcune informazioni a riguardo nei documenti o se vuoi approfondire, in questo commit .

Tutto quello che devi fare è aggiungere AUTH_USER_MODELalle impostazioni con il percorso alla classe utente personalizzata, che si estende AbstractBaseUser(versione più personalizzabile) o AbstractUser(classe utente più o meno vecchia che puoi estendere).

Per le persone che sono pigre da fare clic, ecco un esempio di codice (tratto da documenti ):

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

La funzione create_user non sembra memorizzare il nome utente, come ?!
Orca,

6
Perché in questo esempio emailè il nome utente.
Ondrej Slinták il

5
Devi aggiungere unique=Trueal campo e-mail per USERNAME_FIELDaccettarlo
Richard de Wit,

Ciao, stavo cercando di creare un utente personalizzato come hai detto, ma non sono riuscito a effettuare l'accesso utilizzando l'e-mail dell'utente e-mail personalizzata. non ti dispiacerebbe dire perché?
Lionel,

Non posso davvero aiutarti senza particolari. Sarebbe meglio se hai creato una nuova domanda.
Ondrej Slinták,

47

Da Django 1.5 è possibile estendere facilmente il modello utente e mantenere una singola tabella nel database.

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

È inoltre necessario configurarlo come classe utente corrente nel file delle impostazioni

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

Se vuoi aggiungere molte preferenze degli utenti, l'opzione OneToOneField potrebbe essere una scelta migliore.

Una nota per chi sviluppa librerie di terze parti: se devi accedere alla classe utente ricorda che le persone possono cambiarla. Usa l'aiutante ufficiale per ottenere la classe giusta

from django.contrib.auth import get_user_model

User = get_user_model()

3
Se django_social_authprevedi di utilizzare , ti consiglio di utilizzare una relazione OneToOne. NON usare questo metodo o rovinerà le tue migrazioni.
Nimo,

@Nimo: potresti elaborare o citare un riferimento
utente

@buffer, è passato molto tempo, ma penso di aver provato a combinare il django_social_authplugin e definire AUTH_USER_MODEL per l'utente dell'autenticazione sociale. Quindi, quando ho eseguito la gestione di migr.py, ha incasinato la mia app. Quando invece ho usato il modello utente di social auth come relazione OneToOne s descritta qui: stackoverflow.com/q/10638293/977116
Nimo,

3
Probabilmente ha a che fare 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 schemaFonte: docs.djangoproject.com/en/dev/topics/auth/customizing/…
utente


23

Quello sotto è un altro approccio per estendere un Utente. Sento che è più chiaro, facile, leggibile rispetto a due approcci.

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

Utilizzando l'approccio sopra:

  1. non è necessario utilizzare user.get_profile (). newattribute per accedere alle informazioni aggiuntive relative all'utente
  2. puoi semplicemente accedere direttamente ad altri nuovi attributi tramite user.newattribute

1
Mi piace molto di più l'approccio di Scott, basato sull'eredità dell'oggetto Utente piuttosto che direttamente sul modello. Qualcuno può dire se questo approccio non è saggio?
BozoJoe,

1
@BozoJoe - Ho appena incontrato questo problema importando dati di dettaglio, che sembra essere una conseguenza di questo metodo: stackoverflow.com/questions/8840068/...
Ben Regenspan

15

Puoi semplicemente estendere il profilo utente creando una nuova voce ogni volta che viene creato un utente utilizzando i segnali di salvataggio post di 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)

Ciò creerà automaticamente un'istanza del dipendente quando viene creato un nuovo utente.

Se si desidera estendere il modello utente e si desidera aggiungere ulteriori informazioni durante la creazione di un utente, è possibile utilizzare django-betterforms ( http://django-betterforms.readthedocs.io/it/latest/multiform.html ). Ciò creerà un modulo di aggiunta utente con tutti i campi definiti nel modello 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'),
]

Ciao e grazie per questa risposta. Faccio ancora fatica a capire come collegare tutto insieme in urls.py..ogni suggerimento?
sal

@sal Aggiunto esempio di URL che puoi controllare ora
Atul Yadav

Grazie!!. Mi aiuta molto. Buon esempio
Sunny Chaudhari,

@AtulYadav .. qual è la versione di Django che hai usato?
Rido,

8

Estensione di Django User Model (UserProfile) come un professionista

L'ho trovato molto utile: link

Un estratto:

da 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

2
Fatto. Non capisco perché un -1. Meglio una modifica che un downvote in questo caso.
Massimo Variolo,

3

Nuovo in Django 1.5, ora puoi creare il tuo modello utente personalizzato (che sembra essere una buona cosa da fare nel caso sopra). Fare riferimento a "Personalizzazione dell'autenticazione in Django"

Probabilmente la nuova funzionalità più interessante della versione 1.5.


1
Si Certamente. Ma attenzione che si dovrebbe evitare questo a meno che non sia necessario. L'implementazione del proprio per il motivo di questa domanda è perfettamente valida anche se nel caso in cui si sia a proprio agio con le conseguenze documentate. Per i campi semplice aggiunta di un rapporto con il modello utente normale si raccomanda .
gertvdijk,

2

Questo è quello che faccio ed è secondo me il modo più semplice per farlo. definire un gestore oggetti per il nuovo modello personalizzato, quindi definire il modello.

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

Non dimenticare di aggiungere questa riga di codice nel tuo settings.py:

AUTH_USER_MODEL = 'YourApp.User'

Questo è quello che faccio e funziona sempre.



0

Approccio semplice ed efficace è models.py

from django.contrib.auth.models import User
class CustomUser(User):
     profile_pic = models.ImageField(upload_to='...')
     other_field = models.CharField()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.