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

369

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

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

  • 21
    Большинство ответов устарели / устарели. См. Stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
  • 0
    @buffer - первый вопрос, на который вы ссылались (stackoverflow.com/a/22856042/781695), теперь удален. Другие все еще действительны.
Показать ещё 2 комментария
Теги:
django-users
django-authentication

9 ответов

190
Лучший ответ

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

Расширение существующей модели пользователя

& hellip;

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

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

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

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

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

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

  • 0
    С этим решением вы должны ForeignKey для пользователя или профиля?
  • 47
    К вашему сведению, новый (1.0+) рекомендуемый метод - OneToOneField (пользователь) docs.djangoproject.com/en/dev/topics/auth/…
Показать ещё 12 комментариев
222

Примечание: этот ответ устарел. см. другие ответы, если вы используете 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 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 устарел с версии v1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module

  • 6
    Спасибо за наглядный пример, обратите внимание, что def create_user .... не является частью класса UserProfile и должен быть выровнен по левому краю.
  • 5
    С этим решением должны другие модели ForeignKey для пользователя или UserProfile?
Показать ещё 6 комментариев
183

Ну, какое-то время прошло с 2008 года, и настало время для некоторого нового ответа. С Django 1.5 вы сможете создать пользовательский класс. Фактически, в то время, когда я пишу это, оно уже слилось с мастером, поэтому вы можете попробовать его.

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

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

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

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
  • 0
    Кажется, что функция create_user не хранит имя пользователя, как же так ?!
  • 6
    Потому что в этом примере email - это имя пользователя.
Показать ещё 8 комментариев
42

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

37

С 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. НЕ используйте этот метод, иначе он испортит ваши миграции.
  • 0
    @Nimo: Не могли бы вы разработать или привести ссылку
Показать ещё 2 комментария
19

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

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

Использование вышеприведенного подхода:

  • вам не нужно использовать user.get_profile(). newattribute для доступа к дополнительной информации связанные с пользователем
  • вы можете прямо получить доступ дополнительные новые атрибуты через user.newattribute
  • 1
    Мне больше нравится подход Скотта, основанный на наследовании объекта User, а не на модели. Кто-нибудь может сказать, если этот подход не является мудрым?
  • 1
    @BozoJoe - я только что столкнулся с этой проблемой при импорте данных дампа, что, по-видимому, является следствием использования этого метода: stackoverflow.com/questions/8840068/…
8

Вы можете просто расширить профиль пользователя, создавая новую запись каждый раз, когда пользователь создается с помощью сигналов сохранения сообщения django

models.py

from django.db.models.signals import *
from __future__ import unicode_literals

class userProfile(models.Model):

    userName = models.OneToOneField(User, related_name='profile')
    city = models.CharField(max_length=100, null=True)

    def __unicode__(self):  # __str__
        return unicode(self.userName)

def create_user_profile(sender, instance, created, **kwargs):
    if created:
    userProfile.objects.create(userName=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):

    userName = models.OneToOneField(User)
    city = models.CharField(max_length=100)

    def __unicode__(self):  # __str__
        return unicode(self.userName)

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 = ('userName',)


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 = "addUser.html"
    success_url = '/your url after user created'

    def form_valid(self, form):
        user = form['user'].save()
        profile = form['profile'].save(commit=False)
        profile.userName = 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='addDistributor'),
]
  • 0
    Привет и спасибо за этот ответ. Я все еще пытаюсь понять, как связать все это вместе в urls.py..any намеки?
  • 0
    @sal Добавлен пример URL, который вы можете проверить сейчас
Показать ещё 2 комментария
6

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

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

Выдержка:

из 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. Лучше редактировать, чем понижать голос в этом случае.
1

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню