Розширення моделі користувача на спеціальні поля в Django


442

Який найкращий спосіб розширити модель користувача (у комплекті з програмою аутентифікації Django) спеціальними полями? Можливо, я також хотів би використовувати електронну пошту як ім'я користувача (для цілей аутентифікації).

Я вже бачив кілька способів зробити це, але не можу визначитися, який із них найкращий.


24
Більшість відповідей застаріли / застаріли. Перегляньте stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
користувача

@buffer - Перше питання, з яким ви зв’язалися (stackoverflow.com/a/22856042/781695), тепер було видалено. Інші досі діють.
Тоні

3
Щоб використовувати електронну пошту замість імені користувача для автентифікації, ця стаття корисна.


Я дуже захоплююсь тим, що питання було задано у 2008 році до того, як Джанго вийшов на світ @Farinha
Tessaracter

Відповіді:


253

Найменш болісний і дійсно рекомендований спосіб джанго - це через OneToOneField(User)власність.

Розширення існуючої моделі користувача

Якщо ви хочете зберігати інформацію, пов’язану з цим User, ви можете використовувати відношення один до одного до моделі, що містить поля для додаткової інформації. Цю модель "один на один" часто називають профільною моделлю, оскільки вона може зберігати інформацію, що не стосується автора, про користувача сайту.

Однак, розширення django.contrib.auth.models.Userта витіснення його також працює ...

Заміна користувацької моделі користувача

Деякі проекти можуть мати вимоги до аутентифікації, для яких вбудована Userмодель Django не завжди підходить. Наприклад, на деяких сайтах має сенс використовувати адресу електронної пошти як ідентифікаційний маркер замість імені користувача.

[Ред: Наступні два попередження та повідомлення , згадуючи, що це досить різко .]

Я б точно не тримався від зміни фактичного класу користувача у вашому вихідному дереві Django та / або копіювання та зміни модуля auth.


51
FYI - новий (1.0+) рекомендований метод - OneToOneField (User) docs.djangoproject.com/en/dev/topics/auth/…
Дейв Forgac

2
Шон Райдер ФБР дав деякі дійсно гарні причини , чому ви повинні НЕ розширити django.contrib.auth.models.User. Використовуйте замість OneToOneField (Користувача).
pydanny

2
user = models.ForeignKey(User, unique=True)це те саме, що user = models.OneToOneField(User)? Я думаю, що кінцевий ефект такий же? Але можливо реалізація в бекенде є іншою.
Сем Столінга

7
Чи може хто-небудь посилатися на аргумент / міркування про Шон Вершників для цього?
Джеремі Бланшард

1
Ось додаткова інформація про розширення моделей користувачів станом на django 1.7 docs
Derek Adair

226

Примітка: ця відповідь застаріла. дивіться інші відповіді, якщо ви використовуєте Django 1.7 або новішої версії.

Ось як я це роблю.

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

Це створить профіль користувача щоразу, коли користувач буде збережений, якщо він створений. Потім можна використовувати

  user.get_profile().whatever

Ось додаткова інформація від документів

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

Оновлення: Зверніть увагу, що AUTH_PROFILE_MODULEзастаріло з версії 1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module


6
Дякуємо за чіткий приклад, зауважте, що def create_user .... не є частиною класу UserProfile, і його слід вирівняти ліворуч.
PhoebeB

5
З цим рішенням повинні інші моделі ForeignKey для користувача чи UserProfile?
andrewrk

9
Інші моделі повинні використовувати user = models.ForeignKey( User )та отримувати об'єкт профілю через user.get_profile(). Пам'ятайте from django.contrib.admin.models import User.
Крейг Трейдер

1
Використовуючи цей метод, мені потрібно відокремитись, коли я отримую звичайну інформацію (ім’я, пароль) та спеціальну або є спосіб зробити це відразу? Те саме для створення нового користувача?
Мартін Тріго

5
Ця відповідь та коментарі застаріли, наприклад AUTH_PROFILE_MODULE застаріла,User
користувач

196

Що ж, минуло деякий час з 2008 року, і настав час для нової відповіді. З Джанго 1.5 ви зможете створити користувацький клас користувача. Насправді, на той час, коли я це пишу, це вже об'єднано в майстер, тож ви можете спробувати.

Там якась - то інформація про нього в документації або якщо ви хочете копати глибше в неї, в цій фіксації .

Все, що вам потрібно зробити, - це додати AUTH_USER_MODELдо налаштувань шлях із користувацьким класом користувача, який розширюється або AbstractBaseUser(більш настроювана версія), або AbstractUser(більш-менш старий клас користувача, який ви можете розширити).

Для людей, які лінуються натискати, ось приклад коду (взято з документів ):

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

Функція create_user, схоже, не зберігає ім’я користувача, як так ?!
Орка

6
Тому що в цьому прикладі emailє ім'я користувача.
Ondrej Slinták

5
Вам потрібно додати unique=Trueв поле електронної пошти, щоб USERNAME_FIELDприйняти його
Річард де Віт

