Mở rộng mô hình Người dùng với các trường tùy chỉnh trong Django


442

Cách tốt nhất để mở rộng mô hình Người dùng (đi kèm với ứng dụng xác thực của Django) với các trường tùy chỉnh là gì? Tôi cũng có thể muốn sử dụng email làm tên người dùng (cho mục đích xác thực).

Tôi đã thấy một vài cách để làm điều đó, nhưng không thể quyết định xem cách nào là tốt nhất.


24
Hầu hết các câu trả lời đã lỗi thời / không dùng nữa. Vui lòng xem stackoverflow.com/a/22856042/781695 & stackoverflow.com/q/14104677/781695 & stackoverflow.com/q/16880461/781695
người dùng

@buffer - Câu hỏi đầu tiên bạn liên kết đến (stackoverflow.com/a/22856042/781695) hiện đã bị xóa. Những người khác vẫn còn hiệu lực.
Tony

3
Để sử dụng email thay vì tên người dùng để xác thực, bài viết này rất hữu ích.


Tôi khá thích thú khi thấy câu hỏi đã được hỏi vào năm 2008 trước khi Django được phát hành @Farinha
Tessaracter

Câu trả lời:


253

Cách ít đau đớn nhất và thực sự được Django khuyên dùng là thông qua một OneToOneField(User)tài sản.

Mở rộng mô hình Người dùng hiện có

Giáo dục

Nếu bạn muốn lưu trữ thông tin liên quan đến User, bạn có thể sử dụng mối quan hệ một đối một với một mô hình có chứa các trường để biết thêm thông tin. Mô hình một-một này thường được gọi là mô hình hồ sơ, vì nó có thể lưu trữ thông tin không liên quan đến người dùng trang web.

Điều đó nói rằng, mở rộng django.contrib.auth.models.Uservà thay thế nó cũng hoạt động ...

Thay thế một mô hình người dùng tùy chỉnh

Một số loại dự án có thể có các yêu cầu xác thực mà Usermô hình tích hợp của Django không phải lúc nào cũng phù hợp. Chẳng hạn, trên một số trang web, việc sử dụng địa chỉ email làm mã thông báo nhận dạng của bạn thay vì tên người dùng sẽ hợp lý hơn.

[Ed: Hai cảnh báo và thông báo theo sau , đề cập rằng điều này khá quyết liệt .]

Tôi chắc chắn sẽ tránh xa việc thay đổi lớp Người dùng thực tế trong cây nguồn Django của bạn và / hoặc sao chép và thay đổi mô-đun xác thực.


50
FYI phương thức mới được đề xuất (1.0+) là OneToOneField (Người dùng) docs.djangoproject.com/en/dev/topics/auth/ trộm
Dave Forgac

2
Shawn Rider của PBS đã đưa ra một số lý do thực sự tốt tại sao bạn không nên mở rộng django.contrib.auth.models.User. Sử dụng OneToOneField (Người dùng) thay thế.
pydanny

2
user = models.ForeignKey(User, unique=True)cái này có giống như user = models.OneToOneField(User)không? Tôi sẽ nghĩ rằng hiệu ứng cuối cùng là như nhau? Nhưng có lẽ việc thực hiện trong phần phụ trợ là khác nhau.
Sam Stoelinga

7
Bất cứ ai cũng có thể liên kết đến lập luận / lý luận của Shawn Riders cho điều đó?
Jeremy Blanchard

1
Dưới đây là một số thông tin bổ sung về việc mở rộng các mô hình người dùng kể từ tài liệu django 1.7
Derek Adair

226

Lưu ý: câu trả lời này không được chấp nhận. xem các câu trả lời khác nếu bạn đang sử dụng Django 1.7 trở lên.

Đây là cách tôi làm điều đó.

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

Điều này sẽ tạo một userprofile mỗi khi người dùng được lưu nếu nó được tạo. Sau đó bạn có thể sử dụng

  user.get_profile().whatever

Dưới đây là một số thông tin từ các tài liệu

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

Cập nhật: Xin lưu ý rằng AUTH_PROFILE_MODULEkhông được chấp nhận kể từ v1.5: https://docs.djangoproject.com/en/1.5/ref/sinstall/#auth-profile-module


6
Cảm ơn ví dụ rõ ràng, lưu ý rằng def created_user .... không phải là một phần của lớp UserProfile và nên được căn trái.
PhoebeB

