django 扩展User

简介:

环境

Python 3.5.1

django 1.9.1

前言

今天用django写web平台,第一时间想到django自带的认证,连session都提供好了,既然有轮子了,我们就不需要自己造了。

扩展django user的部分方法:

一、重写user,将新的user注册到admin,还要重写认证

二、继承user,进行扩展(记得在settings中设置AUTH_USER_MODEL

AUTH_USER_MODEL = "myapp.NewUser"

)

2.1 继承AbstractUser类

如果你对django自带的User model感到满意, 又希望增加额外的field的话, 你可以扩展AbstractUser类(本文就是这种方法实现)

新的django User类支持email,也可以用email作为用户登陆

2.2 继承AbstractBaseUser类

AbstractBaseUser中只含有3个field: password, last_login和is_active. 这个就是你自己高度定制自己需要的东西

model.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
84
# class UserManager(BaseUserManager):
#     # def create_user(self, email, username, mobile, password=None):
#     def create_user(self, email, username, mobile, password=None, **kwargs):
#         """通过邮箱,密码,手机号创建用户"""
#         if not email:
#             raise ValueError(u'用户必须要有邮箱')
#
#         user = self.model(
#             email = self.normalize_email(email),
#             username = username,
#             mobile = mobile,
#         )
#
#         user.set_password(password)
#         if kwargs:
#             if kwargs.get('qq', None): user.qq = kwargs['qq']                                          #qq号
#             if kwargs.get('is_active', None): user.is_active = kwargs['is_active']                  #是否激活
#             if kwargs.get('wechat', None): user.wechat = kwargs['wechat']                            #微信号
#             if kwargs.get('refuserid', None): user.refuserid = kwargs['refuserid']                  #推荐人ID
#             if kwargs.get('vevideo', None): user.vevideo = kwargs['vevideo']                         #视频认证
#             if kwargs.get('identicard', None): user.identicard = kwargs['identicard']               #身份证认证
#             if kwargs.get('type', None): user.type = kwargs['type']
#         user.save(using=self._db)
#         return user
#
#     def create_superuser(self,email, username, password,mobile):
#         user = self.create_user(email,
#                             username=username,
#                             password=password,
#                             mobile = mobile,
#         )
#         user.is_admin = True
#         user.save(using=self.db)
#         return user
#
# class User(AbstractBaseUser, PermissionsMixin):
#     """扩展User"""
#     email = models.EmailField(verbose_name='Email', max_length=255, unique=True, db_index=True)
#     username = models.CharField(max_length=50)
#     qq = models.CharField(max_length=16)
#     mobile = models.CharField(max_length=11)
#     wechat = models.CharField(max_length=100)
#     refuserid = models.CharField(max_length=20)
#     vevideo = models.BooleanField(default=False)
#     identicard = models.BooleanField(default=False)
#     created_at = models.DateTimeField(auto_now_add=True)
#     type = models.CharField(u'用户类型', default='0', max_length=1)
#
#     is_active = models.BooleanField(default=True)
#     is_admin = models.BooleanField(default=False)
#
#     objects = UserManager()
#
#     USERNAME_FIELD = 'email'
#     REQUIRED_FIELDS = ['mobile']
#
#     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
#
#     #On python 2: def __unicode__(self):
#     def __str__(self):
#         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
#

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
# class UserCreationForm(forms.ModelForm):
#     password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
#     password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
#
#     class Meta:
#         model = MyUser
#         fields = ('email', 'mobile')
#
#     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):
#     password = ReadOnlyPasswordHashField()
#
#     class Meta:
#         model = MyUser
#         fields = ('email', 'password', 'mobile', 'is_active', 'is_admin')
#
#     def clean_password(self):
#         return self.initial['password']
#
# class UserAdmin(BaseUserAdmin):
#     form = UserChangeForm
#     add_form = UserCreationForm
#     list_display = ('email', 'mobile','is_admin')
#     list_filter = ('is_admin',)
#     fieldsets = (
#         (None, {'fields': ('email', 'password')}),
#         ('Personal info', {'fields': ('mobile',)}),
#         ('Permissions', {'fields': ('is_admin',)}),
#     )
#     add_fieldsets = (
#         (None, {
#             'classes': ('wide',),
#             'fields' :('email','mobile', 'password1', 'password2')}
#         ),
#     )
#     search_fields = ('email',)
#     ordering = ('email',)
#     filter_horizontal = ()
#
# admin.site.register(MyUser,UserAdmin)
# admin.site.unregister(Group)

