Django實戰1-權限管理功能實現-03:用戶認證

1 權限管理模型設計

1.1 建立app

新建一個app, 名稱叫system包含用戶管理、菜單管理和權限管理等系統基礎模塊。html

  • 使用pycharm打開咱們的項目,右鍵項目根目錄,選擇 New → Python Package, 在彈出的窗口輸入apps,這個包就用來存放項目中建立的全部app.
  • 選擇pycharm上方Tools,點擊Run manage.py Task..., 這時在pycharm下方會打開一個窗口,輸入startapp system 回車建立app, 以下圖:

  • 將剛剛建立的system 移動到 apps下
  • 爲了可以順利訪問到咱們新建的app,右鍵apps,選擇Mark Directory as → Sources root
  • 修改sandboxMP/sandboxMP/settings.py 加入以下內容:
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) 

1.2 建立權限認證模型

sandboxMP項目使用的是自定義權限認證模型,模型說明:python

Menu: 菜單管理,用來存儲系統可用的URL Role: 角色組,經過外鍵關聯Menu,角色組中的用戶將繼承Role關聯菜單的訪問權限 Structure:組織架構,包含單位和部門信息 UserProfile自定義用戶認證模型,替換系統原有的User模型 

下面內容就是權限認證的模型詳細內容,將以下內容複製到apps/system/models.pyweb

from django.db import models from django.contrib.auth.models import AbstractUser class Menu(models.Model): """ 菜單 """ name = models.CharField(max_length=30, unique=True, verbose_name="菜單名") # unique=True, 這個字段在表中必須有惟一值. parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="父菜單") icon = models.CharField(max_length=50, null=True, blank=True, verbose_name="圖標") code = models.CharField(max_length=50, null=True, blank=True, verbose_name="編碼") url = models.CharField(max_length=128, unique=True, null=True, blank=True) def __str__(self): return self.name class Meta: verbose_name = '菜單' verbose_name_plural = verbose_name  @classmethod def get_menu_by_request_url(cls, url): return dict(menu=Menu.objects.get(url=url)) class Role(models.Model): """ 角色:用於權限綁定 """ name = models.CharField(max_length=32, unique=True, verbose_name="角色") permissions = models.ManyToManyField("menu", blank=True, verbose_name="URL受權") desc = models.CharField(max_length=50, blank=True, null=True, verbose_name="描述") class Structure(models.Model): """ 組織架構 """ type_choices = (("unit", "單位"), ("department", "部門")) name = models.CharField(max_length=60, verbose_name="名稱") type = models.CharField(max_length=20, choices=type_choices, default="department", verbose_name="類型") parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="父類架構") class Meta: verbose_name = "組織架構" verbose_name_plural = verbose_name def __str__(self): return self.name class UserProfile(AbstractUser): name = models.CharField(max_length=20, default="", verbose_name="姓名") birthday = models.DateField(null=True, blank=True, verbose_name="出生日期") gender = models.CharField(max_length=10, choices=(("male", "男"), ("female", "女")), default="male", verbose_name="性別") mobile = models.CharField(max_length=11, default="", verbose_name="手機號碼") email = models.EmailField(max_length=50, verbose_name="郵箱") image = models.ImageField(upload_to="image/%Y/%m", default="image/default.jpg", max_length=100, null=True, blank=True) department = models.ForeignKey("Structure", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="部門") post = models.CharField(max_length=50, null=True, blank=True, verbose_name="職位") superior = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, verbose_name="上級主管") roles = models.ManyToManyField("role", verbose_name="角色", blank=True) class Meta: verbose_name = "用戶信息" verbose_name_plural = verbose_name ordering = ['id'] def __str__(self): return self.name 

1.3 使用模型

定義好模型後,還要告訴Django使用這些模型,咱們須要修改settings.py文件,在INSTALLED_APPS中添加models.py所在應用的名稱:數據庫

