python 全棧開發,Day108(客戶管理之權限控制,客戶管理之動態"一級"菜單,其餘應用使用rbac組件,django static文件的引入方式)

1、客戶管理之權限控制

昨天的做業,有不少不完善的地方css

下載代碼,基本實現權限驗證html

https://github.com/987334176/luffy_permission/archive/v1.2.zip前端

必須下載此代碼,不然下面的不用看了!!!python

補充說明:jquery

admin後臺

查看rbac目錄下的admin.py,看下面幾行代碼git

class PermissionAdmin(admin.ModelAdmin):
    list_display = ['title','url']  # 顯示的字段
    list_editable = ['url']  # 容許編輯

admin.site.register(models.Permission,PermissionAdmin)

它能實現下面的效果:github

url能夠直接編輯。注意url的最前面,必須包含"/",爲何呢?後面作菜單的時候,會用到!web

 

session問題

查看web目錄-->views目錄下的account.py,看這一行數據庫

permission_list = obj.roles.filter(permissions__url__isnull=False).values('permissions__url').distinct()

有些用戶,是沒有角色的。須要使用permissions__url__isnull=False   過濾掉爲空的記錄apache

因爲權限判斷,只須要url。因此取url字段,也就是 values('permissions__url')

因爲用戶可能有多個角色,那麼url必然有重複的。使用 distinct()   作去重!

 

在django中session存儲時,默認對數據作序列化。因此數據不能是queryset對象,使用list進行強制轉換!

權限在不少地方會用到,爲了不key放生變更,致使處處代碼改動。將key名放在settings.py中。

 

正則匹配

新建一個11.py文件,內容以下:

current_url = '/customer/edit/1/'

reg = "^/customer/edit/(\d+)/$"


import re
result = re.match(reg,current_url)
print(result)
View Code

執行輸出:

<_sre.SRE_Match object; span=(0, 17), match='/customer/edit/1/'>

輸入一個不匹配的

current_url = '/customer/edit/1/dfasfsda'
reg = "^/customer/edit/(\d+)/$"

import re
result = re.match(reg,current_url)
print(result)
View Code

執行輸出:None

 

注意:這裏必須使用match,它表示從頭至尾匹配。不能使用search和findall

search表示有多個結果時,只輸出第一個!

 

中間件權限校驗

查看web目錄-->middleware目錄-->rbac.py,看這一行代碼

reg = "^%s$" % item.get('permissions__url')

它表示爲每個url增長開始符號"^"和結束符號"$"

這樣,就能精確匹配了!

對於白名單,也就是不須要進行權限驗證的。寫在settings.py中,方便擴展!

 

2、客戶管理之動態"一級"菜單

上述過程當中的菜單是在程序中定義好的,沒法根據用戶權限進行動態配置。

那麼,接下來咱們來完成一個 「單級菜單」的功能:

表設計

看下面的url,有些能夠成爲菜單,有些不能!

上面紅色方框的,就是菜單。其餘的,好比帶參數的。它是動態連接,所以不能成爲菜單!

那麼如何區分呢?給權限表加2個字段,一個表示是否可作菜單,一個是菜單的圖標。

 

進入目錄rbac,修改models.py

from django.db import models


class Permission(models.Model):
    """
    權限表
    """
    title = models.CharField(verbose_name='標題', max_length=32)
    url = models.CharField(verbose_name='含正則的URL', max_length=128)

    is_menu = models.BooleanField(verbose_name='是否可作菜單', default=False)
    icon = models.CharField(verbose_name='菜單圖標',max_length=32, null=True, blank=True)

    def __str__(self):
        return self.title


class Role(models.Model):
    """
    角色
    """
    title = models.CharField(verbose_name='角色名稱', max_length=32)
    permissions = models.ManyToManyField(verbose_name='擁有的全部權限', to='Permission', blank=True)

    def __str__(self):
        return self.title


