Erweitern des Benutzermodells um benutzerdefinierte Felder in Django

442

Wie kann das Benutzermodell (zusammen mit der Authentifizierungs-App von Django) am besten um benutzerdefinierte Felder erweitert werden? Möglicherweise möchte ich auch die E-Mail als Benutzernamen verwenden (zur Authentifizierung).

Ich habe bereits einige Möglichkeiten gesehen , kann mich aber nicht entscheiden, welche die beste ist.

Farinha
quelle
24
Die meisten Antworten sind veraltet / veraltet. Siehe stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
Benutzer
@buffer - Die erste Frage, auf die Sie verlinkt haben (stackoverflow.com/a/22856042/781695), wurde jetzt gelöscht. Die anderen sind noch gültig.
Tony
3
Dieser Artikel ist nützlich, um E-Mail anstelle des Benutzernamens zur Authentifizierung zu verwenden .
Ich bin ziemlich fasziniert zu sehen, dass die Frage 2008 gestellt wurde, bevor Django
Tessaracter

Antworten:

253

Der am wenigsten schmerzhafte und in der Tat von Django empfohlene Weg, dies zu tun, ist durch eine OneToOneField(User)Eigenschaft.

Erweitern des vorhandenen Benutzermodells

Wenn Sie Informationen zu speichern möchten User, können Sie eine Eins-zu-Eins-Beziehung zu einem Modell verwenden, das die Felder für zusätzliche Informationen enthält. Dieses Eins-zu-Eins-Modell wird häufig als Profilmodell bezeichnet, da es möglicherweise nicht authentifizierungsbezogene Informationen über einen Site-Benutzer speichert.

Das heißt, es zu erweitern django.contrib.auth.models.Userund zu ersetzen funktioniert auch ...

Ersetzen eines benutzerdefinierten Benutzermodells

Für einige Arten von Projekten gelten möglicherweise Authentifizierungsanforderungen, für die das integrierte UserModell von Django nicht immer geeignet ist. Auf einigen Websites ist es beispielsweise sinnvoller, anstelle eines Benutzernamens eine E-Mail-Adresse als Identifikations-Token zu verwenden.

[Ed: Es folgen zwei Warnungen und eine Benachrichtigung , in der erwähnt wird, dass dies ziemlich drastisch ist .]

Ich würde mich definitiv davon fernhalten, die tatsächliche Benutzerklasse in Ihrem Django-Quellbaum zu ändern und / oder das Auth-Modul zu kopieren und zu ändern.

Ryan Duffield
quelle
50
Zu Ihrer Information,
Dave Forgac
2
Shawn Rider von PBS gab einige wirklich gute Gründe an, warum Sie django.contrib.auth.models.User nicht erweitern sollten. Verwenden Sie stattdessen OneToOneField (Benutzer).
Pydanny
2
user = models.ForeignKey(User, unique=True)ist das das gleiche wie user = models.OneToOneField(User)? Ich würde denken, der Endeffekt ist der gleiche? Aber vielleicht ist die Implementierung im Backend anders.
Sam Stoelinga
7
Kann jemand auf Shawn Riders Argument / Argumentation dafür verweisen?
Jeremy Blanchard
1
Hier sind einige zusätzliche Informationen über die Erweiterung von Benutzermodellen ab dem Django 1.7 Docs
Derek Adair
226

Hinweis: Diese Antwort ist veraltet. Weitere Antworten finden Sie, wenn Sie Django 1.7 oder höher verwenden.

So mache ich es.

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

Dadurch wird jedes Mal ein Benutzerprofil erstellt, wenn ein Benutzer gespeichert wird, wenn er erstellt wird. Sie können dann verwenden

  user.get_profile().whatever

Hier finden Sie weitere Informationen aus den Dokumenten

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

Update: Bitte beachten Sie, dass dies AUTH_PROFILE_MODULEseit Version 1.5 veraltet ist: https://docs.djangoproject.com/de/1.5/ref/settings/#auth-profile-module

