Django 重寫用戶模型的實(shí)現(xiàn)
Django內(nèi)建的User模型可能不適合某些類型的項(xiàng)目。例如,在某些網(wǎng)站上使用郵件地址而不是用戶名作為身份的標(biāo)識(shí)可能更合理。
1.修改配置文件,覆蓋默認(rèn)的User模型
Django允許你通過修改setting.py文件中的 AUTH_USER_MODEL 設(shè)置覆蓋默認(rèn)的User模型,其值引用一個(gè)自定義的模型。
AUTH_USER_MODEL = 'myapp.MyUser'
上面的值表示Django應(yīng)用的名稱(必須位于INSTALLLED_APPS中)和你想使用的User模型的名稱。
注意:
1.在創(chuàng)建任何遷移或者第一次運(yùn)行 manager.py migrate 前設(shè)置 AUTH_USER_MODEL。
設(shè)置AUTH_USER_MODEL對(duì)你的數(shù)據(jù)庫(kù)結(jié)構(gòu)有很大的影響。它改變了一些會(huì)使用到的表格,并且會(huì)影響到一些外鍵和多對(duì)多關(guān)系的構(gòu)造。在你有表格被創(chuàng)建后更改此設(shè)置是不被 makemigrations 支持的,并且會(huì)導(dǎo)致你需要手動(dòng)修改數(shù)據(jù)庫(kù)結(jié)構(gòu),從舊用戶表中導(dǎo)出數(shù)據(jù),可能重新應(yīng)用一些遷移。
警告 :
1.確保 AUTH_USER_MODEL 引用的模型在所屬app中第一個(gè)遷移文件中被創(chuàng)建
由于Django的可交換模型的動(dòng)態(tài)依賴特性的局限,你必須確保 AUTH_USER_MODEL 引用的模型在所屬app中第一個(gè)遷移文件中被創(chuàng)建(通常命名為 0001_initial),否則你會(huì)碰到錯(cuò)誤。
The easiest way to construct a compliant custom User model is to inherit fromAbstractBaseUser. AbstractBaseUser provides the core implementation of a Usermodel, including hashed passwords and tokenized password resets. You must then provide some key implementation details:
2.引用User模型
在 AUTH_USER_MODEL 設(shè)置為自定義用戶模型時(shí),如果你直接引用User(例如:通過一個(gè)外鍵引用它),你的代碼將不能工作。你應(yīng)該使用django.contrib.auth.get_user_model()來(lái)引用用戶模型————指定的自定義用戶模型或者User
from django.contrib.auth import get_user_model User = get_user_model()
當(dāng)你定義一個(gè)外鍵或者到用戶模型的多對(duì)多關(guān)系是,你應(yīng)該使用AUTH_USER_MODEL設(shè)置來(lái)指定自定義的模型。
from django.conf import settings from django.db import models class Article(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL)
一般來(lái)說(shuō),在導(dǎo)入時(shí)候執(zhí)行的代碼中,你應(yīng)該使用 AUTH_USER_MODEL 設(shè)置引用用戶模型。get_user_model() 只在Django已經(jīng)導(dǎo)入所有的模型后才工作。
3.指定自定義的用戶模型
3.1 Django 期望你自定義的 User model 滿足一些最低要求:
- 模型必須有一個(gè)唯一的字段可被用于識(shí)別目的。可以是一個(gè)用戶名,電子郵件地址,或任何其它獨(dú)特屬性。
- 定制一個(gè)User Model最簡(jiǎn)單的方式是構(gòu)造一個(gè)兼容的用戶模型繼承于AbstractBaseUser。
AbstractBaseUser提供了User類最核心的實(shí)現(xiàn),包括哈希的passwords和 標(biāo)識(shí)的密碼重置。
3.2 下面為一些AbstractBaseUser的子類必須定義的關(guān)鍵的字段和方法:
USERNAME_FIELD
必須設(shè)置。 設(shè)置認(rèn)證標(biāo)識(shí),設(shè)置成標(biāo)識(shí)的字段 unique=True
class MyUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True) ... USERNAME_FIELD = 'identifier'
REQUIRED_FIELDS
必須設(shè)置。當(dāng)通過createsuperuser管理命令創(chuàng)建一個(gè)用戶時(shí),用于提示的一個(gè)字段名稱列表。
class MyUser(AbstractBaseUser): ... date_of_birth = models.DateField() height = models.FloatField() ... REQUIRED_FIELDS = ['date_of_birth', 'height']
列表中不應(yīng)該包含USERNAME_FIELD字段和password字段。
is_active
必須定義。 一個(gè)布爾屬性,標(biāo)識(shí)用戶是否是 "active" 的。AbstractBaseUser默認(rèn)為 Ture。
get_full_name()
必須定義。 long格式的用戶標(biāo)識(shí)。
get_short_name()
必須定義。 short格式的用戶標(biāo)識(shí)。
3.3 下面為一些AbstractBaseUser的子類可以使用的方法:
get_username()
返回 USERNAME_FIELD 的值。
is_anonymous()
一直返回 False。用來(lái)區(qū)分 AnonymousUser。
is_authenticated()
一直返回 Ture。用來(lái)告訴用戶已被認(rèn)證。
set_password(raw_password)
設(shè)置密碼。按照給定的原始字符串設(shè)置用戶的密碼,taking care of the password hashing。 不保存 AbstractBaseUser 對(duì)象。如果沒有給定密碼,密碼就會(huì)被設(shè)置成不使用,同用set_unusable_password()。
check_password(raw_password)
檢查密碼是否正確。 給定的密碼正確返回 True。
set_unusable_password()
設(shè)置user無(wú)密碼。 不同于密碼為空,如果使用 check_password(),則不會(huì)返回True。不保存AbstractBaseUser 對(duì)象。
has_usable_password()
如果設(shè)置了set_unusable_password(),返回False。
get_session_auth_hash()
返回密碼字段的HMAC。 Used for Session invalidation on password change.
3.4 為你的User模型自定義一個(gè)管理器
如果你的User模型定義了這些字段:username, email, is_staff, is_active, is_superuser, last_login, and date_joined跟默認(rèn)的User沒什么區(qū)別, 那么你還不如僅僅替換Django的UserManager就行了; 總之,如果你的User定義了不同的字段, 你就要去自定義一個(gè)管理器,它繼承自BaseUserManager并提供兩個(gè)額外的方法:
create_user(username_field, password=None, other_fields)
接受username field和required字段來(lái)創(chuàng)建用戶。例如,如果使用email作為username field, date_of_birth作為required field:
def create_user(self, email, date_of_birth, password=None): # create user here ...
create_superuser(username_field, password, other_fields)
接受username field和required字段來(lái)創(chuàng)建superuser。例如,如果使用email作為username field, date_of_birth作為required field:
def create_superuser(self, email, date_of_birth, password): # create superuser here ...
create_superuser中的password是必需的
4.擴(kuò)展Django默認(rèn)的User
如果你完全滿意Django的用戶模型和你只是想添加一些額外的屬性信息,你只需繼承 django.contrib.auth.models.AbstractUser 然后添加自定義的屬性。AbstractUser 作為一個(gè)抽象模型提供了默認(rèn)的User的所有的實(shí)現(xiàn)(AbstractUser provides the full implementation of the default User as an abstract model.)。
5.自定義用戶與內(nèi)置身份驗(yàn)證表單
Django內(nèi)置的forms和views和相關(guān)聯(lián)的user model有一些先決條件。如果你的user model沒有遵循同樣的條件,則需要定義一個(gè)替代的form,通過form成為身份驗(yàn)證views配置的一部分。
UserCreationForm
依賴于User Model. 擴(kuò)展User時(shí)必須重寫。
UserChangeForm
依賴于User Model. 擴(kuò)展User時(shí)必須重寫。
AuthenticationForm
Works with any subclass of AbstractBaseUser, and will adapt to use the field defined in USERNAME_FIELD.
PasswordResetForm
Assumes that the user model has a field named email that can be used to identify the user and a boolean field named is_active to prevent password resets for inactive users.
SetPasswordForm
Works with 任何AbstractBaseUser子類
PasswordChangeForm
Works with 任何AbstractBaseUser子類
AdminPasswordChangeForm
Works with 任何AbstractBaseUser子類
6.自定義用戶和django.contrib.admin
如果你想讓你自定義的User模型也可以在站點(diǎn)管理上工作,那么你的模型應(yīng)該再定義一些額外的屬性和方法。 這些方法允許管理員去控制User到管理內(nèi)容的訪問:
is_staff
是否允許user訪問admin界面
is_active
用戶是否活躍。
has_perm(perm, obj=None):
user是否擁有perm權(quán)限。
has_module_perms(app_label):
user是否擁有app中訪問models的權(quán)限
你同樣也需要注冊(cè)你自定義的用戶模型到admin。如果你的自定義用戶模型擴(kuò)展于django.contrib.auth.models.AbscustomauthtractUser,你可以用django的 django.contrib.auth.admin.UserAdmin 類。如果你的用戶模型擴(kuò)展于 AbstractBaseUser,你需要自定義一個(gè)ModelAdmin類。他可能繼承于默認(rèn)的django.contrib.auth.admin.UserAdmin。然而,你也需要覆寫一些django.contrib.auth.models.AbstractUser 字段的定義不在你自定義用戶模型中的。
7.自定義用戶和權(quán)限
如果想讓在自定義用戶模型中包含Django的權(quán)限控制框架變得簡(jiǎn)單,Django提供了PermissionsMixin。這是一個(gè)抽象的類,你可以為你的自定義用戶模型中的類的層次結(jié)構(gòu)中包含它。它提供給你所有Django權(quán)限類所必須的的方法和字段
7.1 如果要定制User的權(quán)限系統(tǒng),最簡(jiǎn)單的方法是繼承PermissionsMixin
源碼:
class PermissionsMixin(models.Model): """ A mixin class that adds the fields and methods necessary to support Django's Group and Permission model using the ModelBackend. """ is_superuser = models.BooleanField(_('superuser status'), default=False, help_text=_('Designates that this user has all permissions without ' 'explicitly assigning them.')) groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True, help_text=_('The groups this user belongs to. A user will ' 'get all permissions granted to each of ' 'their groups.'), related_name="user_set", related_query_name="user") user_permissions = models.ManyToManyField(Permission, verbose_name=_('user permissions'), blank=True, help_text=_('Specific permissions for this user.'), related_name="user_set", related_query_name="user") class Meta: abstract = True def get_group_permissions(self, obj=None): """ Returns a list of permission strings that this user has through their groups. This method queries all available auth backends. If an object is passed in, only permissions matching this object are returned. """ permissions = set() for backend in auth.get_backends(): if hasattr(backend, "get_group_permissions"): permissions.update(backend.get_group_permissions(self, obj)) return permissions def get_all_permissions(self, obj=None): return _user_get_all_permissions(self, obj) def has_perm(self, perm, obj=None): """ Returns True if the user has the specified permission. This method queries all available auth backends, but returns immediately if any backend returns True. Thus, a user who has permission from a single auth backend is assumed to have permission in general. If an object is provided, permissions for this specific object are checked. """ # Active superusers have all permissions. if self.is_active and self.is_superuser: return True # Otherwise we need to check the backends. return _user_has_perm(self, perm, obj) def has_perms(self, perm_list, obj=None): """ Returns True if the user has each of the specified permissions. If object is passed, it checks if the user has all required perms for this object. """ for perm in perm_list: if not self.has_perm(perm, obj): return False return True def has_module_perms(self, app_label): """ Returns True if the user has any permissions in the given app label. Uses pretty much the same logic as has_perm, above. """ # Active superusers have all permissions. if self.is_active and self.is_superuser: return True return _user_has_module_perms(self, app_label)
4.3.2 Django內(nèi)置的User對(duì)象就繼承了AbstractBaseUser和PermissionsMixin:
源碼:
class AbstractUser(AbstractBaseUser, PermissionsMixin): """ An abstract base class implementing a fully featured User model with admin-compliant permissions. Username, password and email are required. Other fields are optional. """ username = models.CharField(_('username'), max_length=30, unique=True, help_text=_('Required. 30 characters or fewer. Letters, digits and ' '@/./+/-/_ only.'), validators=[ validators.RegexValidator(r'^[\w.@+-]+$', _('Enter a valid username. ' 'This value may contain only letters, numbers ' 'and @/./+/-/_ characters.'), 'invalid'), ], error_messages={ 'unique': _("A user with that username already exists."), }) first_name = models.CharField(_('first name'), max_length=30, blank=True) last_name = models.CharField(_('last name'), max_length=30, blank=True) email = models.EmailField(_('email address'), blank=True) is_staff = models.BooleanField(_('staff status'), default=False, help_text=_('Designates whether the user can log into this admin ' 'site.')) is_active = models.BooleanField(_('active'), default=True, help_text=_('Designates whether this user should be treated as ' 'active. Unselect this instead of deleting accounts.')) date_joined = models.DateTimeField(_('date joined'), default=timezone.now) objects = UserManager() USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['email'] class Meta: verbose_name = _('user') verbose_name_plural = _('users') abstract = True def get_full_name(self): """ Returns the first_name plus the last_name, with a space in between. """ full_name = '%s %s' % (self.first_name, self.last_name) return full_name.strip() def get_short_name(self): "Returns the short name for the user." return self.first_name def email_user(self, subject, message, from_email=None, **kwargs): """ Sends an email to this User. """ send_mail(subject, message, from_email, [self.email], **kwargs) 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'
4.3.3 PermissionsMixin提供的這些方法和屬性:
is_superuser
布爾類型。 Designates that this user has all permissions without explicitly assigning them.
get_group_permissions(obj=None)
Returns a set of permission strings that the user has, through their groups.
If obj is passed in, only returns the group permissions for this specific object.
get_all_permissions(obj=None)
Returns a set of permission strings that the user has, both through group and user permissions.
If obj is passed in, only returns the permissions for this specific object.
has_perm(perm, obj=None)
Returns True if the user has the specified permission, where perm is in the format "<app label>.<permission codename>" (see permissions). If the user is inactive, this method will always return False.
If obj is passed in, this method won't check for a permission for the model, but for this specific object.
has_perms(perm_list, obj=None)
Returns True if the user has each of the specified permissions, where each perm is in the format "<app label>.<permission codename>". If the user is inactive, this method will always return False.
If obj is passed in, this method won't check for permissions for the model, but for the specific object.
has_module_perms(package_name)
Returns True if the user has any permissions in the given package (the Django app label). If the user is inactive, this method will always return False.
5.官方提供的一個(gè)完整的例子
這是一個(gè)管理器允許的自定義user這個(gè)用戶模型使用郵箱地址作為用戶名,并且要求填寫出生年月。it provides no permission checking, beyond a simple admin flag on the user account. This model would be compatible with all the built-in auth forms and views, except for the User creation forms. This example illustrates how most of the components work together, but is not intended to be copied directly into projects for production use.
# models.py 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=self.normalize_email(email), date_of_birth=date_of_birth, ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, date_of_birth, password): """ Creates and saves a superuser with the given email, date of birth and password. """ user = self.create_user(email, password=password, date_of_birth=date_of_birth ) user.is_admin = True user.save(using=self._db) return user 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 __str__(self): # __unicode__ on Python 2 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
可以看到manager定義了create_user()和create_superuser()方法,MyUser定義了USERNAME_FIELD,REQUIRED_FIELDS字段和get_full_name(),get_short_name()方法,為了能與admin一起使用,還定義了is_active,is_staff,has_perm(),has_module_perms()
要在admin中注冊(cè)自定義的MyUser,還需要在app的admin.py中重寫UserCreationForm和UserChangeForm:
# 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 from django.contrib.auth.forms import ReadOnlyPasswordHashField from customauth.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 = ('email', 'date_of_birth') 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 = ('email', 'password', 'date_of_birth', '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 MyUserAdmin(UserAdmin): # 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 = ('email', 'date_of_birth', 'is_admin') list_filter = ('is_admin',) fieldsets = ( (None, {'fields': ('email', 'password')}), ('Personal info', {'fields': ('date_of_birth',)}), ('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': ('email', 'date_of_birth', 'password1', 'password2')} ), ) search_fields = ('email',) ordering = ('email',) filter_horizontal = () # Now register the new UserAdmin... admin.site.register(MyUser, MyUserAdmin) # ... and, since we're not using Django's built-in permissions, # unregister the Group model from admin. admin.site.unregister(Group)
最后,別忘了在settings.py中定義AUTH_USER_MODEL:
AUTH_USER_MODEL = 'customauth.MyUser'
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
requests在python中發(fā)送請(qǐng)求的實(shí)例講解
在本篇文章里小編給大家整理的是一篇關(guān)于requests在python中發(fā)送請(qǐng)求的實(shí)例講解內(nèi)容,有興趣的朋友們可以測(cè)試學(xué)習(xí)下。2021-02-02Python matplotlib實(shí)現(xiàn)散點(diǎn)圖的繪制
Matplotlib作為Python的2D繪圖庫(kù),它以各種硬拷貝格式和跨平臺(tái)的交互式環(huán)境生成出版質(zhì)量級(jí)別的圖形。本文將利用Matplotlib庫(kù)繪制散點(diǎn)圖,感興趣的可以了解一下2022-03-03Python使用plt庫(kù)實(shí)現(xiàn)繪制動(dòng)態(tài)曲線圖并導(dǎo)出為GIF或MP4
這篇文章主要為大家詳細(xì)介紹了Python如何使用plt庫(kù)實(shí)現(xiàn)繪制動(dòng)態(tài)曲線圖并導(dǎo)出為GIF或MP4,文中的示例代碼講解詳細(xì),需要的可以了解一下2024-03-03python計(jì)算圓周長(zhǎng)、面積、球體體積并畫出圓
這篇文章主要介紹了python計(jì)算圓周長(zhǎng)、面積、球體體積并畫出圓(python3+PyObject+Gtk實(shí)現(xiàn)界面聯(lián)動(dòng)),需要的朋友可以參考下2014-04-04給Python的Django框架下搭建的BLOG添加RSS功能的教程
這篇文章主要介紹了給Python的Django框架下搭建的BLOG添加RSS功能的教程,示例代碼非常簡(jiǎn)單,需要的朋友可以參考下2015-04-04