class UserInfo(models.Model):
    """
    用戶表
    """
    name = models.CharField(verbose_name='用戶名', max_length=32)
    password = models.CharField(verbose_name='密碼', max_length=64)
    email = models.CharField(verbose_name='郵箱', max_length=32)
    roles = models.ManyToManyField(verbose_name='擁有的全部角色', to='Role', blank=True)

    def __str__(self):
        return self.name
View Code

執行2個命令,生成字段

python manage.py makemigrations
python manage.py migrate

 

font awesome

 一套絕佳的圖標字體庫和CSS框架,Font Awesome爲您提供可縮放的矢量圖標,您可使用CSS所提供的全部特性對它們進行更改,包括:大小、顏色、陰影或者其它任何支持的效果。

官方網址:

http://fontawesome.dashgame.com/

使用方式

將如下代碼粘貼到網頁HTML代碼的 <head> 部分

<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

基本圖標

您能夠將Font Awesome圖標使用在幾乎任何地方,只須要使用CSS前綴 fa ,再加上圖標名稱。 Font Awesome是爲使用內聯元素而設計的。咱們一般更喜歡使用 <i> ,由於它更簡潔。 但實際上使用 <span> 才能更加語義化。

<i class="fa fa-camera-retro"></i> fa-camera-retro

搜索圖標

官方沒有提供圖標搜索功能,這裏提供一個網站,能夠搜索你想要的圖標

https://www.thinkcmf.com/font/search.html

 

錄入數據

還有一個

 

 

修改orm

修改web目錄-->views-->account.py

from django.shortcuts import render, redirect, HttpResponse
from rbac import models
from django.conf import settings


def login(request):
    """
    用戶登錄
    :param request:
    :return:
    """
    if request.method == 'GET':
        return render(request, 'login.html')

    # 1. 獲取提交的用戶名和密碼
    user = request.POST.get('user')
    pwd = request.POST.get('pwd')

    # 2. 檢驗用戶是否合法
    obj = models.UserInfo.objects.filter(name=user, password=pwd).first()
    if not obj:
        return render(request, 'login.html', {'msg': '用戶名或密碼錯誤'})

    # 3. 獲取用戶信息和權限信息寫入session
    permission_queryset = obj.roles.filter(permissions__url__isnull=False).values('permissions__url',
                                                                                   'permissions__is_menu',
                                                                                   'permissions__title',
                                                                                   'permissions__icon',
                                                                                   ).distinct()

    menu_list = []  # 菜單列表
    permission_list = []  # 權限列表

    for row in permission_queryset:
        permission_list.append({'permissions__url': row['permissions__url']})

        if row['permissions__is_menu']:
            menu_list.append(
                {'title': row['permissions__title'], 'icon': row['permissions__icon'],
                 'url': row['permissions__url']})

    request.session[settings.PERMISSION_SESSION_KEY] = permission_list
    request.session[settings.MENU_SESSION_KEY] = menu_list

    return redirect('/customer/list/')
View Code

修改settings.py,增長變量

########################## 權限相關 #######################
PERMISSION_SESSION_KEY = "permission_list"
MENU_SESSION_KEY = "menu_list"
VALID_URL = [
    '^/login/$',
    '^/admin/.*',
]
View Code

經過這樣,權限和菜單,就分開了。各不影響!

 

菜單展現

菜單在是web目錄-->templates目錄-->layout.html裏面的

對於每一個用戶展現不一樣的菜單,須要分離出來。須要使用自定義標籤來渲染!

自定義標籤

在web目錄下面建立templatetags文件夾,在此文件夾中建立rbac.py

from django.template import Library
from django.conf import settings
import re
register = Library()


@register.inclusion_tag('menu.html')
def menu(request):
    """
    生成菜單
    :param request:
    :return:
    """

    # 獲取session中的菜單列表
    menu_list = request.session.get(settings.MENU_SESSION_KEY)

    for item in menu_list:
        # 每個url增長^$,好比/customer/list/變成^/customer/list/$
        reg = "^%s$" % item['url']
        if re.match(reg,request.path_info):  # 判斷當前路徑是否匹配
            # 增長一個樣式,class爲action。表示選中狀態
            item['class'] = 'active'

    return {'menu_list':menu_list}  # 變量傳給模板
