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

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

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

Большинство ответов устарели / устарели. См. Stackoverflow.com/a/22856042/781695 и stackoverflow.com/q/14104677/781695 и stackoverflow.com/q/16880461/781695

user 04.04.2014 11:17

@buffer - Первый вопрос, на который вы указали (stackoverflow.com/a/22856042/781695), теперь удален. Остальные еще в силе.

Tony 19.05.2015 22:53

Чтобы использовать электронную почту вместо имени пользователя для аутентификации, полезно использовать Эта статья.

user5117926 15.07.2015 15:49
learnbatta.com/blog/using-custom-user-model-in-django-23
anjaneyulubatta505 03.02.2018 07:12

Мне очень приятно видеть, что этот вопрос был задан в 2008 году до того, как был выпущен Django @Farinha

Trect 20.09.2019 17:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
475
5
238 623
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Есть официальная рекомендация по хранение дополнительной информации о пользователях. Книга Django также обсуждает эту проблему в разделе Профили.

Ответ принят как подходящий

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

Extending the existing User model

If you wish to store information related to User, you can use a one-to-one relationship to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.

Тем не менее, расширение django.contrib.auth.models.User и его замена тоже работает ...

Substituting a custom User model

Some kinds of projects may have authentication requirements for which Django’s built-in User model is not always appropriate. For instance, on some sites it makes more sense to use an email address as your identification token instead of a username.

[Ed: Two warnings and a notification follow, mentioning that this is pretty drastic.]

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

С помощью этого решения вы должны использовать ForeignKey для пользователя или профиля?

andrewrk 11.05.2010 10:52

К вашему сведению, новый (1.0+) рекомендуемый метод - OneToOneField (User) docs.djangoproject.com/en/dev/topics/auth/…

Dave Forgac 18.11.2010 04:39

Шон Райдер из PBS привел несколько действительно веских причин, по которым вам следует нет расширять django.contrib.auth.models.User. Вместо этого используйте OneToOneField (User).

pydanny 14.01.2011 21:38

user = models.ForeignKey(User, unique=True) это то же самое, что и user = models.OneToOneField(User)? Я думаю, конечный эффект такой же? Но, возможно, реализация в бэкэнде другая.

Sam Stoelinga 23.04.2011 10:04

@pydanny: на самом деле он привел аргумент в пользу не преобладающий пользовательской модели

Webthusiast 31.10.2011 20:18

Может ли кто-нибудь сослаться на аргументы / доводы Шона Райдера?

Jeremy Blanchard 29.03.2012 03:00

@JeremyBlanchard: Я немного покопался, и лучшее, что я смог найти, - это один пункт из этого: birdhouse.org/blog/2010/09/13/djangocon-2010 "вам придется исправлять каждое повторно используемое приложение, которое вы вставляете"

Sasha Chedygov 08.11.2012 02:52

@JeremyBlanchard: Полное видео здесь: blip.tv/djangocon/… Аргумент исправления - единственный, который он выдвигает; он не упомянул никаких других проблем и, похоже, предположил, что это не нарушает условия сделки (у них все еще есть часть этого кода в их системе).

Sasha Chedygov 08.11.2012 03:42

@DaveForgac ссылка кажется неработающей, подробнее здесь docs.djangoproject.com/en/1.4/topics/auth/…

Bibhas Debnath 03.01.2013 12:41

