1 許可權管理模型設計
1.1 建立app
新建一個app, 名稱叫system包含使用者管理、選單管理和許可權管理等系統基礎模組。
- 使用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專案使用的是自定義許可權認證模型,模型說明:
Menu: 選單管理,用來儲存系統可用的URL
Role: 角色組,通過外來鍵關聯Menu,角色組中的使用者將繼承Role關聯選單的訪問許可權
Structure:組織架構,包含單位和部門資訊
UserProfile: 自定義使用者認證模型,替換系統原有的User模型
複製程式碼
下面內容就是許可權認證的模型詳細內容,將如下內容複製到apps/system/models.py
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中新增下面內容:
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視窗輸入下面命令:
makemigrations
migrate
複製程式碼
1.4 模型(Models)相關知識點
欄位型別:
在許可權認證模型中使用到的欄位型別如下:
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='預設值')
複製程式碼
更多欄位型別和欄位選項請參考:
docs.djangoproject.com/en/1.11/ref…
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')
複製程式碼
知識點介紹:
1、檢視: Django官方文件對“檢視”的介紹是用來封裝處理使用者請求和返回響應的邏輯。
我們可以定義檢視函式,用來接受Web請求並返回Web響應,也可以使用基於類的檢視物件,本專案的檢視實現都是基於類建立的檢視,和基於函式的檢視相比據有一定的區別和優勢:
- 可以通過單獨的方法編寫與HTTP方法相關的程式碼(GET, POST等),無需通過條件分支來判斷HTTP方法
- 可將程式碼分解成可重用的元件,例如Mixin(多繼承),發揮物件導向技術優勢,使用更加靈活,易於擴充套件
2、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例項。
1、request.user.is_authenticated: 用來判斷使用者是否登入,如LoginView中:
# 當使用者訪問登陸頁面時,判斷使用者如果未登入則訪問登陸頁面,如果登入則跳轉到首頁
if not request.user.is_authenticated:
return render(request, 'system/users/login.html')
else:
return HttpResponseRedirect('/')
複製程式碼
2、is_valid(): Form實力的一個方法,用來做欄位驗證,當輸入欄位值合法時,它將返回True,同時將表單的資料存放到cleaned_data屬性中。
3、authenticate(request=None, **credentials): 用來認證使用者,credentials為關鍵字引數,預設為username和password,如果通過認證後端檢查,則返回一個User物件。
4、login(request, user, backend=None): 用來從檢視中登陸一個使用者,同時將使用者的ID儲存在session表中。注意:在呼叫login()之前必須使用authenticate()成功認證這個使用者。
5、HttpResponseRedirect[source]: 用來重定向訪問,引數是重定向的地址,可以是完整的URL,也可以相想讀與專案的絕對路徑。
2.3 建立使用者登出檢視
class LogoutView(View):
def get(self, request):
logout(request)
return HttpResponseRedirect(reverse('login'))
複製程式碼
知識點介紹:
1、logout(request): 登出使用者。
2、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前端來處理這些媒體檔案的訪問。
最新最全文件釋出在知識星球,可以通過微信搜尋公眾號“知識星球”,直接回復"52824366"獲得訪問入口
本節文件對應原始碼版本: github.com/RobbieHan/s…
非常歡迎感興趣的朋友,到我的Github或掘金上做客,閒暇之餘給個贊或Star,贈人玫瑰手留餘香
文件配套專案地址:github.com/RobbieHan/s…
輕量級辦公管理系統專案開源地址:github.com/RobbieHan/g…