Rosinen
quelle
6
Beachten Sie, dass def create_user .... nicht Teil der UserProfile-Klasse ist und nach links ausgerichtet werden sollte.
PhoebeB
5
Sollten mit dieser Lösung andere Modelle ForeignKey to User oder UserProfile?
Andrewrk
9
Andere Modelle sollten user = models.ForeignKey( User )das Profilobjekt verwenden und über abrufen user.get_profile(). Denken Sie daran from django.contrib.admin.models import User.
Craig Trader
1
Bei Verwendung dieser Methode muss ich mich trennen, wenn ich die üblichen Informationen (Name, Passwort) und die benutzerdefinierten Informationen abrufe, oder gibt es eine Möglichkeit, dies sofort zu tun? Gleiches gilt für die Erstellung eines neuen Benutzers?
Martin Trigaux
5
Diese Antwort & Kommentare sind veraltet, zB AUTH_PROFILE_MODULE ist veraltet,User
Benutzer
196

Nun, seit 2008 ist einige Zeit vergangen und es ist Zeit für eine neue Antwort. Seit Django 1.5 können Sie benutzerdefinierte Benutzerklassen erstellen. Zum Zeitpunkt, an dem ich dies schreibe, ist es bereits in master zusammengeführt, sodass Sie es ausprobieren können.

In diesem Commit finden Sie einige Informationen dazu in Dokumenten oder wenn Sie sich eingehender damit befassen möchten .

Alles, was Sie tun müssen, ist AUTH_USER_MODEL, Einstellungen mit dem Pfad zur benutzerdefinierten Benutzerklasse hinzuzufügen , der entweder erweitert AbstractBaseUser(anpassbarere Version) oderAbstractUser (mehr oder weniger alte Benutzerklasse, die Sie erweitern können).

Für Leute, die faul sind zu klicken, hier das Codebeispiel (aus Dokumenten entnommen ):

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
quelle
Die Funktion create_user scheint den Benutzernamen nicht zu speichern, wie?!
Orca
6
Denn in diesem Beispiel emailist der Benutzername.
Ondrej Slinták
5
Sie müssen unique=Truedem E-Mail-Feld hinzufügen , USERNAME_FIELDdamit es akzeptiert wird
Richard de Wit
Hallo, ich habe versucht, einen benutzerdefinierten Benutzer zu erstellen, wie Sie sagten, konnte mich jedoch nicht per E-Mail oder E-Mail eines benutzerdefinierten Benutzers anmelden. Würde es Ihnen nichts ausmachen zu sagen warum?
Lionel
Ohne Einzelheiten kann ich Ihnen nicht wirklich helfen. Es wäre besser, wenn Sie eine neue Frage erstellen würden.
Ondrej Slinták
47

Seit Django 1.5 können Sie das Benutzermodell problemlos erweitern und eine einzelne Tabelle in der Datenbank behalten.

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

Sie müssen es auch als aktuelle Benutzerklasse in Ihrer Einstellungsdatei konfigurieren

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

Wenn Sie viele Benutzereinstellungen hinzufügen möchten, ist die OneToOneField-Option möglicherweise die bessere Wahl.

Ein Hinweis für Benutzer, die Bibliotheken von Drittanbietern entwickeln: Wenn Sie auf die Benutzerklasse zugreifen müssen, denken Sie daran, dass Benutzer diese ändern können. Verwenden Sie den offiziellen Helfer, um die richtige Klasse zu finden

from django.contrib.auth import get_user_model

User = get_user_model()
Riccardo Galli
quelle
3
Wenn Sie die Verwendung planen django_social_auth, empfehle ich die Verwendung einer OneToOne-Beziehung. Verwenden Sie diese Methode NICHT, da dies Ihre Migrationen durcheinander bringt.
Nimo
@Nimo: Könnten Sie eine Referenz ausarbeiten oder zitieren
Benutzer
@buffer, es ist lange her, aber ich glaube, ich habe versucht, das django_social_authPlugin zu kombinieren und AUTH_USER_MODEL für den Benutzer der sozialen Authentifizierung zu definieren. Als ich dann manage.py migrate ausführte, war meine App durcheinander. Als ich stattdessen das Social-Auth-Benutzermodell als OneToOne-Beziehung verwendete, wurde hier beschrieben: stackoverflow.com/q/10638293/977116
Nimo
3
Hat wahrscheinlich mit 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 schemaQuelle zu tun : docs.djangoproject.com/de/dev/topics/auth/customizing/…
Benutzer
23