5
Với giải pháp này, các mô hình khác ForeignKey to User hay UserProfile?
andrewrk

9
Các mô hình khác nên sử dụng user = models.ForeignKey( User )và truy xuất đối tượng hồ sơ thông qua user.get_profile(). Nhớ nhé from django.contrib.admin.models import User.
Craig Trader

1
Bằng cách sử dụng phương pháp này, tôi cần tách riêng khi tôi truy xuất thông tin thông thường (tên, mật khẩu) và tùy chỉnh một hoặc có cách nào để làm điều đó cùng một lúc không? Tương tự cho việc tạo một người dùng mới?
Martin Trigaux

5
Câu trả lời và nhận xét này đã trở nên lỗi thời, ví dụ: AUTH_PROFILE_MODULE không được dùng nữa,User
người dùng

196

Vâng, một thời gian đã trôi qua kể từ năm 2008 và đó là thời gian cho một câu trả lời mới. Kể từ Django 1.5, bạn sẽ có thể tạo lớp Người dùng tùy chỉnh. Trên thực tế, tại thời điểm tôi viết bài này, nó đã được hợp nhất thành chủ, vì vậy bạn có thể dùng thử.

Có một số thông tin về nó trong các tài liệu hoặc nếu bạn muốn tìm hiểu sâu hơn về nó, trong cam kết này .

Tất cả những gì bạn phải làm là thêm AUTH_USER_MODELvào các cài đặt với đường dẫn đến lớp người dùng tùy chỉnh, mở rộng AbstractBaseUser(phiên bản có thể tùy chỉnh nhiều hơn) hoặc AbstractUser(lớp Người dùng cũ hơn hoặc ít hơn mà bạn có thể mở rộng).

Đối với những người lười nhấp, đây là ví dụ mã (lấy từ tài liệu ):

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

Hàm created_user dường như không lưu trữ tên người dùng, làm sao vậy?!
Orca

6
Bởi vì trong ví dụ này, emaillà tên người dùng.
Ondrej Slinták

5
Bạn cần thêm unique=Truevào trường email để USERNAME_FIELDchấp nhận nó
Richard de Wit

Xin chào, tôi đã cố gắng tạo người dùng tùy chỉnh như bạn đã nói nhưng không thể đăng nhập bằng email của email người dùng tùy chỉnh. bạn sẽ không phiền để nói tại sao chứ?
Lionel

Tôi thực sự không thể giúp bạn mà không có chi tiết cụ thể. Sẽ tốt hơn nếu bạn tạo câu hỏi mới.
Ondrej Slinták

47

Kể từ Django 1.5, bạn có thể dễ dàng mở rộng mô hình người dùng và giữ một bảng duy nhất trên cơ sở dữ liệu.

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

Bạn cũng phải định cấu hình nó làm lớp người dùng hiện tại trong tệp cài đặt của mình

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

Nếu bạn muốn thêm nhiều tùy chọn của người dùng, tùy chọn OneToOneField có thể là một lựa chọn tốt hơn.

Một lưu ý cho những người phát triển thư viện bên thứ ba: nếu bạn cần truy cập vào lớp người dùng hãy nhớ rằng mọi người có thể thay đổi nó. Sử dụng người trợ giúp chính thức để có được lớp đúng

from django.contrib.auth import get_user_model

User = get_user_model()

3
Nếu bạn có kế hoạch sử dụng django_social_auth, tôi khuyên bạn nên sử dụng mối quan hệ OneToOne. KHÔNG sử dụng phương pháp này nếu không nó sẽ làm rối loạn việc di chuyển của bạn.
Nimo

@Nimo: Bạn có thể xây dựng hoặc trích dẫn một tài liệu tham khảo
người dùng

@buffer, đã lâu rồi, nhưng tôi nghĩ tôi đã thử kết hợp django_social_authplugin và xác định AUTH_USER_MODEL cho người dùng auth xã hội. Sau đó, khi tôi chạy Manage.txt di chuyển, nó làm rối ứng dụng của tôi. Thay vào đó, khi tôi sử dụng mô hình người dùng xác thực xã hội như một mối quan hệ OneToOne được mô tả ở đây: stackoverflow.com/q/10638293/977116
Nimo

3
Có lẽ phải làm với 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 schemaNguồn: docs.djangoproject.com/en/dev/topics/auth/customizing/ Kẻ
người dùng