三、profile方式扩展,但是从django1.6开始就放弃这种写法

四、网上找的方法,不改源码、不加新表,扩展user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from  django.db  import  models   from  django.contrib.auth.models  import  User   from  django.contrib.auth.admin  import  UserAdmin   import  datetime   class  ProfileBase( type ):  
     def  __new__( cls , name, bases, attrs):   #构造器,(名字,基类,类属性)
         module  =  attrs.pop( '__module__' )  
         parents  =  [b  for  in  bases  if  isinstance (b, ProfileBase)]  
         if  parents:  
             fields  =  []  
             for  obj_name, obj  in  attrs.items():  
                 if  isinstance (obj, models.Field): fields.append(obj_name)  
                 User.add_to_class(obj_name, obj)        ####最重要的步骤
             UserAdmin.fieldsets  =  list (UserAdmin.fieldsets)  
             UserAdmin.fieldsets.append((name, { 'fields' : fields}))  
         return  super (ProfileBase,  cls ).__new__( cls , name, bases, attrs)   class  ProfileUser( object ):  
     __metaclass__  =  ProfileBase   class  ExtraInfo(ProfileUser):  
     phone_number =  models.CharField(max_length  =  20 , verbose_name = u '电话号码' )

稍微解释一下这段代码: ProfileBase是自定义的一个元类,继承自types.ClassType,其中ProfileUser为一个基类,其元类为ProfileBase,而ExtraInfo才是我们真正自定义字段的类,之所以把基类ProfileUser和ExtraInfo分开,是为了便于在其他地方引用ProfileUser,进行自定义扩展。简单说来,当解释器看到你在定义一个ProfileUser类的子类,而ProfileUser类的元类是ProfileBase,所以ExtraInfo的元类也是ProfileBase,在定义ProfileUser的子类的时候,它就会执行元类ProfileBase中的new中代码,并且将正在定义的类的(名字,基类,类属性)作为参数传递给new,这里的name就是类名ExtraInfo,attrs中则包含你新加的字段,通过User.add_to_class把新的字段加入到User中,为了能在admin中显示出来,把它加入到UserAdmin.fieldsets中,这样就能在后台编辑这个这个字段,当然,你也可以加入到ist_display,使之在列表中显示。

如果你有其他app也想往User Model中加field或方法,都只要通过子类ProfileUser类,然后使用声明语法进行定义即可,所有其他工作都有元类帮你完成。这也是所有django的model的内部工作,你可以用此方法扩展任何model。

转载出处:http://www.opscoder.info/extend_user.html

需求

注册登录都有现成的代码,主要是自带的User字段只有(email,username,password),所以需要扩展User,来增加自己需要的字段


代码如下:


model.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#coding:utf8
from  django.db  import  models
from  django.contrib.auth.models  import  AbstractUser
from  django.utils.encoding  import  python_2_unicode_compatible
 
# Create your models here.
@python_2_unicode_compatible        
"""是django内置的兼容python2和python3的unicode语法的一个装饰器
只是针对 __str__ 方法而用的,__str__方法是为了后台管理(admin)和django shell的显示,Meta类也是为后台显示服务的
"""
class  MyUser(AbstractUser):
     qq  =  models.CharField(u 'qq号' , max_length = 16 )
     weChat  = models.CharField(u '微信账号' , max_length = 100 )
     mobile  = models.CharField(u '手机号' , primary_key = True , max_length = 11 )
     identicard  = models.BooleanField(u '身份证认证' , default = False )                              #默认是0,未认证, 1:身份证认证, 2:视频认证
     refuserid  =  models.CharField(u '推荐人ID' , max_length = 20 )
     Level  =  models.CharField(u '用户等级' , default = '0' , max_length = 2 )                         #默认是0,用户等级0-9
     vevideo  =  models.BooleanField(u '视频认证' , default = False )                       #默认是0,未认证。 1:已认证
     Type  = models.CharField(u '用户类型' , default = '0' , max_length = 1 )                           #默认是0,未认证, 1:刷手 2:商家
 
     def  __str__( self ):
         return  self .username


settings.py