View Code

在web目錄-->templates目錄下,建立menu.html

 <div class="static-menu">
    {% for item in menu_list %}
    <a href="{{ item.url }}" class="{{ item.class }}">
        <span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span> {{ item.title }}</a>
    {% endfor %}
</div>
View Code

修改web目錄-->templates目錄-->layout.html,使用自定義標籤

{% load staticfiles %}
{% load rbac %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>路飛學城</title>
    <link rel="shortcut icon" href="{% static 'imgs/luffy-study-logo.png' %} ">
    <link rel="stylesheet" href="{% static 'plugins/bootstrap/css/bootstrap.css' %} "/>
    <link rel="stylesheet" href="{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
    <link rel="stylesheet" href="{% static 'css/commons.css' %} "/>
    <link rel="stylesheet" href="{% static 'css/nav.css' %} "/>
    <style>
        body {
            margin: 0;
        }

        .no-radius {
            border-radius: 0;
        }

        .no-margin {
            margin: 0;
        }

        .pg-body > .left-menu {
            background-color: #EAEDF1;
            position: absolute;
            left: 1px;
            top: 48px;
            bottom: 0;
            width: 220px;
            border: 1px solid #EAEDF1;
            overflow: auto;
        }

        .pg-body > .right-body {
            position: absolute;
            left: 225px;
            right: 0;
            top: 48px;
            bottom: 0;
            overflow: scroll;
            border: 1px solid #ddd;
            border-top: 0;
            font-size: 13px;
            min-width: 755px;
        }

        .navbar-right {
            float: right !important;
            margin-right: -15px;
        }

        .luffy-container {
            padding: 15px;
        }

        .left-menu .menu-body .static-menu {

        }

        .left-menu .menu-body .static-menu .icon-wrap {
            width: 20px;
            display: inline-block;
            text-align: center;
        }

        .left-menu .menu-body .static-menu a {
            text-decoration: none;
            padding: 8px 15px;
            border-bottom: 1px solid #ccc;
            color: #333;
            display: block;
            background: #efefef;
            background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
            background: -ms-linear-gradient(bottom, #efefef, #fafafa);
            background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%);
            background: -o-linear-gradient(bottom, #efefef, #fafafa);
            filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
            -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
            box-shadow: inset 0px 1px 1px white;
        }

        .left-menu .menu-body .static-menu a:hover {
            color: #2F72AB;
            border-left: 2px solid #2F72AB;
        }

        .left-menu .menu-body .static-menu a.active {
            color: #2F72AB;
            border-left: 2px solid #2F72AB;
        }
    </style>
</head>
<body>

<div class="pg-header">
    <div class="nav">
        <div class="logo-area left">
            <a href="#">
                <img class="logo" src="{% static 'imgs/logo.svg' %}">
                <span style="font-size: 18px;">路飛學城 </span>
            </a>
        </div>

        <div class="left-menu left">
            <a class="menu-item">資產管理</a>
            <a class="menu-item">用戶信息</a>
            <a class="menu-item">路飛管理</a>
            <div class="menu-item">
                <span>使用說明</span>
                <i class="fa fa-caret-down" aria-hidden="true"></i>
                <div class="more-info">
                    <a href="#" class="more-item">管他什麼菜單</a>
                    <a href="#" class="more-item">實在是編不了</a>
                </div>
            </div>
        </div>

        <div class="right-menu right clearfix">

            <div class="user-info right">
                <a href="#" class="avatar">
                    <img class="img-circle" src="{% static 'imgs/default.png' %}">
                </a>

                <div class="more-info">
                    <a href="#" class="more-item">我的信息</a>
                    <a href="#" class="more-item">註銷</a>
                </div>
            </div>

            <a class="user-menu right">
                消息
                <i class="fa fa-commenting-o" aria-hidden="true"></i>
                <span class="badge bg-success">2</span>
            </a>

            <a class="user-menu right">
                通知
                <i class="fa fa-envelope-o" aria-hidden="true"></i>
                <span class="badge bg-success">2</span>
            </a>

            <a class="user-menu right">
                任務
                <i class="fa fa-bell-o" aria-hidden="true"></i>
                <span class="badge bg-danger">4</span>
            </a>
        </div>

    </div>
</div>
<div class="pg-body">
    <div class="left-menu">
        <div class="menu-body">
            {% menu request %}

        </div>
    </div>
    <div class="right-body">
        <div>
            <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">

                <li><a href="#">首頁</a></li>
                <li class="active">客戶管理</li>

            </ol>
        </div>
        {% block content %} {% endblock %}
    </div>
</div>


<script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
<script src="{% static 'plugins/bootstrap/js/bootstrap.js' %} "></script>
{% block js %} {% endblock %}
</body>
</html>
View Code

 

使用徹底權限的用戶,登陸頁面,效果以下:

使用非徹底權限的用戶登陸

效果以下:

經過這樣,就能夠不一樣的用戶登陸,動態的展現菜單!

 

解耦代碼

那麼問題來了,在web應用中。設計到權限的,都在此目錄中。這樣是不合理的,得須要把相關代碼移植到rbac應用中!

將web目錄下的middleware文件夾,剪貼到rbac目錄中

修改settings.py,修改中間件的路徑

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'rbac.middleware.rbac.RbacMiddleware',
]
View Code

在rbac目錄下,建立文件夾service,在此路徑下建立文件init_permission.py,表示權限初始化

from django.conf import settings


def init_permission(request,user):
    """
    權限和菜單信息初始化,之後使用時,須要在登錄成功後調用該方法將權限和菜單信息放入session
    :param request:
    :param user:
    :return:
    """

    # 3. 獲取用戶信息和權限信息寫入session
    permission_queryset = user.roles.filter(permissions__url__isnull=False).values('permissions__url',
                                                                                  'permissions__is_menu',
                                                                                  'permissions__title',
                                                                                  'permissions__icon',
                                                                                  ).distinct()


    menu_list = []
    permission_list = []

    for row in permission_queryset:
        permission_list.append({'permissions__url': row['permissions__url']})

        if row['permissions__is_menu']:
            menu_list.append(
                {'title': row['permissions__title'], 'icon': row['permissions__icon'], 'url': row['permissions__url']})

    request.session[settings.PERMISSION_SESSION_KEY] = permission_list
    request.session[settings.MENU_SESSION_KEY] = menu_list
View Code

修改web目錄-->views目錄-->account.py

from django.shortcuts import render, redirect, HttpResponse
from rbac import models
from rbac.service.init_permission import init_permission
from django.conf import settings


def login(request):
    """
    用戶登錄
    :param request:
    :return:
    """
    if request.method == 'GET':
        return render(request, 'login.html')

    # 1. 獲取提交的用戶名和密碼
    user = request.POST.get('user')
    pwd = request.POST.get('pwd')

    # 2. 檢驗用戶是否合法
    obj = models.UserInfo.objects.filter(name=user, password=pwd).first()
    if not obj:
        return render(request, 'login.html', {'msg': '用戶名或密碼錯誤'})

    # 寫入session
    request.session['user_info'] = {'id': obj.id, 'name': obj.name}
    # 初始化權限,並寫入session
    init_permission(request, obj)
    
    return redirect('/customer/list/')
View Code

在rbac目錄-->建立templates-->建立rbac文件夾,避免文件衝突。將web目錄-->templates裏面的menu.html剪切過來

將web目錄下的templatetags文件夾剪貼到rbac目錄下

修改rbac目錄-->templatetags-->rbac.py,更改模板路徑

from django.template import Library
from django.conf import settings
import re
register = Library()


@register.inclusion_tag('rbac/menu.html')
def menu(request):
    """
    生成菜單
    :param request:
    :return:
    """

    # 獲取session中的菜單列表
    menu_list = request.session.get(settings.MENU_SESSION_KEY)

    for item in menu_list:
        # 每個url增長^$,好比/customer/list/變成^/customer/list/$
        reg = "^%s$" % item['url']
        if re.match(reg,request.path_info):  # 判斷當前路徑是否匹配
            # 增長一個樣式,class爲action。表示選中狀態
            item['class'] = 'active'

    return {'menu_list':menu_list}  # 變量傳給模板
View Code

在rbac目錄-->建立目錄static-->建立目錄rbac,在此文件下建立文件rbac.css

.static-menu {

}

.static-menu .icon-wrap {
    width: 20px;
    display: inline-block;
    text-align: center;
}

.static-menu a {
    text-decoration: none;
    padding: 8px 15px;
    border-bottom: 1px solid #ccc;
    color: #333;
    display: block;
    background: #efefef;
    background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
    background: -ms-linear-gradient(bottom, #efefef, #fafafa);
    background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%);
    background: -o-linear-gradient(bottom, #efefef, #fafafa);
    filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
    box-shadow: inset 0px 1px 1px white;
}

.static-menu a:hover {
    color: #2F72AB;
    border-left: 2px solid #2F72AB;
}

.static-menu a.active {
    color: #2F72AB;
    border-left: 2px solid #2F72AB;
}
View Code

修改web目錄-->templates-->layout.html。刪除多餘的css文件

{% load staticfiles %}
{% load rbac %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>路飛學城</title>
    <link rel="shortcut icon" href="{% static 'imgs/luffy-study-logo.png' %} ">
    <link rel="stylesheet" href="{% static 'plugins/bootstrap/css/bootstrap.css' %} "/>
    <link rel="stylesheet" href="{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
    <link rel="stylesheet" href="{% static 'css/commons.css' %} "/>
    <link rel="stylesheet" href="{% static 'css/nav.css' %} "/>
    <link rel="stylesheet" href="{% static 'rbac/rbac.css' %} "/>
    <style>
        body {
            margin: 0;
        }

        .no-radius {
            border-radius: 0;
        }

        .no-margin {
            margin: 0;
        }

        .pg-body > .left-menu {
            background-color: #EAEDF1;
            position: absolute;
            left: 1px;
            top: 48px;
            bottom: 0;
            width: 220px;
            border: 1px solid #EAEDF1;
            overflow: auto;
        }

        .pg-body > .right-body {
            position: absolute;
            left: 225px;
            right: 0;
            top: 48px;
            bottom: 0;
            overflow: scroll;
            border: 1px solid #ddd;
            border-top: 0;
            font-size: 13px;
            min-width: 755px;
        }

        .navbar-right {
            float: right !important;
            margin-right: -15px;
        }

        .luffy-container {
            padding: 15px;
        }


    </style>
</head>
<body>

<div class="pg-header">
    <div class="nav">
        <div class="logo-area left">
            <a href="#">
                <img class="logo" src="{% static 'imgs/logo.svg' %}">
                <span style="font-size: 18px;">路飛學城 </span>
            </a>
        </div>

        <div class="left-menu left">
            <a class="menu-item">資產管理</a>
            <a class="menu-item">用戶信息</a>
            <a class="menu-item">路飛管理</a>
            <div class="menu-item">
                <span>使用說明</span>
                <i class="fa fa-caret-down" aria-hidden="true"></i>
                <div class="more-info">
                    <a href="#" class="more-item">管他什麼菜單</a>
                    <a href="#" class="more-item">實在是編不了</a>
                </div>
            </div>
        </div>

        <div class="right-menu right clearfix">

            <div class="user-info right">
                <a href="#" class="avatar">
                    <img class="img-circle" src="{% static 'imgs/default.png' %}">
                </a>

                <div class="more-info">
                    <a href="#" class="more-item">我的信息</a>
                    <a href="#" class="more-item">註銷</a>
                </div>
            </div>

            <a class="user-menu right">
                消息
                <i class="fa fa-commenting-o" aria-hidden="true"></i>
                <span class="badge bg-success">2</span>
            </a>

            <a class="user-menu right">
                通知
                <i class="fa fa-envelope-o" aria-hidden="true"></i>
                <span class="badge bg-success">2</span>
            </a>

            <a class="user-menu right">
                任務
                <i class="fa fa-bell-o" aria-hidden="true"></i>
                <span class="badge bg-danger">4</span>
            </a>
        </div>

    </div>
</div>
<div class="pg-body">
    <div class="left-menu">
        <div class="menu-body">
            {% menu request %}
        </div>
    </div>
    <div class="right-body">
        <div>
            <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">

                <li><a href="#">首頁</a></li>
                <li class="active">客戶管理</li>

            </ol>
        </div>
        {% block content %} {% endblock %}
    </div>
</div>


<script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
<script src="{% static 'plugins/bootstrap/js/bootstrap.js' %} "></script>
{% block js %} {% endblock %}
</body>
</html>
View Code

重啓django項目,從新登陸,效果同上!

rbac組件就獨立出來了,之後想用的話,複製整個文件夾。

並作一些相關配置,就可使用了!

 

完整代碼,請訪問github

https://github.com/987334176/luffy_permission/archive/v1.4.zip

 

3、其餘應用使用rbac組件

建立新項目

新建一個項目untitled,注意django 版本爲1.11

修改models.py,增長2個表

from django.db import models

class Classes(models.Model):
    name = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
View Code

修改urls.py,增長路徑

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.login),
    url(r'^student/$', views.student),
    url(r'^student/add/$', views.student_add),
]
View Code

修改views.py,增長視圖函數

from django.shortcuts import render

# Create your views here.
def login(request):
    """
    用戶登錄
    :param request:
    :return:
    """
    if request.method == 'GET':
        return render(request,'login.html')

def student(request):

    return render(request,'student.html')


def student_add(request):
    return render(request, 'student_add.html')
View Code

在templates新增文件layout.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div style="height: 48px;background-color: aquamarine"></div>
    <div>
        <div style="width: 20%;float: left;background-color: #dddddd">
            菜單
        </div>
        <div style="width: 80%;float: left">
            {% block content %} {% endblock %}
        </div>
    </div>
</body>
</html>
View Code

student_add.html

{% extends 'layout.html' %}


{% block content %} 
    <h1>添加學生</h1>
{% endblock %}
View Code

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post">
        {% csrf_token %}

        <input type="text" name="user">
        <input type="password" name="pwd">

        <input type="submit" value="提交"> {{ msg }}
    </form>
</body>
</html>
View Code

student.html

{% extends 'layout.html' %}


{% block content %}
    <h1>學生列表</h1>
{% endblock %}
View Code

啓動djang項目,訪問頁面

http://127.0.0.1:8000/student/add/

效果以下:

 

拷貝rbac組件

將上面的rbac文件中,直接copy到項目根目錄

清空migrations目錄

進入rbac目錄中的migrations文件夾,將裏面的全部文件刪除

註冊rbac到app

修改settings.py,註冊rbac到app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rbac.apps.RbacConfig',
]
View Code