23

Cách dưới đây là một cách tiếp cận khác để mở rộng Người dùng. Tôi cảm thấy nó rõ ràng hơn, dễ dàng, dễ đọc hơn hai cách tiếp cận.

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

Sử dụng phương pháp trên:

  1. bạn không cần sử dụng user.get_profile (). newattribution để truy cập thông tin bổ sung liên quan đến người dùng
  2. bạn chỉ có thể truy cập trực tiếp các thuộc tính mới bổ sung thông qua user.newattribution

1
Tôi thích cách tiếp cận của Scott tốt hơn nhiều, dựa trên sự kế thừa của đối tượng Người dùng thay vì trực tiếp tắt mô hình. Bất cứ ai có thể nói nếu cách tiếp cận này là không khôn ngoan?
BozoJoe

1
@BozoJoe - Tôi vừa gặp phải vấn đề này khi nhập dữ liệu kết xuất, dường như là hậu quả của việc sử dụng phương thức này: stackoverflow.com/questions/8840068/
Lỗi

15

Bạn chỉ có thể mở rộng hồ sơ người dùng bằng cách tạo một mục mới mỗi khi người dùng được tạo bằng cách sử dụng tín hiệu lưu bài Django

mô hình

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)

Điều này sẽ tự động tạo một cá thể nhân viên khi người dùng mới được tạo.

Nếu bạn muốn mở rộng mô hình người dùng và muốn thêm thông tin trong khi tạo người dùng, bạn có thể sử dụng django-betterforms ( http://django-betterforms.readthedocs.io/en/latest/multiform.html ). Điều này sẽ tạo một biểu mẫu thêm người dùng với tất cả các trường được xác định trong mô hình UserProfile.

mô hình

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)

biểu mẫu

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,
    }

lượt xem

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>

url

from django.conf.urls import url, include
from appName.views import *
urlpatterns = [
    url(r'^add-user/$', AddUser.as_view(), name='add-user'),
]

Xin chào và cảm ơn vì câu trả lời này. Tôi vẫn đang cố gắng để hiểu làm thế nào để liên kết tất cả lại với nhau trong urls.py..nhiều gợi ý?
sal

@sal Đã thêm url ví dụ bạn có thể kiểm tra ngay bây giờ
Atul Yadav

Cảm ơn!!. Nó giúp tôi rất nhiều. Ví dụ điển hình
Sunny Chaudhari

@AtulYadav .. phiên bản Django mà bạn đã sử dụng là gì?
Rido

8

Mở rộng Mô hình người dùng Django (UserProfile) như Pro

Tôi đã tìm thấy điều này rất hữu ích: liên kết

Trích:

từ django.contrib.auth.models người dùng nhập khẩu

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
Làm xong. Tôi không hiểu tại sao -1. Tốt hơn là chỉnh sửa hơn một downvote trong trường hợp này.
Massimo Variolo

3

Mới trong Django 1.5, bây giờ bạn có thể tạo Mô hình người dùng tùy chỉnh của riêng bạn (có vẻ là điều tốt để làm trong trường hợp trên). Tham khảo 'Tùy chỉnh xác thực trong Django'

Có lẽ là tính năng mới thú vị nhất trên phiên bản 1.5.


1
Vâng, thực sự. Nhưng hãy cẩn thận rằng người ta nên tránh điều này trừ khi cần thiết. Việc thực hiện của riêng bạn vì lý do trong câu hỏi này là hoàn toàn hợp lệ mặc dù trong trường hợp bạn cảm thấy thoải mái với các hậu quả được ghi lại. Để đơn giản thêm các trường, mối quan hệ với mô hình Người dùng thông thường được khuyến nghị .
gertvdijk

2

Đây là những gì tôi làm và theo ý kiến ​​của tôi cách đơn giản nhất để làm điều này. xác định một trình quản lý đối tượng cho mô hình tùy chỉnh mới của bạn sau đó xác định mô hình của bạn.

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

Đừng quên thêm dòng mã này vào settings.py:

AUTH_USER_MODEL = 'YourApp.User'

Đây là những gì tôi làm và nó luôn hoạt động.



0

Cách tiếp cận đơn giản và hiệu quả là model.py

from django.contrib.auth.models import User
class CustomUser(User):
     profile_pic = models.ImageField(upload_to='...')
     other_field = models.CharField()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.