前言
參考上篇博文,我們利用了OneToOneField的方式使用了django自帶的user,http://www.cnblogs.com/caseast/p/5909248.html , 但這么用有個問題,就是每次增刪改查數據,因為有外鍵的存在都要查詢兩次(當然可以用select_related方式減少查詢次數,參考: Django models對象的select_related方法(減少查詢次數) ),另外在admin中需要維護2張表,先創建User,再在UserProfile中進行關聯操作。本篇就來介紹一下,如何定制User和admin達到以下目的:1.擴展django自帶的User,且不通過OneToOne的方式。2.修改User中的字段,讓諸如email這種字段變為必選項(默認為可選)。3.admin表單定制,讓不同權限的用戶顯示不同的頁面。
期間踩了很多坑,統一做一次整理,admin可定制的地方很多,但是定制的方法肯定不如自己寫的后台那么靈活,需要大體了解django的User和admin的工作模式和源碼,怎么取舍還看自己的需求了。
代碼實現
擴展User的方法大概有4種,參考這個國外博客:https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html#proxy ,我用的是里邊描述的第四種方法。
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser, Group
# Create your models here.
''' OneToOne的擴展寫法,原來的寫法
class UserProfile(models.Model):
user = models.OneToOneField(User)
name = models.CharField(u'姓名', max_length=32, blank=False, null=False)
class Meta:
verbose_name = u'用戶詳情'
verbose_name_plural = u"用戶詳情"
'''
class MyUser(AbstractUser): # 繼承AbstractUser類,實際上django的User也是繼承他,我們要做的就是用自己的類代替django自己的User
name = models.CharField(u'中文名', max_length=32, blank=False, null=False)
class Meta:
verbose_name = u'用戶詳情'
verbose_name_plural = u"用戶詳情"
這里附上Django User類部分源碼
class User(AbstractUser):
"""
Users within the Django authentication system are represented by this model.
Username, password and email are required. Other fields are optional.
"""
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
光做以上這些還不夠,我們需要告訴django,我們不用你的User了,要用自己的,所以需要在settings.py里重新設定一個變量
settings.py
AUTH_USER_MODEL = "web_sso.MyUser" # 我們的app叫web_sso,這個MyUser就是models定義的那個類
看下擴展完的效果,可以看到,我們不用再像之前一樣維護“用戶”和“用戶詳情”兩張表了,但還是有很多小問題需要解決。

長話短說,直接看一下,我的admin.py改了哪些東西吧.
admin.py
from django.contrib import admin
from web_sso import models
from django.contrib.auth.admin import UserAdmin # 從django繼承過來后進行定制
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.forms import UserCreationForm, UserChangeForm # admin中涉及到的兩個表單
class User_exAdmin(admin.ModelAdmin): # 驗證碼部分展示
list_display = ('valid_code', 'valid_time', 'email')
# custom user admin
class MyUserCreationForm(UserCreationForm): # 增加用戶表單重新定義,繼承自UserCreationForm
def __init__(self, *args, **kwargs):
super(MyUserCreationForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True # 為了讓此字段在admin中為必選項,自定義一個form
self.fields['name'].required = True # 其實這個name字段可以不用設定required,因為在models中的MyUser類中已經設定了blank=False,但email字段在系統自帶User的models中已經設定為
# email = models.EmailField(_('email address'), blank=True),除非直接改源碼的django(不建議這么做),不然還是自定義一個表單做一下繼承吧。
class MyUserChangeForm(UserChangeForm): # 編輯用戶表單重新定義,繼承自UserChangeForm
def __init__(self, *args, **kwargs):
super(MyUserChangeForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
self.fields['name'].required = True
class CustomUserAdmin(UserAdmin):
def __init__(self, *args, **kwargs):
super(CustomUserAdmin, self).__init__(*args, **kwargs)
self.list_display = ('username', 'name', 'email', 'is_active', 'is_staff', 'is_superuser')
self.search_fields = ('username', 'email', 'name')
self.form = MyUserChangeForm # 編輯用戶表單,使用自定義的表單
self.add_form = MyUserCreationForm # 添加用戶表單,使用自定義的表單
# 以上的屬性都可以在django源碼的UserAdmin類中找到,我們做以覆蓋
def changelist_view(self, request, extra_context=None): # 這個方法在源碼的admin/options.py文件的ModelAdmin這個類中定義,我們要重新定義它,以達到不同權限的用戶,返回的表單內容不同
if not request.user.is_superuser: # 非super用戶不能設定編輯是否為super用戶
self.fieldsets = ((None, {'fields': ('username', 'password',)}),
(_('Personal info'), {'fields': ('name', 'email')}), # _ 將('')里的內容國際化,這樣可以讓admin里的文字自動隨着LANGUAGE_CODE切換中英文
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'groups')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
) # 這里('Permissions')中沒有'is_superuser',此字段定義UserChangeForm表單中的具體顯示內容,並可以分類顯示
self.add_fieldsets = ((None, {'classes': ('wide',),
'fields': ('username', 'name', 'password1', 'password2', 'email', 'is_active',
'is_staff', 'groups'),
}),
) #此字段定義UserCreationForm表單中的具體顯示內容
else: # super賬戶可以做任何事
self.fieldsets = ((None, {'fields': ('username', 'password',)}),
(_('Personal info'), {'fields': ('name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
self.add_fieldsets = ((None, {'classes': ('wide',),
'fields': ('username', 'name', 'password1', 'password2', 'email', 'is_active',
'is_staff', 'is_superuser', 'groups'),
}),
)
return super(CustomUserAdmin, self).changelist_view(request, extra_context)
admin.site.register(models.MyUser, CustomUserAdmin) # 注冊一下
admin.site.register(models.User_ex, User_exAdmin)
效果展示
首頁面

設定組權限管理

編輯用戶頁面
管理員:

非管理員:(沒有設定超級用戶的權限)

新增用戶頁面

通過以上,基本可以實現一個用戶管理后台的需求了。
參考資料
https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html#proxy
http://www.cnblogs.com/daliangtou/p/5435385.html
https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#django.contrib.auth.models.PermissionsMixin.has_perms