INSTALLED_APPS = [
     ...原內容省略... 'system', ] 

想要使用自定義的認證模型UserProfile, 還須要在setting.py中添加下面內容:django

AUTH_USER_MODEL = 'system.UserProfile' 

注意:
在定義用戶模型的時候使用到了ImageField字段類型,在執行makemigrations前須要安裝依賴包:pillow,打開CMD窗口,進入本項目的python虛擬環境,而後安裝pillow:後端

C:\Users\RobbieHan>workon sandboxMP (sandboxMP) C:\Users\RobbieHan>pip install pillow 

也能夠在pycharm 的Terminal終端窗口執行安裝命令: pip install pillow瀏覽器

最後執行makemigrations 和 migrate來生成數據表, 使用pycharm Tools,點擊Run manage.py Task..., 在manage.py窗口輸入下面命令:session

makemigrations migrate 

1.4 模型(Models)相關知識點

字段類型:架構

在權限認證模型中使用到的字段類型以下:app

CharField: 用來存儲字符串,必須制定一個參數 max_length用來限定字段最大長度 Foreignkey是一個關聯字段,建立多表之間的多對一關係,若是建立同表之間的遞歸關聯關係,可使用models.ForeignKey('self') ManyToManyField: 用來實現多對多的關聯關係 DateField日期時間字段 EmailField: email字段,用來檢查email地址是否合法 ImageField圖片字段,用來定義圖片上傳和圖片檢查,須要安裝pillow

字段選項:

unique: 設置爲True, 則表示這個字段必須有惟一值,這是從數據庫級別來強制數據惟一,後面咱們還會介紹經過form驗證來確保數據輸入的惟一 verbose_name: blank: 默認值是False, 設置爲True,則該字段容許爲空 null: 默認值是False,若是爲True,Django會在數據庫中將空值轉存爲NULL choices: 是一個可迭代結構(元祖),每一個元組中的第一個元素,是存儲在數據庫中的值;第二個元素是令人容易理解的描述。 

on_delete :在django2.0版本之前,定關聯字段時,on_delete選項並非必須的,而在django2.0版本後,在定義關聯字段時on_delete是必需要定義的,經常使用參數以下:

on_delete=models.CASCADE,     # 刪除關聯數據,與之關聯也刪除 on_delete=models.DO_NOTHING, # 刪除關聯數據,什麼也不作 on_delete=models.PROTECT, # 刪除關聯數據,引起錯誤ProtectedError on_delete=models.SET_NULL, # 刪除關聯數據,與之關聯的值設置爲null on_delete=models.SET_DEFAULT, # 刪除關聯數據,與之關聯的值設置爲默認值 

須要注意的是在使用SET_NULL的時候,該字段在模型定義的時候須要設置可爲空,例如:

user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True) 

一樣在使用SET_DEFAULT的時候,須要預先定義default:

user = models.ForeignKey(User, on_delete=models.SET_DEFAULT, default='默認值') 

更多字段類型和字段選項請參考:
https://docs.djangoproject.com/en/1.11/ref/models/fields/#model-field-types

2 用戶認證和訪問限制

用戶登陸認證的需求以下:

  • 用戶登錄系統才能夠訪問某些頁面,
  • 若是用戶沒有登錄而直接訪問就會跳轉到登錄界面,
  • 用戶在跳轉的登錄界面中完成登錄後,自動訪問跳轉到以前訪問的地址,
  • 用戶可使用用戶名、手機號碼或者其餘字段做爲登錄用戶名。

在pycharm中,選中sandboxMP/apps/system,右鍵,選擇 New → Python File, 在彈出的窗口輸入名稱:views_user,在剛建立的頁面中導入須要的模塊:

from django.shortcuts import render from django.views.generic.base import View from django.http import HttpResponseRedirect from django.contrib.auth import authenticate, login, logout from django.urls import reverse 