@SamStoelinga от: (docs.djangoproject.com/en/dev/ref/models/fields/#oneto‌ onefield) Отношения один к одному. Концептуально это похоже на ForeignKey с unique = True, но «обратная» сторона отношения будет напрямую возвращать один объект.

chaim 30.05.2013 19:41
Здесь is some additional info about extending user models as of the django 1.7 docs
Derek Adair 21.11.2014 01:17

Разве я не могу просто создать новый класс, унаследованный от django.contrib.auth.models.User, если мне нужно только несколько дополнительных полей для моего пользователя?

アレックス 08.03.2015 17:53

Не знаю, как вы, но меня OneToOneField(User) очень раздражает для больших проектов! я предпочитаю использовать O.O.P. (расширение) в моих проектах!

Ebrahim Karimi 20.04.2018 11:10

Ниже приведен еще один подход к расширению User. Я считаю, что это более ясно, легко и читабельно, чем два вышеуказанных подхода.

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

Используя вышеуказанный подход:

  1. вам не нужно использовать user.get_profile (). newattribute для доступа к дополнительной информации связанный с пользователем
  2. вы можете просто получить прямой доступ дополнительные новые атрибуты через user.newattribute

Мне гораздо больше нравится подход Скотта, основанный на наследовании объекта User, а не непосредственно на модели. Кто-нибудь может сказать, не разумен ли такой подход?

BozoJoe 23.03.2010 17:55

@BozoJoe - Я только что столкнулся с этой проблемой при импорте данных дампа, которая, похоже, является следствием использования этого метода: stackoverflow.com/questions/8840068/…

Ben Regenspan 27.03.2012 03:27

Примечание: этот ответ устарел. см. другие ответы, если вы используете 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

Спасибо за ясный пример, обратите внимание, что def create_user .... не является частью класса UserProfile и должен быть выровнен по левому краю.

PhoebeB 02.04.2010 13:31

С этим решением следует ли другим моделям ForeignKey to User или UserProfile?

andrewrk 11.05.2010 10:52

Другие модели должны использовать user = models.ForeignKey( User ) и получать объект профиля через user.get_profile(). Не забывайте о from django.contrib.admin.models import User.

Craig Trader 13.08.2010 00:01

Используя этот метод, мне нужно разделить, когда я получаю обычную информацию (имя, пароль) и пользовательскую, или есть способ сделать это сразу? То же самое для создания нового пользователя?

Martin Trigaux 02.12.2010 23:29

Может кто-нибудь объяснить аргументы, взятые create_user_profile ??

Timothy Leung 28.03.2014 04:11

Этот ответ и комментарии устарели, например. AUTH_PROFILE_MODULE устарел, User

user 03.04.2014 10:36

Настройка AUTH_PROFILE_MODULE и метод get_profile() на модели User будут удалены в Django 1.7.

utapyngo 25.06.2014 11:23

Вы не должны ссылаться на / dev / в документации django, потому что ветвь / dev / документации всегда меняется. Ссылка, размещенная для #storing-additional-information-about-users, больше не относится к теме об этом.

GreenAsJade 28.11.2014 10:01

Что ж, прошло какое-то время с 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 09.04.2013 01:18

Потому что в этом примере email - это имя пользователя.

Ondrej Slinták 09.04.2013 10:14

Вам нужно добавить unique=True в поле электронной почты, чтобы USERNAME_FIELD принял его

Richard de Wit 24.06.2014 18:28

Привет, я пытался создать специального пользователя, как вы сказали, но не смог войти в систему, используя адрес электронной почты настраиваемого пользователя. не могли бы вы сказать почему?

Lionel 10.07.2014 11:18

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

Ondrej Slinták 10.07.2014 13:00

получаем следующую ошибку, когда мы запускаем syncd, что приводит к созданию суперпользователя: TypeError: create_superuser () получил неожиданный аргумент ключевого слова 'email'

user3048148 06.05.2015 08:30

Настраиваемые поля являются обычными полями модели, вы можете получить к ним доступ из шаблонов, например, используя. user.custom_field.

Ondrej Slinták 28.01.2016 14:34

Новое в Django 1.5, теперь вы можете создать свою собственную пользовательскую модель (что, кажется, неплохо сделать в приведенном выше случае). Обратитесь к 'Настройка аутентификации в Django'

Вероятно, самая крутая новая функция в версии 1.5.

Да, в самом деле. Но помните, что этого следует избегать без необходимости. Внедрение собственного по причине, указанной в этом вопросе, вполне допустимо, хотя в случае, если вас устраивают задокументированные последствия. Для простого добавления полей связь с обычной моделью пользователя Рекомендовано.

gertvdijk 28.03.2013 19:59

Начиная с 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()

Если вы планируете использовать django_social_auth, я рекомендую использовать отношения OneToOne. НЕ используйте этот метод, иначе он испортит вашу миграцию.

Nimo 11.01.2014 06:30

@Nimo: Не могли бы вы уточнить или процитировать ссылку

user 03.04.2014 12:53

@buffer, это было давно, но я думаю, что попытался объединить плагин django_social_auth и определить AUTH_USER_MODEL для пользователя социальной аутентификации. Затем, когда я запустил manage.py migrate, мое приложение испортилось. Когда вместо этого я использовал пользовательскую модель социальной аутентификации в качестве отношений OneToOne, описанных здесь: stackoverflow.com/q/10638293/977116

Nimo 03.04.2014 20:59

Вероятно, имеет отношение к 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/…

user 04.04.2014 09:35

Расширение модели пользователя Django (UserProfile) как профессионал

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

Выписка:

from 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

Выполнено. Я не понимаю, почему -1. В этом случае лучше отредактировать, чем проголосовать против.

Massimo Variolo 13.04.2016 17:39

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

sal 04.10.2016 15:20

@sal Добавлен пример URL, который вы можете проверить сейчас

Atul Yadav 04.10.2016 18:44

Спасибо!!. Это мне очень помогает. Хороший пример

Sunny Chaudhari 02.05.2017 13:01

@AtulYadav .. какую версию Django вы использовали?

Rido 30.12.2017 18:48

Это то, что я делаю, и, на мой взгляд, это самый простой способ сделать это. определите диспетчер объектов для вашей новой настроенной модели, а затем определите свою модель.

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'

Это то, что я делаю, и это всегда срабатывает.

В настоящее время, начиная с 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

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

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

Я бы никогда не использовал это решение. НО, как чистое и действительно простое решение, это лучший и самый правильный способ для чистого django.

ilyas Jumadurdyew 26.02.2021 16:45

Слишком поздно, но мой ответ предназначен для тех, кто ищет решение с последней версией Django.

models.py:

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    extra_Field_1 = models.CharField(max_length=25, blank=True)
    extra_Field_2 = models.CharField(max_length=25, blank=True)


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

вы можете использовать его в таких шаблонах:

<h2>{{ user.get_full_name }}</h2>
<ul>
  <li>Username: {{ user.username }}</li>
  <li>Location: {{ user.profile.extra_Field_1 }}</li>
  <li>Birth Date: {{ user.profile.extra_Field_2 }}</li>
</ul>

а в views.py вот так:

def update_profile(request, user_id):
    user = User.objects.get(pk=user_id)
    user.profile.extra_Field_1 = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit...'
    user.save()

Я становлюсь AttributeError: объект «Пользователь» не имеет ошибки атрибута «профиль» при срабатывании сигнала save_user_profile (instance.profile.save ()). Как ты это решил?

fbzyx 12.01.2021 13:00

@fbzyx вы импортировали модель profile?

Shahriar.M 13.01.2021 15:41

Попробуй это:

Создайте модель с именем Profile и укажите пользователю OneToOneField, а также укажите вариант related_name.

models.py

from django.db import models
from django.contrib.auth.models import *
from django.dispatch import receiver
from django.db.models.signals import post_save

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_profile')

    def __str__(self):
        return self.user.username

@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    try:
        if created:
            Profile.objects.create(user=instance).save()
    except Exception as err:
        print('Error creating user profile!')

Теперь для прямого доступа к профилю с помощью объекта User вы можете использовать related_name.

views.py

from django.http import HttpResponse

def home(request):
    profile = f'profile of {request.user.user_profile}'
    return HttpResponse(profile)

Другие вопросы по теме