數據庫遷移

使用2個命令,生成表。注意:定義要帶上應用名rbac,不然沒法生成表!

python manage.py makemigrations rbac
python manage.py migrate

查看錶是否生成了

錄入權限信息

建立超級用戶

python manage.py createsuperuser

 查看admin.py

from django.contrib import admin
from rbac import models

class PermissionAdmin(admin.ModelAdmin):
    list_display = ['title','url']  # 顯示的字段
    list_editable = ['url']  # 容許編輯

admin.site.register(models.Permission,PermissionAdmin)


admin.site.register(models.Role)
admin.site.register(models.UserInfo)
View Code

 

登陸後臺,添加數據

添加角色

添加用戶

用戶登錄作權限和菜單的初始化

 修改app01目錄下的views.py

from django.shortcuts import render, redirect,HttpResponse
from rbac import models
from rbac.service.init_permission import init_permission


def login(request):
    """
    用戶登錄
    :param request:
    :return:
    """
    if request.method == 'GET':
        return render(request,'login.html')

    # 1. 獲取提交的用戶名和密碼
    user = request.POST.get('user')
    pwd = request.POST.get('pwd')

    # 2. 檢驗用戶是否合法
    obj = models.UserInfo.objects.filter(name=user,password=pwd).first()
    if not obj:
        return render(request, 'login.html',{'msg':'用戶名或密碼錯誤'})
    request.session['user_info'] = {'id': obj.id, 'name': obj.name}
    init_permission(request,obj)
    return redirect('/student/')