1
2
AUTH_USER_MODEL  =  'appname.MyUser'
AUTHENTICATION_BACKENDS  =  ( 'django.contrib.auth.backends.ModelBackend' ,)

踩过的坑:

1、扩展user表后,要在settings.py 添加

1
AUTH_USER_MODEL  =  'appname.扩展user的class name'

2、认证后台要在settings添加,尤其记得加逗号,否则报错

认证后台不加的报错

1
Django-AttributeError  'User'  object has no attribute  'backend'

没加逗号的报错

1
ImportError: a doesn't  look  like a module path


form.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
#coding:utf-8
from  django  import  forms
 
#注册表单
class  RegisterForm(forms.Form):
     username  =  forms.CharField(label = '用户名' ,max_length = 100 )
     password  =  forms.CharField(label = '密码' ,widget = forms.PasswordInput())
     password2  =  forms.CharField(label = '确认密码' ,widget = forms.PasswordInput())
     mobile  =  forms.CharField(label = '手机号' , max_length = 11 )
     email  =  forms.EmailField()
     qq  =  forms.CharField(label = 'QQ号' , max_length = 16 )
     type  =  forms.ChoiceField(label = '注册类型' , choices = (( 'buyer' , '买家' ),( 'saler' , '商家' )))
 
     def  clean( self ):
         if  not  self .is_valid():
             raise  forms.ValidationError( '所有项都为必填项' )
         elif  self .cleaned_data[ 'password2' ] ! =  self .cleaned_data[ 'password' ]:
             raise  forms.ValidationError( '两次输入密码不一致' )
         else :
             cleaned_data  =  super (RegisterForm,  self ).clean()
         return  cleaned_data
 
#登陆表单
class  LoginForm(forms.Form):
     username  =  forms.CharField(label = '用户名' ,widget = forms.TextInput(attrs = { "placeholder" "用户名" "required" "required" ,}),
                                max_length = 50 , error_messages = { "required" "username不能为空" ,})
     password  =  forms.CharField(label = '密码' ,widget = forms.PasswordInput(attrs = { "placeholder" "密码" "required" "required" ,}),
                                max_length = 20 , error_messages = { "required" "password不能为空" ,})


views.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
from  django.shortcuts  import  render,render_to_response
from  .models  import  MyUser
from  django.http  import  HttpResponse,HttpResponseRedirect
from  django.template  import  RequestContext
import  time
from  .myclass  import  form
from  django.template  import  RequestContext
from  django.contrib.auth  import  authenticate,login,logout
 
#注册
def  register(request):
     error  =  []
     # if request.method == 'GET':
     #     return render_to_response('register.html',{'uf':uf})
     if  request.method  = =  'POST' :
         uf  =  form.RegisterForm(request.POST)
         if  uf.is_valid():
             username  =  uf.cleaned_data[ 'username' ]
             password  =  uf.cleaned_data[ 'password' ]
             password2  =  uf.cleaned_data[ 'password2' ]
             qq  =  uf.cleaned_data[ 'qq' ]
             email  =  uf.cleaned_data[ 'email' ]
             mobile  =  uf.cleaned_data[ 'mobile' ]
             type  =  uf.cleaned_data[ 'type' ]
             if  not  MyUser.objects. all (). filter (username = username):
                 user  =  MyUser()
                 user.username  =  username
                 user.set_password(password)
                 user.qq  =  qq
                 user.email  =  email
                 user.mobile  =  mobile
                 user. type  =  type
                 user.save()
                 return  render_to_response( 'member.html' , { 'username' : username})
     else :
         uf  =  form.RegisterForm()
     return  render_to_response( 'register.html' ,{ 'uf' :uf, 'error' :error})
  
#登陆    
def  do_login(request):
     if  request.method  = = 'POST' :
         lf  =  form.LoginForm(request.POST)
         if  lf.is_valid():
             username  =  lf.cleaned_data[ 'username' ]
             password  =  lf.cleaned_data[ 'password' ]
             user  =  authenticate(username = username, password = password)                #django自带auth验证用户名密码
             if  user  is  not  None :                                                   #判断用户是否存在
                 if  user.is_active:                                                   #判断用户是否激活
                     login(request,user)                                                  #用户信息验证成功后把登陆信息写入session
                     return  render_to_response( "member.html" , { 'username' :username})
                 else :
                     return  render_to_response( 'disable.html' ,{ 'username' :username})
             else :
                 return  HttpResponse( "无效的用户名或者密码!!!" )
     else :
         lf  =  form.LoginForm()
     return  render_to_response( 'index.html' ,{ 'lf' :lf})
     
