1. 程式人生 > >自定義Django使用者模型

自定義Django使用者模型

Django最方便的一點可以說就是自帶的使用者系統了,不過某些情況下自帶的使用者系統不太符合專案需求,比如你想新增幾個欄位怎麼辦?當然可以使用自定Model然後外來鍵關聯User類來實現,不過一方面關聯查詢的效率比直接查詢效率要低,另一方面想刪除系統自帶使用者系統的某些欄位怎麼辦呢?

所以,自定義使用者模型可以說是一種很常見的需求。這裡以Django1.9為例,記錄一下自定義使用者模型的方法。

首先新建一個APP,我這裡叫做’accounts’,為了簡單起見,我只定義一個包含使用者名稱、密碼、手機號、是否有效、是否是管理員的類。編寫models.py檔案:

1
2
3
4
5
6
7
8
9
10
11
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
# 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,否則在管理介面是看不到自定義使用者模型的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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'