def student(request):

    return render(request,'student.html')


def student_add(request):
    return render(request, 'student_add.html')
View Code

應用中間件進行權限校驗

修改settings.py,註冊中間件

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'rbac.middleware.rbac.RbacMiddleware',
]
View Code

設置配置文件

修改settings.py,最後一行添加

########################## 權限相關 #######################
PERMISSION_SESSION_KEY = "permission_list"
MENU_SESSION_KEY = "menu_list"
VALID_URL = [
    '^/login/$',
    '^/admin/.*',
]
View Code

顯示動態菜單

引入css

{% load staticfiles %}
<link rel="stylesheet" href="{% static 'rbac/rbac.css' %}" />

調用動態菜單

{% load rbac %}
{% menu request %}

合併

修改app01-->templates-->layout.html

{% load staticfiles %}
{% load rbac %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'rbac/rbac.css' %}" />
    {% block css %} {% endblock %}
</head>
<body>
    <div style="height: 48px;background-color: aquamarine"></div>
    <div>
        <div style="width: 20%;float: left;background-color: #dddddd">
            {% menu request %}
        </div>
        <div style="width: 80%;float: left">
            {% block content %} {% endblock %}
        </div>
    </div>
    {% block js %} {% endblock %}
</body>
</html>
View Code

 

