Расширение модели User с помощью пользовательских полей в Django

442

Как лучше всего расширить модель User (в комплекте с приложением аутентификации Django) с помощью пользовательских полей? Я также хотел бы использовать электронную почту в качестве имени пользователя (в целях аутентификации).

Я уже видел несколько способов сделать это, но не могу решить, какой из них лучший.

Farinha
источник
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

Наименее болезненный и действительно рекомендуемый Django способ сделать это - через OneToOneField(User)свойство.

Расширение существующей модели User

...

Если вы хотите хранить информацию, относящуюся к User, вы можете использовать отношение « один к одному» с моделью, содержащей поля для дополнительной информации. Эту модель «один к одному» часто называют моделью профиля, поскольку она может хранить информацию, не связанную с аутентификацией, о пользователе сайта.

Тем не менее, расширение django.contrib.auth.models.Userи вытеснение это также работает ...

Подстановка пользовательской модели пользователя

Некоторые виды проектов могут иметь требования к аутентификации, для которых встроенная Userмодель Django не всегда подходит. Например, на некоторых сайтах имеет смысл использовать адрес электронной почты в качестве идентификационного токена вместо имени пользователя.

[Ред .: За ним следуют два предупреждения и уведомление о том, что это довольно радикально .]

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

Райан Даффилд
источник
51
К вашему сведению, новый (1.0+) рекомендуемый метод - OneToOneField (пользователь) docs.djangoproject.com/en/dev/topics/auth/…
Дейв Форгак
2
Шон Райдер из PBS привел несколько веских причин, по которым вам не следует расширять django.contrib.auth.models.User. Вместо этого используйте OneToOneField (User).
пиданный
2
user = models.ForeignKey(User, unique=True)это так же, как user = models.OneToOneField(User)? Я думаю, что конечный эффект такой же? Но, возможно, реализация в бэкэнде другая.
Сэм Стоилинга
7
Может кто-нибудь связать с аргументом / обоснованием Шона Райдера?
Джереми Бланшар
1
Вот некоторая дополнительная информация о расширении пользовательских моделей с django 1.7 документа
Дерек Адэйр
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'

Это создаст 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 и должен быть выровнен по левому краю.
Фиби
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 года прошло некоторое время, и пришло время для свежего ответа. Начиная с Django 1.5 вы сможете создавать пользовательский класс User. На самом деле, в то время, когда я пишу это, оно уже объединено с мастером, так что вы можете попробовать его.

Есть некоторая информация об этом в документах или, если вы хотите углубиться в это, в этом коммите .

Все, что вам нужно сделать, это добавить 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 не хранит имя пользователя, как же так ?!
Orca
6
Потому что в этом примере emailэто имя пользователя.
Ондрей Слинтак
5
Вы должны добавить unique=Trueв поле электронной почты, чтобы USERNAME_FIELDпринять его
Ричард де Вит
Привет, я пытался создать пользовательского пользователя, как вы сказали, но не смог войти, используя электронную почту пользовательского адреса электронной почты. Вы не против сказать почему?
Лайонел
Я не могу действительно помочь вам без подробностей. Было бы лучше, если бы вы создали новый вопрос.
Ондрей Слинтак
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 для пользователя социальной аутентификации. Затем, когда я запустил manage.py migrate, это испортило мое приложение. Когда вместо этого я использовал модель социальной аутентификации пользователя в качестве отношения OneToOne, описанного здесь: stackoverflow.com/q/10638293/977116
Nimo
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/…
Бен Регенспан,
15

Вы можете просто расширить профиль пользователя, создавая новую запись каждый раз, когда пользователь создается с помощью сигналов сохранения сообщений 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)

Это автоматически создаст экземпляр сотрудника при создании нового пользователя.

Если вы хотите расширить пользовательскую модель и добавить дополнительную информацию при создании пользователя, вы можете использовать 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)

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'),
]
Атул Ядав
источник
Привет и спасибо за этот ответ. Я все еще пытаюсь понять, как связать все это вместе в urls.py..any намеки?
Sal
@sal Добавлен пример URL, который вы можете проверить сейчас
Атул Ядав
Спасибо!!. Это мне очень помогает. Хороший пример
Санни Чаудхари
@AtulYadav .. какая версия Django, которую вы использовали?
Ридо
8

Расширение Django User Model (UserProfile) как профессионала

Я нашел это очень полезным: ссылка

Выписка:

из 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
Выполнено. Я не понимаю, почему -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

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

from django.contrib.auth.models import User
class CustomUser(User):
     profile_pic = models.ImageField(upload_to='...')
     other_field = models.CharField()
NeerajSahani
источник