Django自帶強(qiáng)大的User系統(tǒng),為我們提供用戶認(rèn)證、權(quán)限、組等一系列功能,可以快速建立一個完整的后臺功能。
但User模型并不能滿足我們的需求,例如自帶的User表中沒有手機(jī)號碼,而且對于國人來說表中的first_name和last_name并沒有什么卵用,對于實際生產(chǎn)中靈活的用戶表來說重寫User模型是非常有必要的。
擴(kuò)展User模型
擴(kuò)展User模型有許多的方法:
1、Proxy繼承:
代理繼承,此方法只能夠繼承User本身擁有的字段,并不能夠添加和刪改,不會影響表中原有的結(jié)構(gòu),但是可以定義一些方法來獲取我們需要的數(shù)據(jù):
from django.contrib.auth.models import User
class ProxyUser(User):
class Meta:
proxy = True # 定義代理模型
def get_data(self):
return self.objects.filter("過濾條件")
2、一對一外鍵:
如果我們對User本身的字段和驗證方法沒有要求,只是想要增加額外字段,可以通過創(chuàng)建另外一張表去關(guān)聯(lián)User表,從而添加額外字段,并且我們可以寫一個接受保存模型的信號處理方法,只要User調(diào)用了save方法,那么關(guān)聯(lián)表就會自動添加一條數(shù)據(jù)與User新添加的用戶進(jìn)行綁定:
from django.db import models
from django.contrib.auth.models import User
from django.dispatch import receiver # 導(dǎo)入receiver監(jiān)聽信號
from django.db.models.signals import post_save # 導(dǎo)入post_save信號
class ExtensionUser(object):
"""創(chuàng)建一對一模型,并添加新的字段"""
user = models.OneToOneField(User,on_delete=models.CASCADE)
telephone = models.CharField(max_length=11,verbose_name="手機(jī)號碼")
@receiver(post_save,sender=User) # 監(jiān)聽到post_save事件且發(fā)送者是User則執(zhí)行create_extension_user函數(shù)
def create_extension_user(sender,instance,created,**kwargs):
"""
sender:發(fā)送者
instance:save對象
created:是否是創(chuàng)建數(shù)據(jù)
"""
if created:
# 如果創(chuàng)建對象,ExtensionUser進(jìn)行綁定
ExtensionUser.objects.create(user=instance)
else:
# 如果不是創(chuàng)建對象,同樣將改變進(jìn)行保存
instance.extension.save()
3、繼承AbstractUser自定義模型:
Django自帶的User模型就是繼承的AbstractUser類,因此我們可以通過繼承AbractUser類自定義User模型:
from django.contrib.auth.models import BaseUserManager,AbstractUser
from shortuuidfield import ShortUUIDField # 使用shortuuid作為User表的主鍵,使數(shù)據(jù)更加的安全
class UserManager(BaseUserManager): #自定義Manager管理器
def _create_user(self,username,password,email,**kwargs):
if not username:
raise ValueError("請傳入用戶名!")
if not password:
raise ValueError("請傳入密碼!")
if not email:
raise ValueError("請傳入郵箱地址!")
user = self.model(username=username,email=email,**kwargs)
user.set_password(password)
user.save()
return user
def create_user(self,username,password,email,**kwargs): # 創(chuàng)建普通用戶
kwargs['is_superuser'] = False
return self._create_user(username,password,email,**kwargs)
def create_superuser(self,username,password,email,**kwargs): # 創(chuàng)建超級用戶
kwargs['is_superuser'] = True
kwargs['is_staff'] = True
return self._create_user(username,password,email,**kwargs)
class User(AbstractUser): # 自定義User
GENDER_TYPE = (
("1","男"),
("2","女")
)
uid = ShortUUIDField(primary_key=True)
username = models.CharField(max_length=15,verbose_name="用戶名",unique=True)
nickname = models.CharField(max_length=13,verbose_name="昵稱",null=True,blank=True)
age = models.IntegerField(verbose_name="年齡",null=True,blank=True)
gender = models.CharField(max_length=2,choices=GENDER_TYPE,verbose_name="性別",null=True,blank=True)
phone = models.CharField(max_length=11,null=True,blank=True,verbose_name="手機(jī)號碼")
email = models.EmailField(verbose_name="郵箱")
picture = models.ImageField(upload_to="Store/user_picture",verbose_name="用戶頭像",null=True,blank=True)
home_address = models.CharField(max_length=100,null=True,blank=True,verbose_name="地址")
card_id = models.CharField(max_length=30,verbose_name="身份證",null=True,blank=True)
is_active = models.BooleanField(default=True,verbose_name="激活狀態(tài)")
is_staff = models.BooleanField(default=True,verbose_name="是否是員工")
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'username' # 使用authenticate驗證時使用的驗證字段,可以換成其他字段,但驗證字段必須是唯一的,即設(shè)置了unique=True
REQUIRED_FIELDS = ['email'] # 創(chuàng)建用戶時必須填寫的字段,除了該列表里的字段還包括password字段以及USERNAME_FIELD中的字段
EMAIL_FIELD = 'email' # 發(fā)送郵件時使用的字段
objects = UserManager()
def get_full_name(self):
return self.username
def get_short_name(self):
return self.username
class Meta:
verbose_name = "用戶"
verbose_name_plural = verbose_name
自定義好User模型之后還需要在settings中設(shè)置系統(tǒng)才會識別當(dāng)前User模型作為系統(tǒng)默認(rèn)User模型,settings中需要先安裝app,然后添加AUTH_USER_MODEL=‘a(chǎn)pp.User':
之后重新python manage.py makemigrations,python manage.py migrate就可以使用該模型作為系統(tǒng)User模型了。
4、繼承AbstractBaseUser類、PermissionsMixin類自定義User模型:
繼承AbstracUser類自定義User有一個不好的地方,就是我們沒有辦法改變其已有的字段,比如first_name和last_name我們并不需要,這個時候就可以繼承AbstractBaseUser和PermissionsMixin類完全重寫User模型:
from django.contrib.auth.models import AbstractBaseUser,PermissionsMixin,BaseUserManager
from shortuuidfield import ShortUUIDField
from django.db import models
class UserManager(BaseUserManager):
def _create_user(self,username,password,email,**kwargs):
if not username:
raise ValueError("請傳入用戶名!")
if not password:
raise ValueError("請傳入密碼!")
if not email:
raise ValueError("請傳入郵箱地址!")
user = self.model(username=username,email=email,**kwargs)
user.set_password(password)
user.save()
return user
def create_user(self,username,password,email,**kwargs):
kwargs['is_superuser'] = False
return self._create_user(username,password,email,**kwargs)
def create_superuser(self,username,password,email,**kwargs):
kwargs['is_superuser'] = True
kwargs['is_staff'] = True
return self._create_user(username,password,email,**kwargs)
class User(AbstractBaseUser,PermissionsMixin): # 繼承AbstractBaseUser,PermissionsMixin
GENDER_TYPE = (
("1","男"),
("2","女")
)
uid = ShortUUIDField(primary_key=True)
username = models.CharField(max_length=15,verbose_name="用戶名",unique=True)
nickname = models.CharField(max_length=13,verbose_name="昵稱",null=True,blank=True)
age = models.IntegerField(verbose_name="年齡",null=True,blank=True)
gender = models.CharField(max_length=2,choices=GENDER_TYPE,verbose_name="性別",null=True,blank=True)
phone = models.CharField(max_length=11,null=True,blank=True,verbose_name="手機(jī)號碼")
email = models.EmailField(verbose_name="郵箱")
picture = models.ImageField(upload_to="Store/user_picture",verbose_name="用戶頭像",null=True,blank=True)
home_address = models.CharField(max_length=100,null=True,blank=True,verbose_name="地址")
card_id = models.CharField(max_length=30,verbose_name="身份證",null=True,blank=True)
is_active = models.BooleanField(default=True,verbose_name="激活狀態(tài)")
is_staff = models.BooleanField(default=True,verbose_name="是否是員工")
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
EMAIL_FIELD = 'email'
objects = UserManager()
def get_full_name(self):
return self.username
def get_short_name(self):
return self.username
class Meta:
verbose_name = "用戶"
verbose_name_plural = verbose_name
此時數(shù)據(jù)庫中只會生成我們定義好的字段。
定義好了User模型我們就可以使用Django自帶的登錄驗證和權(quán)限系統(tǒng)了。
首先注冊功能:
form.py
from django.forms import Form
from django.forms import fields
from django.core.exceptions import ValidationError
class RegisterForm(Form):
username = fields.CharField(
required=True,
min_length=3,
max_length=18,
error_messages={
"required":"用戶名不可以為空!",
"min_length":"用戶名不能低于3位!",
"max_length":"用戶名不能超過18位!"
}
)
password1 = fields.CharField(
required=True,
min_length=3,
max_length=18,
error_messages={
"required":"密碼不可以空",
"min_length": "密碼不能低于3位!",
"max_length": "密碼不能超過18位!"
}
)
password2 = fields.CharField(required=False)
email = fields.EmailField(
required=True,
error_messages={
"required":"郵箱不可以為空!"
},
)
def clean_password2(self):
if not self.errors.get("password1"):
if self.cleaned_data["password2"] != self.cleaned_data["password1"]:
raise ValidationError("您輸入的密碼不一致,請重新輸入!")
return self.cleaned_data
views.py
from django.shortcuts import render
from django.contrib.auth import get_user_model
from .forms import *
from django.http import JsonResponse
User = get_user_model() # 獲取User模型
def register(request):
if request.method == "GET":
return render(request,"register.html")
else:
form = RegisterForm(request.POST)
if form.is_valid():
username = form.cleaned_data["username"]
password = form.cleaned_data["password1"]
email = form.cleaned_data["email"]
username_exists = User.objects.filter(username=username).exists()
if username_exists:
return JsonResponse({"code":400,"message":"驗證失敗","data":{"username":"您輸入的用戶名已存在!","password1":"","password2":"","email":""}})
email_exists = User.objects.filter(email=email).exists()
if email_exists:
return JsonResponse({"code": 400, "message":"驗證失敗","data":{"username": "","password1":"","password2":"", "email": "您輸入的郵箱已存在!"}})
User.objects.create_user(username=username,password=password,email=email)
return JsonResponse({"code": 200,"message":"驗證通過", "data":{"username": "","password1":"","password2":"", "email": ""}})
else:
return JsonResponse({"code":400,"message":"驗證失敗","data":{"username":form.errors.get("username"),"password1":form.errors.get("password1"),"password2":form.errors.get("password2"),"email":form.errors.get("email")}})
登錄功能
form.py:
from django.forms import Form
from django.forms import fields
class LoginForm(Form):
username = fields.CharField(
required=True,
min_length=3,
max_length=18,
error_messages={
"required":"用戶名不可以為空!",
"min_length":"用戶名不能低于3位!",
"max_length":"用戶名不能超過18位!"
}
)
password = fields.CharField(
required=True,
error_messages={
"required":"密碼不可以空",
}
)
views.py:
from django.shortcuts import render
from .forms import *
from django.http import JsonResponse
from django.contrib.auth import authenticate
from django.contrib.auth import login
# 登錄視圖名稱不能起成login,與自帶login函數(shù)重名
def loginView(request):
if request.method == "GET":
return render(request,"login.html")
else:
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
remember = int(request.POST.get("remember"))
user = authenticate(request,username=username,password=password) # 使用authenticate進(jìn)行登錄驗證,驗證成功會返回一個user對象,失敗則返回None
# 使用authenticate驗證時如果is_active為False也會返回None,導(dǎo)致無法判斷激活狀態(tài),
# 此時可以在seetings中配置:
# AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']
if user and user.is_active: # 如果驗證成功且用戶已激活執(zhí)行下面代碼
login(request,user) # 使用自帶的login函數(shù)進(jìn)行登錄,會自動添加session信息
request.session["username"] = username # 自定義session,login函數(shù)添加的session不滿足時可以增加自定義的session信息。
if remember:
request.session.set_expiry(None) # 設(shè)置session過期時間,None表示使用系統(tǒng)默認(rèn)的過期時間
else:
request.session.set_expiry(0) # 0代表關(guān)閉瀏覽器session失效
return JsonResponse({"code": 200,"message":"驗證通過","data":{ "error":""}})
elif user and not user.is_active:
return JsonResponse({"code": 400, "message": "用戶未激活", "data": {"error": "該用戶還沒有激活,請a href='#'>激活/a>"}})
else:
return JsonResponse({"code": 400, "message": "驗證失敗", "data": {"error": "用戶名或密碼錯誤"}})
else:
return JsonResponse({"code":400,"message":"用戶名或密碼格式錯誤","data":{"error":"用戶名或密碼錯誤"}})
退出功能
from django.contrib.auth import logout
from django.shortcuts import redirect
# 視圖名不能起成logout
def logoutView(request):
logout(request) # 調(diào)用django自帶退出功能,會幫助我們刪除相關(guān)session
return redirect(request.META["HTTP_REFERER"])
此時我們就完成了通過自定義User模型實現(xiàn)注冊登錄以及退出一系列的功能,配合前端頁面就可以完美實現(xiàn)了。
用戶與權(quán)限管理
定義了用戶模型,就不可避免的涉及到了用戶權(quán)限問題,Django同樣內(nèi)置了Permission系統(tǒng),該權(quán)限系統(tǒng)都是針對表或者模型級別的,比如某個模型上的數(shù)據(jù)可以進(jìn)行增刪改查,他不能夠針對數(shù)據(jù)級別,比如某個表中的某條數(shù)據(jù)是否能夠進(jìn)行增刪改查操作(如果要實現(xiàn)數(shù)據(jù)級別的,可以考慮使用django-guardian)。
創(chuàng)建完一個模型后,針對該模型默認(rèn)有增、刪、改、查四種權(quán)限,權(quán)限存儲了數(shù)據(jù)庫中的auth_permission表中:
codename表示權(quán)限的名字,name表示權(quán)限的作用,content_type_id表示某張表的id,即用來綁定數(shù)據(jù)表的,他關(guān)聯(lián)django_content_type這張表:
添加權(quán)限的兩種方法:
#通過定義模型來添加權(quán)限:
class Address(models.Model):
"""
收貨地址
"""
recv_address = models.TextField(verbose_name = "收貨地址")
receiver = models.CharField(max_length=32,verbose_name="接收人")
recv_phone = models.CharField(max_length=32,verbose_name="收件人電話")
post_number = models.CharField(max_length=32,verbose_name="郵編")
buyer_id = models.ForeignKey(to=User,on_delete = models.CASCADE,verbose_name = "用戶id")
class Meta:
permissions = (
("view_addresses", "查看地址"),
)
# 通過代碼添加權(quán)限
from django.contrib.auth.models import Permission,ContentType
from .models import Address
content_type = ContentType.objects.get_for_model(Address)
permission = Permission.objects.create(name = "查看地址",codename = "view_addresses",content_type = content_type)
User模型和權(quán)限之間可以通過以下幾種方式來進(jìn)行管理:
1、user.user_permissions.set(permission_list):直接給定一個權(quán)限的列表。
2、user.user_permissions.add(permission,permission,...):一個個添加權(quán)限。
3、user.user_permissions.remover(permission,permission):一個個刪除權(quán)限。
4、user.user_permissions.clear():清除權(quán)限
5、user.has_perm('app_name>.'):判斷是否擁有某個權(quán)限,權(quán)限參數(shù)是一個字符串,格式是app_name.codename。
6、user.get_all_permission():獲得所有權(quán)限。
我們可以通過appname_user_user_permission這張表來查看某個用戶是否擁有某項權(quán)限:
權(quán)限限定裝飾器:
使用django.contrib.auth.decorators.permission_required可以很方便的檢查某個用戶是否擁有某項權(quán)限:
from django.contrib.auth.decorators import permission_required
@permission_required('Cart.view_cart',login_url="/login/",raise_exception=True)
# 第一個參數(shù)代表權(quán)限,login_url表示沒有登錄的話需要跳轉(zhuǎn)的頁面,raise_exception表示沒有權(quán)限是返回403錯誤,默認(rèn)是False,會跳轉(zhuǎn)到login_url指定的頁面。
def view_cart(request):
reture HttpResponse("您可以查看購物車")
分組
權(quán)限有很多,一個模型就最少有四個權(quán)限,如果一些用戶擁有相同的權(quán)限,每次都需要重復(fù)添加是很不方便的,此時分組功能就可以幫我們解決這個問題,我們可以把一些權(quán)限歸類,然后添加到某個分組中,之后再把和需要賦予這些權(quán)限的用戶添加的這個分組中,就比較方便的進(jìn)行管理了。分組我們使用的是django.contrib.auth.models.Group模型,每個用戶組擁有id和name兩個字段該模型在數(shù)據(jù)庫被映射為auth_group數(shù)據(jù)表。
分組操作:
1、Group.objects.create(group_name):創(chuàng)建分組
2、group.permission:某個分組上的權(quán)限。多堆多的關(guān)系。
group.permission.add:添加權(quán)限。
group.permission.remove:移除權(quán)限。
group.permission.clear:清除所有權(quán)限。
user.get_group_permission():獲取用戶所屬組的權(quán)限。
3、user.groups:某個用戶上的所有分組。多對多的關(guān)系。
在模板中使用權(quán)限:
在settings.TEMPLATES.OPTIONS.context_processors下,因為添加了django.contrib.auth.context_processors.auth上下文處理器,因此可以在模板中直接通過 perms來獲取用戶的所有權(quán)限:
{% if perms.View.view_cart %}
購物車詳情
{% endif %}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
您可能感興趣的文章:- Django認(rèn)證系統(tǒng)user對象實現(xiàn)過程解析
- Django用戶認(rèn)證系統(tǒng) User對象解析
- Django權(quán)限控制的使用
- Django 權(quán)限管理(permissions)與用戶組(group)詳解
- Django web自定義通用權(quán)限控制實現(xiàn)方法
- Django權(quán)限設(shè)置及驗證方式