登陸帳戶

查看頁面

 

總結:

如何在其餘系統中應用目前的rbac組件。
a. 拷貝rbac組件

b. 清空migrations目錄

c. 註冊rbac 到app

d. 數據庫遷移並錄入權限信息

e. 用戶登錄作權限和菜單的初始化 init_permission 

f. 應用中間件進行權限校驗

g. 設置配置文件
    ########################## 權限相關 #######################
    PERMISSION_SESSION_KEY = "permission_list"
    MENU_SESSION_KEY = "menu_list"
    VALID_URL = [
        '^/login/$',
        '^/admin/.*',
    ]
    
h. 顯示動態菜單
    - 引入css
        {% load staticfiles %}
        <link rel="stylesheet" href="{% static 'rbac/rbac.css' %}" />
    - 調用動態菜單
        
        {% load rbac %}
        {% menu request %}
    合起來:
        {% load staticfiles %}
        {% load rbac %}
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
            <link rel="stylesheet" href="{% static 'rbac/rbac.css' %}" />
            {% block css %} {% endblock %}
        </head>
        <body>
            <div style="height: 48px;background-color: aquamarine"></div>
            <div>
                <div style="width: 20%;float: left;background-color: #dddddd">
                    {% menu request %}
                </div>
                <div style="width: 80%;float: left">
                    {% block content %} {% endblock %}
                </div>
            </div>
            {% block js %} {% endblock %}
        </body>
        </html>
