zoukankan      html  css  js  c++  java
  • python 全栈开发,Day108(客户管理之权限控制,客户管理之动态"一级"菜单,其他应用使用rbac组件,django static文件的引入方式)

    一、客户管理之权限控制

    昨天的作业,有很多不完善的地方

    下载代码,基本实现权限验证

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

    必须下载此代码,否则下面的不用看了!!!

    补充说明:

    admin后台

    查看rbac目录下的admin.py,看下面几行代码

    class PermissionAdmin(admin.ModelAdmin):
        list_display = ['title','url']  # 显示的字段
        list_editable = ['url']  # 允许编辑
    
    admin.site.register(models.Permission,PermissionAdmin)

    它能实现下面的效果:

    url可以直接编辑。注意url的最前面,必须包含"/",为什么呢?后面做菜单的时候,会用到!

    session问题

    查看web目录-->views目录下的account.py,看这一行

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

    有些用户,是没有角色的。需要使用permissions__url__isnull=False   过滤掉为空的记录

    由于权限判断,只需要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中,方便扩展!

    二、客户管理之动态"一级"菜单

    上述过程中的菜单是在程序中定义好的,无法根据用户权限进行动态配置。

    那么,接下来我们来完成一个 “单级菜单”的功能:

    表设计

    看下面的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;
                 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- 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 {
                 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 {
         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;
                 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- 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

    三、其他应用使用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=" 20%;float: left;background-color: #dddddd">
                菜单
            </div>
            <div style=" 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=" 20%;float: left;background-color: #dddddd">
                {% menu request %}
            </div>
            <div style=" 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=" 20%;float: left;background-color: #dddddd">
                        {% menu request %}
                    </div>
                    <div style=" 80%;float: left">
                        {% block content %} {% endblock %}
                    </div>
                </div>
                {% block js %} {% endblock %}
            </body>
            </html>
    View Code

    四、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
  • 相关阅读:
    Yahoo! 35条网站性能优化建议
    常见排序
    文件上传于拖拽
    离线web存储
    【前端学习】【jQuery选择器】
    【前端学习】【CSS选择器】
    【疯狂Java讲义学习笔记】【流程控制与数组】
    【疯狂Java讲义学习笔记】【数据类型与运算符】
    【疯狂Java学习笔记】【理解面向对象】
    【疯狂Java学习笔记】【第一章:Java语言概述】
  • 原文地址:https://www.cnblogs.com/xiao987334176/p/9512585.html
Copyright © 2011-2022 走看看