Das Folgende ist ein weiterer Ansatz zum Erweitern eines Benutzers. Ich denke, es ist klarer, einfacher und lesbarer als über zwei Ansätzen.

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

Verwenden Sie den obigen Ansatz:

  1. Sie müssen user.get_profile (). newattribute nicht verwenden, um auf die zusätzlichen Informationen zuzugreifen, die sich auf den Benutzer beziehen
  2. Sie können einfach direkt über user.newattribute auf zusätzliche neue Attribute zugreifen
Rama Vadakattu
quelle
1
Ich mag Scotts Ansatz viel besser, der auf der Vererbung des Benutzerobjekts basiert und nicht direkt vom Modell abhängt. Kann jemand sagen, ob dieser Ansatz nicht klug ist?
BozoJoe
1
@BozoJoe - Ich bin gerade auf dieses Problem beim Importieren von Dump-Daten gestoßen, was eine Folge der Verwendung dieser Methode zu sein scheint: stackoverflow.com/questions/8840068/…
Ben Regenspan
15

Sie können das Benutzerprofil einfach erweitern, indem Sie jedes Mal, wenn ein Benutzer mithilfe von Django-Post-Save-Signalen erstellt wird, einen neuen Eintrag erstellen

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)

Dadurch wird automatisch eine Mitarbeiterinstanz erstellt, wenn ein neuer Benutzer erstellt wird.

Wenn Sie das Benutzermodell erweitern und beim Erstellen eines Benutzers weitere Informationen hinzufügen möchten, können Sie django-betterforms ( http://django-betterforms.readthedocs.io/en/latest/multiform.html) verwenden ). Dadurch wird ein Benutzer-Add-Formular mit allen im UserProfile-Modell definierten Feldern erstellt.

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
quelle
Hallo und danke für diese Antwort. Ich habe immer noch Probleme zu verstehen, wie ich alles in urls.py miteinander verknüpfen kann.
Sal
@sal URLs Beispiel hinzugefügt, das Sie jetzt überprüfen können
Atul Yadav
Vielen Dank!!. Es hilft mir sehr. Gutes Beispiel
Sunny Chaudhari
@AtulYadav .. was ist die Version von Django, die Sie verwendet haben?
Rido
8

Erweitern des Django-Benutzermodells (UserProfile) wie ein Profi

Ich fand das sehr nützlich: Link

Ein Auszug:

aus django.contrib.auth.models Benutzer importieren

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
quelle
2
Erledigt. Ich verstehe nicht, warum ein -1. Besser eine Bearbeitung als eine Abwertung in diesem Fall.
Massimo Variolo
3

Neu in Django 1.5, jetzt können Sie Ihr eigenes benutzerdefiniertes Benutzermodell erstellen (was im obigen Fall eine gute Sache zu sein scheint). Siehe 'Anpassen der Authentifizierung in Django'

Wahrscheinlich die coolste neue Funktion in Version 1.5.

chhantyal
quelle
1
Ja, in der Tat. Aber Vorsicht, man sollte dies vermeiden, es sei denn, dies ist notwendig. Die Implementierung Ihrer eigenen aus dem Grund in dieser Frage ist jedoch vollkommen gültig, falls Sie mit den dokumentierten Konsequenzen vertraut sind. Zum einfachen Hinzufügen von Feldern wird eine Beziehung zum regulären Benutzermodell empfohlen .
Gertvdijk
2

Dies ist, was ich tue und es ist meiner Meinung nach der einfachste Weg, dies zu tun. Definieren Sie einen Objektmanager für Ihr neues benutzerdefiniertes Modell und definieren Sie dann Ihr Modell.

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

Vergessen Sie nicht, diese Codezeile in Ihre settings.py:

AUTH_USER_MODEL = 'YourApp.User'

Das ist was ich tue und es funktioniert immer.

Milad Khodabandehloo
quelle
0

Einfacher und effektiver Ansatz ist models.py

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