說明: 如下建立的視圖,都是寫在sandboxMP/apps/system/views_user.py文件中

2.1 建立index頁面視圖

index頁面視圖,是本項目建立的第一個視圖:

class IndexView(View): def get(self, request): return render(request, 'index.html') 

知識點介紹:

一、視圖: Django官方文檔對「視圖」的介紹是用來封裝處理用戶請求和返回響應的邏輯。
咱們能夠定義視圖函數,用來接受Web請求並返回Web響應,也可使用基於類的視圖對象,本項目的視圖實現都是基於類建立的視圖,和基於函數的視圖相比據有必定的區別和優點:

  • 能夠經過單獨的方法編寫與HTTP方法相關的代碼(GET, POST等),無需經過條件分支來判斷HTTP方法
  • 可將代碼分解成可重用的組件,例如Mixin(多繼承),發揮面向對象技術優點,使用更加靈活,易於擴展

二、render函數: Django的快捷函數,結合給定的模板和一個給定的上下文字典,並返回一個渲染後的HttpRespose對象,語法:render(request, template_name, context=None, content_type=None, status=None, using=None),其中 request 和template_name必須參數,其它爲可選參數。

2.2 建立用戶登錄視圖

在建立用戶登錄視圖前,先建立一個sandboxMP/apps/system/forms.py文件,用來作登錄用戶的輸入驗證,內容以下:

from django import forms


class LoginForm(forms.Form): username = forms.CharField(required=True, error_messages={"requeired": "請填寫用戶名"}) password = forms.CharField(required=True, error_messages={"requeired": "請填寫密碼"}) 

建立用戶登錄視圖:

from .forms import LoginForm class LoginView(View): def get(self, request): if not request.user.is_authenticated: return render(request, 'system/users/login.html') else: return HttpResponseRedirect('/') def post(self, request): redirect_to = request.GET.get('next', '/') login_form = LoginForm(request.POST) ret = dict(login_form=login_form) if login_form.is_valid(): user_name = request.POST['username'] pass_word = request.POST['password'] user = authenticate(username=user_name, password=pass_word) if user is not None: if user.is_active: login(request, user) return HttpResponseRedirect(redirect_to) else: ret['msg'] = '用戶未激活!' else: ret['msg'] = '用戶名或密碼錯誤!' else: ret['msg'] = '用戶和密碼不能爲空!' return render(request, 'system/users/login.html', ret) 

知識點介紹:
Django使用會話和中間件來攔截認證系統中的請求對象。它們在每個請求上提供一個request.user屬性,表示當前的用戶。若是當前的用戶沒有登入,該屬性將設置成AnonymousUser的一個實例,不然將會是User實例。
一、request.user.is_authenticated: 用來判斷用戶是否登入,如LoginView中:

# 當用戶訪問登錄頁面時,判斷用戶若是未登入則訪問登錄頁面,若是登入則跳轉到首頁 if not request.user.is_authenticated: return render(request, 'system/users/login.html') else: return HttpResponseRedirect('/') 

二、is_valid(): Forms實例的一個方法,用來作字段驗證,當輸入字段值合法時,它將返回True,同時將表單的數據存放到cleaned_data屬性中。
三、authenticate(request=None, **credentials): 用來認證用戶,credentials爲關鍵字參數,默認爲username和password,若是經過認證後端檢查,則返回一個User對象。
四、login(request, user, backend=None): 用來從視圖中登錄一個用戶,同時將用戶的ID保存在session表中。注意:在調用login()以前必須使用authenticate()成功認證這個用戶。
五、HttpResponseRedirect[source]: 用來重定向訪問,參數是重定向的地址,能夠是完整的URL,也能夠相想讀與項目的絕對路徑。

2.3 建立用戶登出視圖

class LogoutView(View): def get(self, request): logout(request) return HttpResponseRedirect(reverse('login')) 