#退出
def  do_logout(request):
     logout(request)
     return  HttpResponseRedirect( '/' )


踩过的坑:

1、登陆的时候用自带的认证模块总是报none

1
user = authenticate(username=username, password=password)

查看源码发现是check_password的方法是用hash进行校验,之前注册的password写法是

1
user.password = password

这种写法是明文入库,需要更改密码的入库写法

1
user.set_password(password)

本文转自银狐博客51CTO博客,原文链接http://blog.51cto.com/foxhound/1828166如需转载请自行联系原作者

战狐
相关文章
|
22天前
|
数据处理 数据库 开发者
Django中的自定义管理命令:扩展管理功能的途径
【4月更文挑战第15天】Django教程:介绍如何创建和使用自定义管理命令以扩展框架功能。在应用的`management/commands`目录下创建Python文件,继承`BaseCommand`,实现`handle`方法。示例代码展示了如何定义参数和执行逻辑。自定义命令适用于批量数据处理、定期任务、项目初始化和自定义迁移操作。注意文件位置、命令安全性和稳定性。自定义管理命令能提升开发和维护效率。
|
22天前
|
缓存 API 数据库
Django中的视图装饰器:扩展视图功能的利器
【4月更文挑战第15天】Django视图装饰器用于扩展视图功能,如权限验证、缓存控制和日志记录。它们是Python的高级特性,能不修改原始函数代码就添加新功能。Django提供内置装饰器,如`@login_required`(强制用户登录)、`@cache_page`(缓存视图输出)和`@csrf_protect`(CSRF保护)。同时,开发者可自定义装饰器,例如上面的`timing_decorator`用于记录视图执行时间。使用装饰器时要注意性能影响、执行顺序和参数处理。装饰器增强了代码复用性和可维护性。
|
测试技术 数据库 数据安全/隐私保护
Django 继承AbstractUser扩展用户模型
Django 继承AbstractUser扩展用户模型
108 0
|
存储 JSON 自然语言处理
Python3.7+Django2.0.4配合Mongodb打造高性能高扩展标签云存储方案
书接上回,之前有一篇文章提到了标签云系统的构建:[Python3.7+jieba(结巴分词)配合Wordcloud2.js来构造网站标签云(关键词集合)](https://v3u.cn/a_id_138),但是这篇只是浅显的说明了一下如何进行切词以及前端如何使用wordcloud2.js进行前端展示,本次主要讨论下标签分词切出来之后,如何进行存储。
Python3.7+Django2.0.4配合Mongodb打造高性能高扩展标签云存储方案
|
缓存 前端开发 数据库
【Django学习笔记 - 18】:drf请求响应简介、基类(APIView、GenericAPIView)、mixin扩展类与三级视图、视图集与路由2
【Django学习笔记 - 18】:drf请求响应简介、基类(APIView、GenericAPIView)、mixin扩展类与三级视图、视图集与路由
128 0
【Django学习笔记 - 18】:drf请求响应简介、基类(APIView、GenericAPIView)、mixin扩展类与三级视图、视图集与路由2
|
XML JSON 前端开发
【Django学习笔记 - 18】:drf请求响应简介、基类(APIView、GenericAPIView)、mixin扩展类与三级视图、视图集与路由
【Django学习笔记 - 18】:drf请求响应简介、基类(APIView、GenericAPIView)、mixin扩展类与三级视图、视图集与路由
170 0
【Django学习笔记 - 18】:drf请求响应简介、基类(APIView、GenericAPIView)、mixin扩展类与三级视图、视图集与路由
|
存储 关系型数据库 MySQL
【Django | allauth】useprofile 用户模型扩展
【Django | allauth】useprofile 用户模型扩展
【Django | allauth】useprofile 用户模型扩展
|
JavaScript Python 前端开发
Django1.11 扩展User属性增加头像上传功能
Django自带的User模型没有头像和电话这两项属性,因此需要通过扩展User达到我们想要的效果,根据官方的文档,扩展新字段到User只需要用one-to-one模型即可。
7368 0