Привіт, я намагався створити користувацького користувача, як ви сказали, але не зміг увійти, використовуючи електронну пошту спеціальної електронної пошти користувача. Ви б не проти сказати, чому?
Ліонель

Я не можу тобі допомогти без конкретики. Було б краще, якщо ви створили нове запитання.
Ondrej Slinták

47

Оскільки Django 1.5 ви можете легко розширити модель користувача та зберегти єдину таблицю в базі даних.

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

Ви також повинні налаштувати його як поточний клас користувача у вашому файлі налаштувань

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

Якщо ви хочете додати багато переваг користувачів, варіант OneToOneField може бути кращим вибором.

Примітка для людей, що розвивають бібліотеки сторонніх організацій: якщо вам потрібно отримати доступ до класу користувачів, пам’ятайте, що люди можуть його змінити. Скористайтеся офіційним помічником, щоб отримати потрібний клас

from django.contrib.auth import get_user_model

User = get_user_model()

3
Якщо ви плануєте використовувати django_social_auth, я рекомендую використовувати відносини OneToOne. НЕ використовуйте цей метод, інакше це зіпсує ваші міграції.
Німо

@Nimo: Ви можете розробити або навести посилання
користувач

@buffer, довгий час повернувся, але я думаю, що я спробував поєднати django_social_authплагін і визначити AUTH_USER_MODEL для користувача соціального аутентифікації. Потім, коли я запустив Manag.py міграцію, він зіпсував мій додаток. Коли замість цього я використовував модель користувача соціальних авторів як стосунки OneToOne, описані тут: stackoverflow.com/q/10638293/977116
Німо

3
Напевно, це стосується 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 schemaДжерело: docs.djangoproject.com/en/dev/topics/auth/customizing/…
користувач


23

Нижче - ще один підхід до розширення Користувача. Я вважаю, що це чіткіше, легше, читабельніше, ніж вище двох підходів.

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

Використовуючи вищезазначений підхід:

  1. вам не потрібно використовувати user.get_profile (). newattribute для доступу до додаткової інформації, що стосується користувача
  2. ви можете просто отримати доступ до додаткових нових атрибутів через user.newattribute

1
Мені набагато більше подобається підхід Скотта, який базується на успадкуванні об'єкта User, а не безпосередньо від моделі. Хтось може сказати, якщо такий підхід не мудрий?
BozoJoe

1
@BozoJoe - Я просто побіг в це питання імпортування даних дампа, який , як видається, є наслідком використання цього методу: stackoverflow.com/questions/8840068 / ...
Бен Regenspan

15

Ви можете просто розширити профіль користувача, створюючи новий запис щоразу, коли користувач створюється за допомогою сигналів Django Post save

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)

Це автоматично створить службовий екземпляр, коли буде створений новий користувач.

Якщо ви хочете розширити модель користувача та хочете додати додаткову інформацію під час створення користувача, ви можете використовувати django-betterforms ( http://django-betterforms.readthedocs.io/en/latest/multiform.html ). Це створить форму додавання користувача зі всіма полями, визначеними в моделі 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)

form.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'),
]

Привіт і дякую за цю відповідь. Я все ще намагаюся зрозуміти, як пов’язати це все разом у urls.py..any підказки?
сл

@sal Додано приклад URL-адреси, який ви можете перевірити зараз
Atul Yadav

Дякую!!. Це мені дуже допомагає. Хороший приклад
Сонячний Чаудхарі

@AtulYadav .. Яку версію Django ви використовували?
Рідо

8

Розширення моделі користувача Django (UserProfile), як Pro

Я вважаю це дуже корисним: посиланням

Виписка:

з імпортувати Користувача django.contrib.auth.models

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
Зроблено. Я не розумію, чому -1. В цьому випадку краще редагування, ніж нижченаведена анкета.
Массімо Варіоло

3

Нове в Django 1.5, тепер ви можете створити власну користувацьку модель користувача (що, здається, було б добре зробити у наведеному вище випадку). Див. "Налаштування автентифікації в Django"

Мабуть, найкрутіша нова функція на версію 1.5.


1
Так, справді. Але будьте уважні, що цього слід уникати, якщо це не потрібно. Виконання власної причини в цьому питанні цілком справедливо, хоча у випадку, якщо вам не подобаються наслідки, задокументовані. Для додавання полів просто відносини зі звичайною моделлю користувача рекомендується .
gertvdijk

2

Це те, що я роблю, і це, на мою думку, найпростіший спосіб зробити це. визначте менеджер об'єктів для вашої нової спеціалізованої моделі, а потім визначте свою модель.

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

Не забудьте додати цей рядок коду у свій settings.py:

AUTH_USER_MODEL = 'YourApp.User'

Це те, що я роблю, і це завжди працює.


0

Наразі станом на Django 2.2 рекомендованим способом при запуску нового проекту є створення власної користувацької моделі, яка успадковується від AbstractUser, а потім вкажіть AUTH_USER_MODEL на модель.

Джерело: https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project


0

Простий та ефективний підхід - models.py

from django.contrib.auth.models import User
class CustomUser(User):
     profile_pic = models.ImageField(upload_to='...')
     other_field = models.CharField()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.