View Code

 

4、django static文件的引入方式

建立 static文件夾

在django project中建立 static文件夾

配置靜態目錄

settings.py中配置要在 STATIC_URL = '/static/'  下邊

STATICFILES_DIRS = [

    os.path.join(BASE_DIR, 'static'), 

  ]

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

 

前端引入

方法一(推薦)

在頁面的較上處寫:

{% load staticfiles %}

在 link script 等src 寫 :

{%static 'xxx.css'%} 

{%static 'xxx.js'%}

完整代碼:

<link rel="stylesheet" href="{%static 'xxx.css'%} "/>
<script src="{%static 'xxx.js'%} "></script>

 

方式二

在 link script 等src 寫

/static/xxx.cs

二者可混用,但不推薦!

推薦使用方式一

 

查找過程

默認的django程序,會設置STATIC_URL 

Django 默認會在各app下的static文件夾中找文件。注意有前後順序,找到了就再也不繼續找了!、

那在模版中使用這些靜態文件時,使用

{%static 'sample.css'%}
{%static 'xxx.js'%}

Django在運行時會自動將這些文件映射到STATIC_URL所給定的值下。也就是如,若是STATIC_URL = '/static/',那麼在運行時,上邊模版中的樣例中的url會被替換爲/static/css/sample.css

 

