4

django自定义用户模型

 2 years ago
source link: https://www.hi-roy.com/posts/django%E8%87%AA%E5%AE%9A%E4%B9%89%E7%94%A8%E6%88%B7%E6%A8%A1%E5%9E%8B/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

django自定义用户模型

2016-06-29

Django最方便的一点可以说就是自带的用户系统了,不过某些情况下自带的用户系统不太符合项目需求,比如你想添加几个字段怎么办?当然可以使用自定Model然后外键关联User类来实现,不过一方面关联查询的效率比直接查询效率要低,另一方面想删除系统自带用户系统的某些字段怎么办呢?

所以,自定义用户模型可以说是一种很常见的需求。这里以Django1.9为例,记录一下自定义用户模型的方法。

首先新建一个APP,我这里叫做’accounts',为了简单起见,我只定义一个包含用户名、密码、手机号、是否有效、是否是管理员的类。编写models.py文件:

# coding=utf-8

from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import (
    BaseUserManager, AbstractBaseUser
)

class MyUserManager(BaseUserManager):
    def create_user(self, username, password=None):
        """
        Creates and saves a User with the given username and password.
        """
        if not username:
            raise ValueError('Users must have an username')

        user = self.model(
            username=username,
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, password):
        """
        Creates and saves a superuser with the given username and password.
        """
        user = self.create_user(
            username,
            password=password,
        )
        user.is_admin = True
        user.save(using=self._db)
        return user

class MyUser(AbstractBaseUser):
    username = models.CharField('username',max_length=11,unique=True)
    phone = models.CharField('phone',blank=True,max_length=11)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = MyUserManager()

    USERNAME_FIELD = 'username'
    # REQUIRED_FIELDS = ['xxx']

    def get_full_name(self):
        # The user is identified by their username
        return self.username

    def get_short_name(self):
        # The user is identified by their username
        return self.username

    def __unicode__(self):
        return self.username

    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

由于不想要自带的某些字段,比如email、firstname一类的,所以继承了AbstractBaseUser,这个类只提供了password、last_login这几个基本属性。USERNAME_FIELD定义了登录的用户名是哪个,REQUIRED_FIELDS则定义了哪些是必填字段,后面的3个方法都和权限相关,为了简单这里都返回True,实际情况需要根据需求进行修改。

接下来修改admin.py,否则在管理界面是看不到自定义用户模型的:

from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField

from accounts.models import MyUser

class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

    class Meta:
        model = MyUser
        fields = ('username','phone')

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user

class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = MyUser
        fields = ('username', 'password','phone', 'is_active', 'is_admin')

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]

class UserAdmin(BaseUserAdmin):
    # The forms to add and change user instances
    form = UserChangeForm
    add_form = UserCreationForm

    # The fields to be used in displaying the User model.
    # These override the definitions on the base UserAdmin
    # that reference specific fields on auth.User.
    list_display = ('username','jy_partner', 'is_admin')
    list_filter = ('is_admin',)
    fieldsets = (
        (None, {'fields': ('username', 'password')}),
        ('Personal info', {'fields': ('phone',)}),
        ('Permissions', {'fields': ('is_admin',)}),
    )
    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('username', 'phone', 'password1', 'password2')}
        ),
    )
    search_fields = ('username',)
    ordering = ('username',)
    filter_horizontal = ()

# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)

这里我们定义了2个Form用于创建、修改用户时使用,然后把我们自定义的用户模型注册到管理页面中,由于我们使用自定义用户模型所以无法使用Django自带的权限系统,注意最后一句把组功能在管理后台移除。

关于权限部分可以看官网文档

如此自定义模型后,登录时需要手动指定后端,否则会报错:AttributeError: 'MyUser' object has no attribute 'backend'

即登陆时:

user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)

另外,django-authtools这个项目也是关于修改默认用户模型的,不过默认使用email作为用户登录名,源码写的很好值得一看。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK