Django自帶強大的User系統,爲咱們提供用戶認證、權限、組等一系列功能,能夠快速創建一個完整的後臺功能。 但User模型並不能知足咱們的需求,例如自帶的User表中沒有手機號碼,並且對於國人來講表中的first_name和last_name並無什麼卵用,對於實際生產中靈活的用戶表來講重寫User模型是很是有必要的。html
擴展User模型
擴展User模型有許多的方法:前端
一、Proxy繼承:
代理繼承,此方法只可以繼承User自己擁有的字段,並不可以添加和刪改,不會影響表中原有的結構,可是能夠定義一些方法來獲取咱們須要的數據:python
from django.contrib.auth.models import User class ProxyUser(User): class Meta: proxy = True # 定義代理模型 def get_data(self): return self.objects.filter("過濾條件")
二、一對一外鍵:
若是咱們對User自己的字段和驗證方法沒有要求,只是想要增長額外字段,能夠經過建立另一張表去關聯User表,從而添加額外字段,而且咱們能夠寫一個接受保存模型的信號處理方法,只要User調用了save方法,那麼關聯表就會自動添加一條數據與User新添加的用戶進行綁定:數據庫
from django.db import models from django.contrib.auth.models import User from django.dispatch import receiver # 導入receiver監聽信號 from django.db.models.signals import post_save # 導入post_save信號 class ExtensionUser(object): """建立一對一模型,並添加新的字段""" user = models.OneToOneField(User,on_delete=models.CASCADE) telephone = models.CharField(max_length=11,verbose_name="手機號碼") @receiver(post_save,sender=User) # 監聽到post_save事件且發送者是User則執行create_extension_user函數 def create_extension_user(sender,instance,created,**kwargs): """ sender:發送者 instance:save對象 created:是不是建立數據 """ if created: # 若是建立對象,ExtensionUser進行綁定 ExtensionUser.objects.create(user=instance) else: # 若是不是建立對象,一樣將改變進行保存 instance.extension.save()
三、繼承AbstractUser自定義模型:
Django自帶的User模型就是繼承的AbstractUser類,所以咱們能夠經過繼承AbractUser類自定義User模型:django
from django.contrib.auth.models import BaseUserManager,AbstractUser from shortuuidfield import ShortUUIDField # 使用shortuuid做爲User表的主鍵,使數據更加的安全 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): # 建立普通用戶 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(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="手機號碼") 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="激活狀態") is_staff = models.BooleanField(default=True,verbose_name="是不是員工") date_joined = models.DateTimeField(auto_now_add=True) USERNAME_FIELD = 'username' # 使用authenticate驗證時使用的驗證字段,能夠換成其餘字段,但驗證字段必須是惟一的,即設置了unique=True REQUIRED_FIELDS = ['email'] # 建立用戶時必須填寫的字段,除了該列表裏的字段還包括password字段以及USERNAME_FIELD中的字段 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
自定義好User模型以後還須要在settings中設置系統纔會識別當前User模型做爲系統默認User模型,settings中須要先安裝app,而後添加AUTH_USER_MODEL=‘app.User’:瀏覽器
以後從新python manage.py makemigrations,python manage.py migrate就可使用該模型做爲系統User模型了。安全
四、繼承AbstractBaseUser類、PermissionsMixin類自定義User模型:
繼承AbstracUser類自定義User有一個很差的地方,就是咱們沒有辦法改變其已有的字段,好比first_name和last_name咱們並不須要,這個時候就能夠繼承AbstractBaseUser和PermissionsMixin類徹底重寫User模型:session
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="手機號碼") 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="激活狀態") 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
此時數據庫中只會生成咱們定義好的字段。 定義好了User模型咱們就可使用Django自帶的登陸驗證和權限系統了。 首先註冊功能: form.pyapp
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函數重名 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進行登陸驗證,驗證成功會返回一個user對象,失敗則返回None # 使用authenticate驗證時若是is_active爲False也會返回None,致使沒法判斷激活狀態, # 此時能夠在seetings中配置: # AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend'] if user and user.is_active: # 若是驗證成功且用戶已激活執行下面代碼 login(request,user) # 使用自帶的login函數進行登陸,會自動添加session信息 request.session["username"] = username # 自定義session,login函數添加的session不知足時能夠增長自定義的session信息。 if remember: request.session.set_expiry(None) # 設置session過時時間,None表示使用系統默認的過時時間 else: request.session.set_expiry(0) # 0表明關閉瀏覽器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) # 調用django自帶退出功能,會幫助咱們刪除相關session return redirect(request.META["HTTP_REFERER"])
此時咱們就完成了經過自定義User模型實現註冊登陸以及退出一系列的功能,配合前端頁面就能夠完美實現了。
用戶與權限管理
定義了用戶模型,就不可避免的涉及到了用戶權限問題,Django一樣內置了Permission系統,該權限系統都是針對表或者模型級別的,好比某個模型上的數據能夠進行增刪改查,他不可以針對數據級別,好比某個表中的某條數據是否可以進行增刪改查操做(若是要實現數據級別的,能夠考慮使用django-guardian)。建立完一個模型後,針對該模型默認有增、刪、改、查四種權限,權限存儲了數據庫中的auth_permission表中:
codename表示權限的名字,name表示權限的做用,content_type_id表示某張表的id,即用來綁定數據表的,他關聯django_content_type這張表:
添加權限的兩種方法:
#經過定義模型來添加權限: 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", "查看地址"), ) # 經過代碼添加權限 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模型和權限之間能夠經過如下幾種方式來進行管理: 一、user.user_permissions.set(permission_list):直接給定一個權限的列表。 二、user.user_permissions.add(permission,permission,...):一個個添加權限。 三、user.user_permissions.remover(permission,permission):一個個刪除權限。 四、user.user_permissions.clear():清除權限 五、user.has_perm('<app_name>.<codename>'):判斷是否擁有某個權限,權限參數是一個字符串,格式是app_name.codename。 六、user.get_all_permission():得到全部權限。 咱們能夠經過appname_user_user_permission這張表來查看某個用戶是否擁有某項權限:
權限限定裝飾器: 使用django.contrib.auth.decorators.permission_required能夠很方便的檢查某個用戶是否擁有某項權限:
from django.contrib.auth.decorators import permission_required @permission_required('Cart.view_cart',login_url="/login/",raise_exception=True) # 第一個參數表明權限,login_url表示沒有登陸的話須要跳轉的頁面,raise_exception表示沒有權限是返回403錯誤,默認是False,會跳轉到login_url指定的頁面。 def view_cart(request): reture HttpResponse("您能夠查看購物車")
分組 權限有不少,一個模型就最少有四個權限,若是一些用戶擁有相同的權限,每次都須要重複添加是很不方便的,此時分組功能就能夠幫咱們解決這個問題,咱們能夠把一些權限歸類,而後添加到某個分組中,以後再把和須要賦予這些權限的用戶添加的這個分組中,就比較方便的進行管理了。分組咱們使用的是django.contrib.auth.models.Group模型,每一個用戶組擁有id和name兩個字段該模型在數據庫被映射爲auth_group數據表。 分組操做: 一、Group.objects.create(group_name):建立分組 二、group.permission:某個分組上的權限。多堆多的關係。
group.permission.add:添加權限。 group.permission.remove:移除權限。 group.permission.clear:清除全部權限。 user.get_group_permission():獲取用戶所屬組的權限。 三、user.groups:某個用戶上的全部分組。多對多的關係。 在模板中使用權限: 在settings.TEMPLATES.OPTIONS.context_processors下,由於添加了django.contrib.auth.context_processors.auth上下文處理器,所以能夠在模板中直接經過 perms來獲取用戶的全部權限:
{% if perms.View.view_cart %} 購物車詳情 {% endif %}
原文連接:https://blog.csdn.net/weixin_44951273/article/details/101028522