環(huán)境

Python 3.5.1
django 1.9.1
今天用django寫web平臺,第一時間想到django自帶的認(rèn)證,連session都提供好了,既然有輪子了,我們就不需要自己造了。
擴(kuò)展django user的部分方法:
一、重寫user,將新的user注冊到admin,還要重寫認(rèn)證
二、繼承user,進(jìn)行擴(kuò)展(記得在settings中設(shè)置AUTH_USER_MODEL
AUTH_USER_MODEL = "myapp.NewUser"
)
2.1 繼承AbstractUser類
如果你對django自帶的User model感到滿意, 又希望增加額外的field的話, 你可以擴(kuò)展AbstractUser類(本文就是這種方法實(shí)現(xiàn))
新的django User類支持email,也可以用email作為用戶登陸
2.2 繼承AbstractBaseUser類
AbstractBaseUser中只含有3個field: password, last_login和is_active. 這個就是你自己高度定制自己需要的東西
model.py
# class UserManager(BaseUserManager):
# # def create_user(self, email, username, mobile, password=None):
# def create_user(self, email, username, mobile, password=None, **kwargs):
# """通過郵箱,密碼,手機(jī)號創(chuàng)建用戶"""
# 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'] #視頻認(rèn)證
# if kwargs.get('identicard', None): user.identicard = kwargs['identicard'] #×××認(rèn)證
# 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):
# """擴(kuò)展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
# 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方式擴(kuò)展,但是從django1.6開始就放棄這種寫法
四、網(wǎng)上找的方法,不改源碼、不加新表,擴(kuò)展user
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): #構(gòu)造器,(名字,基類,類屬性)
module = attrs.pop('__module__')
parents = [b for b 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,進(jìn)行自定義擴(kuò)展。簡單說來,當(dāng)解釋器看到你在定義一個ProfileUser類的子類,而ProfileUser類的元類是ProfileBase,所以ExtraInfo的元類也是ProfileBase,在定義ProfileUser的子類的時候,它就會執(zhí)行元類ProfileBase中的new中代碼,并且將正在定義的類的(名字,基類,類屬性)作為參數(shù)傳遞給new,這里的name就是類名ExtraInfo,attrs中則包含你新加的字段,通過User.add_to_class把新的字段加入到User中,為了能在admin中顯示出來,把它加入到UserAdmin.fieldsets中,這樣就能在后臺編輯這個這個字段,當(dāng)然,你也可以加入到ist_display,使之在列表中顯示。
如果你有其他app也想往User Model中加field或方法,都只要通過子類ProfileUser類,然后使用聲明語法進(jìn)行定義即可,所有其他工作都有元類幫你完成。這也是所有django的model的內(nèi)部工作,你可以用此方法擴(kuò)展任何model。
轉(zhuǎn)載出處:http://www.opscoder.info/extend_user.html
需求
注冊登錄都有現(xiàn)成的代碼,主要是自帶的User字段只有(email,username,password),所以需要擴(kuò)展User,來增加自己需要的字段
代碼如下:
model.py
#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內(nèi)置的兼容python2和python3的unicode語法的一個裝飾器 只是針對 __str__ 方法而用的,__str__方法是為了后臺管理(admin)和django shell的顯示,Meta類也是為后臺顯示服務(wù)的 """ class MyUser(AbstractUser): qq = models.CharField(u'qq號', max_length=16) weChat =models.CharField(u'微信賬號', max_length=100) mobile =models.CharField(u'手機(jī)號', primary_key=True, max_length=11) identicard =models.BooleanField(u'×××認(rèn)證', default=False) #默認(rèn)是0,未認(rèn)證, 1:×××認(rèn)證, 2:視頻認(rèn)證 refuserid = models.CharField(u'推薦人ID', max_length=20) Level = models.CharField(u'用戶等級', default='0', max_length=2) #默認(rèn)是0,用戶等級0-9 vevideo = models.BooleanField(u'視頻認(rèn)證', default=False) #默認(rèn)是0,未認(rèn)證。 1:已認(rèn)證 Type =models.CharField(u'用戶類型', default='0', max_length=1) #默認(rèn)是0,未認(rèn)證, 1:刷手 2:商家 def __str__(self): return self.username
settings.py
AUTH_USER_MODEL = 'appname.MyUser'
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)踩過的坑:
1、擴(kuò)展user表后,要在settings.py 添加
AUTH_USER_MODEL = 'appname.擴(kuò)展user的class name'
2、認(rèn)證后臺要在settings添加,尤其記得加逗號,否則報錯
認(rèn)證后臺不加的報錯
Django-AttributeError 'User' object has no attribute 'backend'
沒加逗號的報錯
ImportError: a doesn't look like a module path
form.py
#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='確認(rèn)密碼',widget=forms.PasswordInput())
mobile = forms.CharField(label='手機(jī)號', 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('所有項(xiàng)都為必填項(xiàng)')
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
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驗(yàn)證用戶名密碼
if user is not None: #判斷用戶是否存在
if user.is_active: #判斷用戶是否激活
login(request,user) #用戶信息驗(yàn)證成功后把登陸信息寫入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、登陸的時候用自帶的認(rèn)證模塊總是報none
user = authenticate(username=username, password=password)
查看源碼發(fā)現(xiàn)是check_password的方法是用hash進(jìn)行校驗(yàn),之前注冊的password寫法是
user.password=password
這種寫法是明文入庫,需要更改密碼的入庫寫法
user.set_password(password)
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。
文章標(biāo)題:django擴(kuò)展User-創(chuàng)新互聯(lián)
網(wǎng)頁URL:http://chinadenli.net/article40/diijho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計、外貿(mào)建站、企業(yè)建站、微信公眾號、電子商務(wù)、網(wǎng)站設(shè)計
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容