知識點介紹:
一、logout(request): 登出用戶。
二、reverse(viewname): 根據url name來進行url的反向解析。

2.4 配置用戶URL路由

想要經過URL來訪問視圖應用,還須要配置URL路由,修改sandboxMP/sandboxMP/urls.py:

from django.contrib import admin from django.urls import path from system.views_user import IndexView, LoginView, LogoutView urlpatterns = [ path('admin/', admin.site.urls), path('', IndexView.as_view(), name='index'), path('login/', LoginView.as_view(), name='login'), path('logout/', LogoutView.as_view(), name='logout'), ] 

2.5 建立認證用戶

在pycharm選擇 Tools,點擊Run manage.py Task..., 在打開的窗口中輸入createsuperuser,根據提示輸入用戶名,郵箱和密碼,操做過程以下:

manage.py@sandboxMP > createsuperuser
"C:\Program Files\JetBrains\PyCharm2017.3.2\bin\runnerw.exe" C:\Users\RobbieHan\Envs\sandboxMP\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm2017.3.2\helpers\pycharm\django_manage.py" createsuperuser D:/ProjectFile/sandboxMP 用戶名: admin 郵箱: robbie_han@outlook.com Warning: Password input may be echoed. Password: !qaz@wsx Warning: Password input may be echoed. Password (again): !qaz@wsx Superuser created successfully. 

運行項目,訪問系統:http://127.0.0.1:8000,咱們並無登入用戶,直接能夠訪問首頁,這和咱們的要求不符。接下來實現頁面訪問限制,要求必須登入用戶才能訪問。

2.6 頁面訪問限制

頁面訪問限制的實現需求:

  • 用戶登陸系統才能夠訪問某些頁面
  • 若是用戶沒有登錄而直接訪問就會跳轉到登錄界面
  • 用戶在跳轉的登錄頁面完成登錄後,自動訪問跳轉前的訪問地址

新建sandboxMP/apps/system/mixin.py,寫入以下內容:

from django.contrib.auth.decorators import login_required


class LoginRequiredMixin(object): @classmethod def as_view(cls, **init_kwargs): view = super(LoginRequiredMixin, cls).as_view(**init_kwargs) return login_required(view) 

修改sandboxMP/sandboxMP/settings.py, 加入LOGIN_URL

LOGIN_URL = '/login/' 

須要登入用戶才能訪問的視圖,只須要繼承LoginRequiredMixin便可,修改後的IndexView視圖以下:

from .mixin import LoginRequiredMixin class IndexView(LoginRequiredMixin, View): def get(self, request): return render(request, 'index.html') 

注意:LoginRequiredMixin位於繼承列表最左側位置

重啓項目,咱們再次訪問首頁,打開瀏覽器,輸入http://127.0.0.1:8000,這時咱們會發現,瀏覽器中的URL會變成:http://127.0.0.1:8000/login/?next=/, 須要咱們先登錄後纔會跳轉到首頁。
使用咱們在2.5小節中建立的用戶:admin,密碼: !qaz@wsx登錄系統

2.7 媒體文件的訪問

儘管在建立用戶時設置了默認頭像,而且已經放置了默認頭像使用的圖片,可是用戶登陸後仍是沒法顯示頭像,因此還須要配置媒體文件的訪問。

媒體文件是由用戶上傳的文件,路徑是變化的,好比用戶上傳的頭像文件。
設置文件上傳目錄

修改sandboxMP/sandboxMP/settings.py文件,添加以下配置:

MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 

打開sandboxMP/sandboxMP/urls.py,新增以下配置:

from django.conf import settings from django.urls import re_path from django.views.static import serve if settings.DEBUG: urlpatterns += [ re_path(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}), ] 

刷新頁面就能夠看到用戶頭像了,須要注意的是,這裏之因此使用if settings.DEBUG,是由於這種配置模式應該僅限用於開發模式,在生產環境應該經過web前

相關文章
相關標籤/搜索