新建一個app, 名稱叫system包含用戶管理、菜單管理和權限管理等系統基礎模塊。html
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
複製代碼
sandboxMP項目使用的是自定義權限認證模型,模型說明:前端
Menu: 菜單管理,用來存儲系統可用的URL
Role: 角色組,經過外鍵關聯Menu,角色組中的用戶將繼承Role關聯菜單的訪問權限
Structure:組織架構,包含單位和部門信息
UserProfile: 自定義用戶認證模型,替換系統原有的User模型
複製代碼
下面內容就是權限認證的模型詳細內容,將以下內容複製到apps/system/models.pypython
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
複製代碼
定義好模型後,還要告訴Django使用這些模型,咱們須要修改settings.py文件,在INSTALLED_APPS中添加models.py所在應用的名稱:git
INSTALLED_APPS = [
...原內容省略...
'system',
]
複製代碼
想要使用自定義的認證模型UserProfile, 還須要在setting.py中添加下面內容:github
AUTH_USER_MODEL = 'system.UserProfile'
複製代碼
注意:
在定義用戶模型的時候使用到了ImageField字段類型,在執行makemigrations前須要安裝依賴包:pillow,打開CMD窗口,進入本項目的python虛擬環境,而後安裝pillow:web
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窗口輸入下面命令:django
makemigrations
migrate
複製代碼
字段類型:
後端
在權限認證模型中使用到的字段類型以下:瀏覽器
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…
用戶登陸認證的需求以下:
在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文件中
index頁面視圖,是本項目建立的第一個視圖:
class IndexView(View):
def get(self, request):
return render(request, 'index.html')
複製代碼
知識點介紹:
一、視圖: Django官方文檔對「視圖」的介紹是用來封裝處理用戶請求和返回響應的邏輯。
咱們能夠定義視圖函數,用來接受Web請求並返回Web響應,也可使用基於類的視圖對象,本項目的視圖實現都是基於類建立的視圖,和基於函數的視圖相比據有必定的區別和優點:
二、render函數: Django的快捷函數,結合給定的模板和一個給定的上下文字典,並返回一個選而後的HttpRespose對象,語法:render(request, template_name, context=None, content_type=None, status=None, using=None),其中 request 和template_name必須參數,其它爲可選參數。
在建立用戶登錄視圖前,先建立一個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(): Form實力的一個方法,用來作字段驗證,當輸入字段值合法時,它將返回True,同時將表單的數據存放到cleaned_data屬性中。
三、authenticate(request=None, **credentials): 用來認證用戶,credentials爲關鍵字參數,默認爲username和password,若是經過認證後端檢查,則返回一個User對象。
四、login(request, user, backend=None): 用來從視圖中登錄一個用戶,同時將用戶的ID保存在session表中。注意:在調用login()以前必須使用authenticate()成功認證這個用戶。
五、HttpResponseRedirect[source]: 用來重定向訪問,參數是重定向的地址,能夠是完整的URL,也能夠相想讀與項目的絕對路徑。
class LogoutView(View):
def get(self, request):
logout(request)
return HttpResponseRedirect(reverse('login'))
複製代碼
知識點介紹:
一、logout(request): 登出用戶。
二、reverse(viewname): 根據url name來進行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'),
]
複製代碼
在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,咱們並無登入用戶,直接能夠訪問首頁,這和咱們的要求不符。接下來實現頁面訪問限制,要求必須登入用戶才能訪問。
頁面訪問限制的實現需求:
新建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登錄系統
儘管在建立用戶時設置了默認頭像,而且已經放置了默認頭像使用的圖片,可是用戶登陸後仍是沒法顯示頭像,因此還須要配置媒體文件的訪問。
媒體文件是由用戶上傳的文件,路徑是變化的,好比用戶上傳的頭像文件。
設置文件上傳目錄
修改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…