注意事項

1.其實將靜態文件路徑硬編碼在模版中也能夠正常運行,如使用href="/static/css/sample.css",前提是配置好了STATIC_URL和STATIC_DIRS。但並不推薦這麼作,由於若是後來靜態資源的位置發生了遷移,如使用獨立服務器或者使用CDN,就要修改一大堆URL。而使用推薦的方法能夠避免這個龐大的工做量,最多隻須要修改STATIC_URL便可。

 

2.若是css文件中也使用了靜態文件如css背景,則按照相對路徑使用便可,由於瀏覽器解析css是會自動按照相對路徑尋找到正確的URL。

 

綜上所述:默認會配置STATIC_URL,在模板中推薦使用{%static 'sample.css'%} 這種方式!

 

 在這裏還須要強調的是,在開發階段,Django使用的是內建的一個靜態文件服務器,雖然在生產環境中也可使用,可是它既不穩定也不安全。更好的方式是使用提供web服務的服務器如apache來服務靜態文件。這須要你首先上傳代碼到服務器,而後運行collectstatic命令:python manage.py collectstatic 而後配置web服務器來爲靜態文件服務,如對Apache2進行配置。

 

做業:

如何實現動態生成二級菜單

思路

一級菜單,在數據庫加字段;
二級菜單,在權限表中,

表設計以下:

class Menu(models.Model):
    """
    菜單
    """
    title = models.CharField(verbose_name='菜單', max_length=32)
    icon = models.CharField(verbose_name='圖標', max_length=32)

    def __str__(self):
        return self.title


class Permission(models.Model):
    """
    權限表
    """
    title = models.CharField(verbose_name='標題', max_length=32)
    url = models.CharField(verbose_name='含正則的URL', max_length=128)

    menu = models.ForeignKey(verbose_name='菜單', to='Menu', null=True, blank=True, help_text='null表示非菜單')

    def __str__(self):
        return self.title
View Code

完整models.py以下:

from django.db import models

class Menu(models.Model):
    """
    菜單
    """
    title = models.CharField(verbose_name='菜單', max_length=32)
    icon = models.CharField(verbose_name='圖標', max_length=32)

    def __str__(self):
        return self.title

class Permission(models.Model):
    """
    權限表
    """
    title = models.CharField(verbose_name='標題', max_length=32)
    url = models.CharField(verbose_name='含正則的URL', max_length=128)

    menu = models.ForeignKey(verbose_name='菜單', to='Menu', null=True, blank=True, help_text='null表示非菜單')
    def __str__(self):
        return self.title


class Role(models.Model):
    """
    角色
    """
    title = models.CharField(verbose_name='角色名稱', max_length=32)
    permissions = models.ManyToManyField(verbose_name='擁有的全部權限', to='Permission', blank=True)

    def __str__(self):
        return self.title


class UserInfo(models.Model):
    """
    用戶表
    """
    name = models.CharField(verbose_name='用戶名', max_length=32)
    password = models.CharField(verbose_name='密碼', max_length=64)
    email = models.CharField(verbose_name='郵箱', max_length=32)
    roles = models.ManyToManyField(verbose_name='擁有的全部角色', to='Role', blank=True)

    def __str__(self):
        return self.name
View Code
相關文章
相關標籤/搜索