Django 重写用户模型的实现
Django内建的User模型可能不适合某些类型的项目。例如,在某些网站上使用邮件地址而不是用户名作为身份的标识可能更合理。
1.修改配置文件,覆盖默认的User模型
Django允许你通过修改setting.py文件中的AUTH_USER_MODEL设置覆盖默认的User模型,其值引用一个自定义的模型。
AUTH_USER_MODEL='myapp.MyUser'
上面的值表示Django应用的名称(必须位于INSTALLLED_APPS中)和你想使用的User模型的名称。
注意:
1.在创建任何迁移或者第一次运行manager.pymigrate前设置AUTH_USER_MODEL。
设置AUTH_USER_MODEL对你的数据库结构有很大的影响。它改变了一些会使用到的表格,并且会影响到一些外键和多对多关系的构造。在你有表格被创建后更改此设置是不被makemigrations支持的,并且会导致你需要手动修改数据库结构,从旧用户表中导出数据,可能重新应用一些迁移。
警告:
1.确保AUTH_USER_MODEL引用的模型在所属app中第一个迁移文件中被创建
由于Django的可交换模型的动态依赖特性的局限,你必须确保AUTH_USER_MODEL引用的模型在所属app中第一个迁移文件中被创建(通常命名为0001_initial),否则你会碰到错误。
TheeasiestwaytoconstructacompliantcustomUsermodelistoinheritfromAbstractBaseUser.AbstractBaseUserprovidesthecoreimplementationofaUsermodel,includinghashedpasswordsandtokenizedpasswordresets.Youmustthenprovidesomekeyimplementationdetails:
2.引用User模型
在AUTH_USER_MODEL设置为自定义用户模型时,如果你直接引用User(例如:通过一个外键引用它),你的代码将不能工作。你应该使用django.contrib.auth.get_user_model()来引用用户模型————指定的自定义用户模型或者User
fromdjango.contrib.authimportget_user_model User=get_user_model()
当你定义一个外键或者到用户模型的多对多关系是,你应该使用AUTH_USER_MODEL设置来指定自定义的模型。
fromdjango.confimportsettings fromdjango.dbimportmodels classArticle(models.Model): author=models.ForeignKey(settings.AUTH_USER_MODEL)
一般来说,在导入时候执行的代码中,你应该使用AUTH_USER_MODEL设置引用用户模型。get_user_model()只在Django已经导入所有的模型后才工作。
3.指定自定义的用户模型
3.1Django期望你自定义的 Usermodel满足一些最低要求:
- 模型必须有一个唯一的字段可被用于识别目的。可以是一个用户名,电子邮件地址,或任何其它独特属性。
- 定制一个UserModel最简单的方式是构造一个兼容的用户模型继承于AbstractBaseUser。
AbstractBaseUser提供了User类最核心的实现,包括哈希的passwords和标识的密码重置。
3.2下面为一些AbstractBaseUser的子类必须定义的关键的字段和方法:
USERNAME_FIELD
必须设置。设置认证标识,设置成标识的字段unique=True
classMyUser(AbstractBaseUser): identifier=models.CharField(max_length=40,unique=True) ... USERNAME_FIELD='identifier'
REQUIRED_FIELDS
必须设置。当通过createsuperuser管理命令创建一个用户时,用于提示的一个字段名称列表。
classMyUser(AbstractBaseUser): ... date_of_birth=models.DateField() height=models.FloatField() ... REQUIRED_FIELDS=['date_of_birth','height']
列表中不应该包含USERNAME_FIELD字段和password字段。
is_active
必须定义。一个布尔属性,标识用户是否是"active"的。AbstractBaseUser默认为Ture。
get_full_name()
必须定义。long格式的用户标识。
get_short_name()
必须定义。short格式的用户标识。
3.3下面为一些AbstractBaseUser的子类可以使用的方法:
get_username()
返回USERNAME_FIELD的值。
is_anonymous()
一直返回False。用来区分AnonymousUser。
is_authenticated()
一直返回Ture。用来告诉用户已被认证。
set_password(raw_password)
设置密码。按照给定的原始字符串设置用户的密码,takingcareofthepasswordhashing。不保存 AbstractBaseUser对象。如果没有给定密码,密码就会被设置成不使用,同用set_unusable_password()。
check_password(raw_password)
检查密码是否正确。给定的密码正确返回True。
set_unusable_password()
设置user无密码。不同于密码为空,如果使用check_password(),则不会返回True。不保存AbstractBaseUser对象。
has_usable_password()
如果设置了set_unusable_password(),返回False。
get_session_auth_hash()
返回密码字段的HMAC。UsedforSessioninvalidationonpasswordchange.
3.4为你的User模型自定义一个管理器
如果你的User模型定义了这些字段:username,email,is_staff,is_active,is_superuser,last_login,anddate_joined跟默认的User没什么区别,那么你还不如仅仅替换Django的UserManager就行了;总之,如果你的User定义了不同的字段,你就要去自定义一个管理器,它继承自BaseUserManager并提供两个额外的方法:
create_user(username_field,password=None,other_fields)
接受usernamefield和required字段来创建用户。例如,如果使用email作为usernamefield,date_of_birth作为requiredfield:
defcreate_user(self,email,date_of_birth,password=None): #createuserhere ...
create_superuser(username_field,password,other_fields)
接受usernamefield和required字段来创建superuser。例如,如果使用email作为usernamefield,date_of_birth作为requiredfield:
defcreate_superuser(self,email,date_of_birth,password): #createsuperuserhere ...
create_superuser中的password是必需的
4.扩展Django默认的User
如果你完全满意Django的用户模型和你只是想添加一些额外的属性信息,你只需继承django.contrib.auth.models.AbstractUser然后添加自定义的属性。AbstractUser作为一个抽象模型提供了默认的User的所有的实现(AbstractUserprovidesthefullimplementationofthedefaultUserasanabstractmodel.)。
5.自定义用户与内置身份验证表单
Django内置的forms和views和相关联的usermodel有一些先决条件。如果你的usermodel没有遵循同样的条件,则需要定义一个替代的form,通过form成为身份验证views配置的一部分。
UserCreationForm
依赖于UserModel.扩展User时必须重写。
UserChangeForm
依赖于UserModel.扩展User时必须重写。
AuthenticationForm
WorkswithanysubclassofAbstractBaseUser,andwilladapttousethefielddefinedinUSERNAME_FIELD.
PasswordResetForm
Assumesthattheusermodelhasafieldnamedemailthatcanbeusedtoidentifytheuserandabooleanfieldnamedis_activetopreventpasswordresetsforinactiveusers.
SetPasswordForm
Workswith任何AbstractBaseUser子类
PasswordChangeForm
Workswith任何AbstractBaseUser子类
AdminPasswordChangeForm
Workswith任何AbstractBaseUser子类
6.自定义用户和django.contrib.admin
如果你想让你自定义的User模型也可以在站点管理上工作,那么你的模型应该再定义一些额外的属性和方法。这些方法允许管理员去控制User到管理内容的访问:
is_staff
是否允许user访问admin界面
is_active
用户是否活跃。
has_perm(perm,obj=None):
user是否拥有perm权限。
has_module_perms(app_label):
user是否拥有app中访问models的权限
你同样也需要注册你自定义的用户模型到admin。如果你的自定义用户模型扩展于django.contrib.auth.models.AbscustomauthtractUser,你可以用django的django.contrib.auth.admin.UserAdmin类。如果你的用户模型扩展于AbstractBaseUser,你需要自定义一个ModelAdmin类。他可能继承于默认的django.contrib.auth.admin.UserAdmin。然而,你也需要覆写一些django.contrib.auth.models.AbstractUser字段的定义不在你自定义用户模型中的。
7.自定义用户和权限
如果想让在自定义用户模型中包含Django的权限控制框架变得简单,Django提供了PermissionsMixin。这是一个抽象的类,你可以为你的自定义用户模型中的类的层次结构中包含它。它提供给你所有Django权限类所必须的的方法和字段
7.1如果要定制User的权限系统,最简单的方法是继承PermissionsMixin
源码:
classPermissionsMixin(models.Model): """ Amixinclassthataddsthefieldsandmethodsnecessarytosupport Django'sGroupandPermissionmodelusingtheModelBackend. """ is_superuser=models.BooleanField(_('superuserstatus'),default=False, help_text=_('Designatesthatthisuserhasallpermissionswithout' 'explicitlyassigningthem.')) groups=models.ManyToManyField(Group,verbose_name=_('groups'), blank=True,help_text=_('Thegroupsthisuserbelongsto.Auserwill' 'getallpermissionsgrantedtoeachof' 'theirgroups.'), related_name="user_set",related_query_name="user") user_permissions=models.ManyToManyField(Permission, verbose_name=_('userpermissions'),blank=True, help_text=_('Specificpermissionsforthisuser.'), related_name="user_set",related_query_name="user") classMeta: abstract=True defget_group_permissions(self,obj=None): """ Returnsalistofpermissionstringsthatthisuserhasthroughtheir groups.Thismethodqueriesallavailableauthbackends.Ifanobject ispassedin,onlypermissionsmatchingthisobjectarereturned. """ permissions=set() forbackendinauth.get_backends(): ifhasattr(backend,"get_group_permissions"): permissions.update(backend.get_group_permissions(self,obj)) returnpermissions defget_all_permissions(self,obj=None): return_user_get_all_permissions(self,obj) defhas_perm(self,perm,obj=None): """ ReturnsTrueiftheuserhasthespecifiedpermission.Thismethod queriesallavailableauthbackends,butreturnsimmediatelyifany backendreturnsTrue.Thus,auserwhohaspermissionfromasingle authbackendisassumedtohavepermissioningeneral.Ifanobjectis provided,permissionsforthisspecificobjectarechecked. """ #Activesuperusershaveallpermissions. ifself.is_activeandself.is_superuser: returnTrue #Otherwiseweneedtocheckthebackends. return_user_has_perm(self,perm,obj) defhas_perms(self,perm_list,obj=None): """ ReturnsTrueiftheuserhaseachofthespecifiedpermissions.If objectispassed,itchecksiftheuserhasallrequiredpermsforthis object. """ forperminperm_list: ifnotself.has_perm(perm,obj): returnFalse returnTrue defhas_module_perms(self,app_label): """ ReturnsTrueiftheuserhasanypermissionsinthegivenapplabel. Usesprettymuchthesamelogicashas_perm,above. """ #Activesuperusershaveallpermissions. ifself.is_activeandself.is_superuser: returnTrue return_user_has_module_perms(self,app_label)
4.3.2Django内置的User对象就继承了AbstractBaseUser和PermissionsMixin:
源码:
classAbstractUser(AbstractBaseUser,PermissionsMixin): """ AnabstractbaseclassimplementingafullyfeaturedUsermodelwith admin-compliantpermissions. Username,passwordandemailarerequired.Otherfieldsareoptional. """ username=models.CharField(_('username'),max_length=30,unique=True, help_text=_('Required.30charactersorfewer.Letters,digitsand' '@/./+/-/_only.'), validators=[ validators.RegexValidator(r'^[\w.@+-]+$', _('Enteravalidusername.' 'Thisvaluemaycontainonlyletters,numbers' 'and@/./+/-/_characters.'),'invalid'), ], error_messages={ 'unique':_("Auserwiththatusernamealreadyexists."), }) first_name=models.CharField(_('firstname'),max_length=30,blank=True) last_name=models.CharField(_('lastname'),max_length=30,blank=True) email=models.EmailField(_('emailaddress'),blank=True) is_staff=models.BooleanField(_('staffstatus'),default=False, help_text=_('Designateswhethertheusercanlogintothisadmin' 'site.')) is_active=models.BooleanField(_('active'),default=True, help_text=_('Designateswhetherthisusershouldbetreatedas' 'active.Unselectthisinsteadofdeletingaccounts.')) date_joined=models.DateTimeField(_('datejoined'),default=timezone.now) objects=UserManager() USERNAME_FIELD='username' REQUIRED_FIELDS=['email'] classMeta: verbose_name=_('user') verbose_name_plural=_('users') abstract=True defget_full_name(self): """ Returnsthefirst_nameplusthelast_name,withaspaceinbetween. """ full_name='%s%s'%(self.first_name,self.last_name) returnfull_name.strip() defget_short_name(self): "Returnstheshortnamefortheuser." returnself.first_name defemail_user(self,subject,message,from_email=None,**kwargs): """ SendsanemailtothisUser. """ send_mail(subject,message,from_email,[self.email],**kwargs) classUser(AbstractUser): """ UserswithintheDjangoauthenticationsystemarerepresentedbythis model. Username,passwordandemailarerequired.Otherfieldsareoptional. """ classMeta(AbstractUser.Meta): swappable='AUTH_USER_MODEL'
4.3.3PermissionsMixin提供的这些方法和属性:
is_superuser
布尔类型。Designatesthatthisuserhasallpermissionswithoutexplicitlyassigningthem.
get_group_permissions(obj=None)
Returnsasetofpermissionstringsthattheuserhas,throughtheirgroups.
Ifobjispassedin,onlyreturnsthegrouppermissionsforthisspecificobject.
get_all_permissions(obj=None)
Returnsasetofpermissionstringsthattheuserhas,boththroughgroupanduserpermissions.
Ifobjispassedin,onlyreturnsthepermissionsforthisspecificobject.
has_perm(perm,obj=None)
ReturnsTrueiftheuserhasthespecifiedpermission,wherepermisintheformat"
Ifobjispassedin,thismethodwon'tcheckforapermissionforthemodel,butforthisspecificobject.
has_perms(perm_list,obj=None)
ReturnsTrueiftheuserhaseachofthespecifiedpermissions,whereeachpermisintheformat"
Ifobjispassedin,thismethodwon'tcheckforpermissionsforthemodel,butforthespecificobject.
has_module_perms(package_name)
ReturnsTrueiftheuserhasanypermissionsinthegivenpackage(theDjangoapplabel).Iftheuserisinactive,thismethodwillalwaysreturnFalse.
5.官方提供的一个完整的例子
这是一个管理器允许的自定义user这个用户模型使用邮箱地址作为用户名,并且要求填写出生年月。itprovidesnopermissionchecking,beyondasimpleadminflagontheuseraccount.Thismodelwouldbecompatiblewithallthebuilt-inauthformsandviews,exceptfortheUsercreationforms.Thisexampleillustrateshowmostofthecomponentsworktogether,butisnotintendedtobecopieddirectlyintoprojectsforproductionuse.
#models.py fromdjango.dbimportmodels fromdjango.contrib.auth.modelsimport( BaseUserManager,AbstractBaseUser ) classMyUserManager(BaseUserManager): defcreate_user(self,email,date_of_birth,password=None): """ CreatesandsavesaUserwiththegivenemail,dateof birthandpassword. """ ifnotemail: raiseValueError('Usersmusthaveanemailaddress') user=self.model( email=self.normalize_email(email), date_of_birth=date_of_birth, ) user.set_password(password) user.save(using=self._db) returnuser defcreate_superuser(self,email,date_of_birth,password): """ Createsandsavesasuperuserwiththegivenemail,dateof birthandpassword. """ user=self.create_user(email, password=password, date_of_birth=date_of_birth ) user.is_admin=True user.save(using=self._db) returnuser classMyUser(AbstractBaseUser): email=models.EmailField( verbose_name='emailaddress', 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'] defget_full_name(self): #Theuserisidentifiedbytheiremailaddress returnself.email defget_short_name(self): #Theuserisidentifiedbytheiremailaddress returnself.email def__str__(self):#__unicode__onPython2 returnself.email defhas_perm(self,perm,obj=None): "Doestheuserhaveaspecificpermission?" #Simplestpossibleanswer:Yes,always returnTrue defhas_module_perms(self,app_label): "Doestheuserhavepermissionstoviewtheapp`app_label`?" #Simplestpossibleanswer:Yes,always returnTrue @property defis_staff(self): "Istheuseramemberofstaff?" #Simplestpossibleanswer:Alladminsarestaff returnself.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中注册自定义的MyUser,还需要在app的admin.py中重写UserCreationForm和UserChangeForm:
#admin.py fromdjangoimportforms fromdjango.contribimportadmin fromdjango.contrib.auth.modelsimportGroup fromdjango.contrib.auth.adminimportUserAdmin fromdjango.contrib.auth.formsimportReadOnlyPasswordHashField fromcustomauth.modelsimportMyUser classUserCreationForm(forms.ModelForm): """Aformforcreatingnewusers.Includesalltherequired fields,plusarepeatedpassword.""" password1=forms.CharField(label='Password',widget=forms.PasswordInput) password2=forms.CharField(label='Passwordconfirmation',widget=forms.PasswordInput) classMeta: model=MyUser fields=('email','date_of_birth') defclean_password2(self): #Checkthatthetwopasswordentriesmatch password1=self.cleaned_data.get("password1") password2=self.cleaned_data.get("password2") ifpassword1andpassword2andpassword1!=password2: raiseforms.ValidationError("Passwordsdon'tmatch") returnpassword2 defsave(self,commit=True): #Savetheprovidedpasswordinhashedformat user=super(UserCreationForm,self).save(commit=False) user.set_password(self.cleaned_data["password1"]) ifcommit: user.save() returnuser classUserChangeForm(forms.ModelForm): """Aformforupdatingusers.Includesallthefieldson theuser,butreplacesthepasswordfieldwithadmin's passwordhashdisplayfield. """ password=ReadOnlyPasswordHashField() classMeta: model=MyUser fields=('email','password','date_of_birth','is_active','is_admin') defclean_password(self): #Regardlessofwhattheuserprovides,returntheinitialvalue. #Thisisdonehere,ratherthanonthefield,becausethe #fielddoesnothaveaccesstotheinitialvalue returnself.initial["password"] classMyUserAdmin(UserAdmin): #Theformstoaddandchangeuserinstances form=UserChangeForm add_form=UserCreationForm #ThefieldstobeusedindisplayingtheUsermodel. #TheseoverridethedefinitionsonthebaseUserAdmin #thatreferencespecificfieldsonauth.User. list_display=('email','date_of_birth','is_admin') list_filter=('is_admin',) fieldsets=( (None,{'fields':('email','password')}), ('Personalinfo',{'fields':('date_of_birth',)}), ('Permissions',{'fields':('is_admin',)}), ) #add_fieldsetsisnotastandardModelAdminattribute.UserAdmin #overridesget_fieldsetstousethisattributewhencreatingauser. add_fieldsets=( (None,{ 'classes':('wide',), 'fields':('email','date_of_birth','password1','password2')} ), ) search_fields=('email',) ordering=('email',) filter_horizontal=() #NowregisterthenewUserAdmin... admin.site.register(MyUser,MyUserAdmin) #...and,sincewe'renotusingDjango'sbuilt-inpermissions, #unregistertheGroupmodelfromadmin. admin.site.unregister(Group)
最后,别忘了在settings.py中定义AUTH_USER_MODEL:
AUTH_USER_MODEL='customauth.MyUser'
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。