zoukankan      html  css  js  c++  java
  • Day 75 CRM业务——rbac组件

    CRM业务

    ModelForm

    生成 ModelForm 对象

    from django import forms
    from rbac import model
    
    class User_add_list(forms.modelfrom):
        class Meta:
            model = model.Menu
            fields = ('user','create_time')     # 获取
            # exclude = ('user')     # 排除
            # widgets = {}            # 自定义修饰 form 组件
            # error_messages        # 重写错误信息

    ModelForm 自定义字段

    from django import forms
    form rbac import model
    
    class User_add_list(forms.modelfrom):
        # 自定义字段
        Confirm_password = forms.CharField(label='确认密码')
        class Meta:
            model = model.Menu
            # 加入自定义字段
            fields = ('Confirm_password')

    forms中钩子的运用

    # 在 Meta 类下,创建 clean 开头的函数
    
    def clean_confirm_password(self):
        pass = self.clean_data('pawd')
        con_pass = self.clean_data('Confirm_password')
        if not pass == con_pass:
             raise ValidationError('两次密码输入错误')
        return con_pass 

    重写 ModelForm 中的 __init__ 方法

    from django import forms
    form rbac import model
    
    class User_add_list(forms.modelfrom):
        class Meta:
            pass
        def __init__(self,*args,**kwargs):
            super(User_add_list,self).__init__(*args,**kwargs)
            # 统一加上样式
            for name,field in self.fields.item():
                field.widget.attrs['class'] = 'form-control'

    ModelForm定制 radio

    from django import forms
    from rbac import models
    from django.utils.safestring import mark_safe
    
    icon_list = [
        ['fa-calendar', '<i class="fa fa-calendar" aria-hidden="true"></i>'],
        ['fa-calculator', '<i class="fa fa-calculator" aria-hidden="true"></i>'],
        ['fa-bus', '<i class="fa fa-bus" aria-hidden="true"></i>'],
        ['fa-calendar-check-o', '<i class="fa fa-calendar-check-o" aria-hidden="true"></i>']
    ]
    
    icon_safe = []
    for i in icon_list:
        i[1] = mark_safe(i[1])
    
    class MenuModelForms(forms.ModelForm):
        class Meta:
            model = models.Menu
            fields = ('title', 'icon')
            widgets = {
                'title': forms.TextInput(attrs={'class': 'form-control'}),
                'icon': forms.RadioSelect(
                    choices=icon_list,
                    attrs={'class': 'clearfix'}
                )
            }

     

    ModelForm显示默认值

    from rbac.form.base import WidegetClass
    
    
    class SecondMenuModelForms(WidegetClass):
        class Meta:
            model = models.Permission
            fields = ('title', 'name', 'url', 'menu')
    
    
    
    from django.shortcuts import render, redirect, reverse, HttpResponse
    from rbac import models
    from rbac.form.menu import MenuModelForms, SecondMenuModelForms, ThreeMenuModelForms
    from rbac.server.urls import memory_reverse
    
    
    def second_menu_add(request, menu_id):
        '''
        添加二级菜单
        :param request:
        :param menu_id:
        :return:
        '''
        menu_obj = models.Menu.objects.filter(pk=menu_id).first()
        if request.method == "GET":
            # initial={"menu": menu_obj}  输入要显示的默认值
            forms = SecondMenuModelForms(initial={"menu": menu_obj})
            return render(request, 'role/changes.html', locals())    

    ModelForm——save之前对其 instance 进行修改

    from django import forms
    form rbac import model
    
    from rbac.form.base import WidegetClass
    
    class ThreeMenuModelForms(WidegetClass):
        class Meta:
            model = models.Permission
            fields = ('title','name','url')
    
    
    def three_menu_add(request, menu_id):
        '''
        权限菜单
        :param requset:
        :param menu_id:
        :return:
        '''
        if request.method == 'GET':
            forms = ThreeMenuModelForms()
            return render(request, 'role/changes.html', locals())
        forms = ThreeMenuModelForms(data=request.POST)
        if forms.is_valid():
            # 在保存前存入二级目录id
            num_s_obj = models.Permission.objects.filter(pk=menu_id).first()
            if not num_s_obj:
                return HttpResponse('二级目录不存在,请重新选择')
            forms.instance.pid = num_s_obj
            forms.save()
            return redirect(memory_reverse(request, 'rbac:menu_list'))
        return render(request, 'role/changes.html', locals())

    报错中文显示

    setting.py 文件
    # LANGUAGE_CODE = 'en-us'    # 英文
    LANGUAGE_CODE = 'zh-hans'    # 中文

    反向解析

    # url:方法
    # rbac:url分发名称,有就加上
    # user_add:自定义 url 的名称空间
    # pk:自定义 url 分组名称
    {{ url ''rbac:user_add" pk='id' }}

    模板查询顺序

    # 根据 INSTALLED_APPS 的注册顺序来查找模板
    INSTALLED_APPS = [
        'django.contrib.staticfiles',
        'rbac.apps.RbacConfig',
        'web.apps.WebConfig'
    ]

    菜单跳转并保留原搜索条件

    from django.shortcuts import reverse
    from django.http import QueryDict
    
    # 跳转菜单后 保留原搜索条件,生成带原搜索条件的 url
    def memory_url(request,name,*args,**kwargs):
        # 反向生成基本的url
        basic_url = reverse(name,args=args,kwargs=kwargs)
        if not basic_url:
            return basic_url
        queru_dict = QueryDict(mutable=True)
        queru_dict['_filter'] = request.GET.urlencode()  # 获取 get 值并放入字典
        get_value = queru_dict.urlencode()  # 打包 转义
    
        return "%s?%s" % (basic_url, get_value)
    
    
    # 返回生成带有 get 值的 url
    def memory_reverse(request, name, *args, **kwargs):
        '''
        返回生成带有 get 值的 url
            http://127.0.0.1:8000/rbac/menu/edit/2/?_filter=num%3D2
            1.在 url 中将原来搜索条件,如 filter 后的值
            2.reverse 圣桦城原来的 url,如:
            3./menu/list/?num%3D2
        :return:
        '''
        url = reverse(name, args=args, kwargs=kwargs)
        origin_params = request.GET.get('_filter')
        if origin_params:
            url = '%s?%s' % (url, origin_params)
        return url

    HTML模板中过滤器的使用

    # 模板中过滤器的使用
    {{obj|filter__name:param}}
    
    {{ 1|safe }}                    # int ---> string
    { value|date:"Y-m-d" }}   # 格式化时间 
        

    RBAC组件的使用文档

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    """
    RBAC组件的使用文档
    
    1. 将rbac组件拷贝项目。
    
    
    2. 将rbac/migrations目录中的数据库迁移记录删除
    
    
    3. 业务系统中用户表结构的设计
    
        业务表结构中的用户表需要和rbac中的用户有继承关系,如:
    
        rbac/models.py
            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) 严重提醒 Role 不要加引号
    
                def __str__(self):
                    return self.name
    
                class Meta:
                    # django以后再做数据库迁移时,不再为UserInfo类创建相关的表以及表结构了。
                    # 此类可以当做"父类",被其他Model类继承。
                    abstract = True
    
        业务/models.py
            class UserInfo(RbacUserInfo):
                phone = models.CharField(verbose_name='联系方式', max_length=32)
                level_choices = (
                    (1, 'T1'),
                    (2, 'T2'),
                    (3, 'T3'),
                )
                level = models.IntegerField(verbose_name='级别', choices=level_choices)
    
                depart = models.ForeignKey(verbose_name='部门', to='Department')
    
    4. 讲业务系统中的用户表的路径写到配置文件。
    
        # 业务中的用户表
        RBAC_USER_MODLE_CLASS = "app01.models.UserInfo"
    
        用于在rbac分配权限时,读取业务表中的用户信息。
    
    
    5. 业务逻辑开发
        将所有的路由都设置一个name,如:
                url(r'^login/$', account.login, name='login'),
                url(r'^logout/$', account.logout, name='logout'),
    
                url(r'^index/$', account.index, name='index'),
    
                url(r'^user/list/$', user.user_list, name='user_list'),
                url(r'^user/add/$', user.user_add, name='user_add'),
                url(r'^user/edit/(?P<pk>d+)/$', user.user_edit, name='user_edit'),
                url(r'^user/del/(?P<pk>d+)/$', user.user_del, name='user_del'),
                url(r'^user/reset/password/(?P<pk>d+)/$', user.user_reset_pwd, name='user_reset_pwd'),
    
                url(r'^host/list/$', host.host_list, name='host_list'),
                url(r'^host/add/$', host.host_add, name='host_add'),
                url(r'^host/edit/(?P<pk>d+)/$', host.host_edit, name='host_edit'),
                url(r'^host/del/(?P<pk>d+)/$', host.host_del, name='host_del'),
        用于反向生成URL以及粒度控制到按钮级别的权限控制。
    
    6. 权限信息录入
        - 在url中添加rbac的路由分发,注意:必须设置namespace
            urlpatterns = [
                ...
                url(r'^rbac/', include('rbac.urls', namespace='rbac')),
    
            ]
    
        - rbac提供的地址进行操作
            - http://127.0.0.1:8000/rbac/menu/list/
            - http://127.0.0.1:8000/rbac/role/list/
            - http://127.0.0.1:8000/rbac/distribute/permissions/
    
        相关配置:自动发现URL时,排除的URL:
    
            # 自动化发现路由中URL时,排除的URL
            AUTO_DISCOVER_EXCLUDE = [
                '/admin/.*',
                '/login/',
                '/logout/',
                '/index/',
            ]
    
    
    7. 编写用户登录的逻辑【进行权限初始化】
    
        from django.shortcuts import render, redirect
        from app01 import models
        from rbac.service.init_permission import init_permission
    
    
        def login(request):
            if request.method == 'GET':
                return render(request, 'login.html')
    
            user = request.POST.get('username')
            pwd = request.POST.get('password')
    
            user_object = models.UserInfo.objects.filter(name=user, password=pwd).first()
            if not user_object:
                return render(request, 'login.html', {'error': '用户名或密码错误'})
    
            # 用户权限信息的初始化
            init_permission(user_object, request)
    
            return redirect('/index/')
    
    
        相关配置: 权限和菜单的session key:
    
            setting.py
                PERMISSION_SESSION_KEY = "luffy_permission_url_list_key"
                MENU_SESSION_KEY = "luffy_permission_menu_key"
    
    8. 编写一个首页的逻辑
    
        def index(request):
            return render(request, 'index.html')
    
    
        相关配置:需要登录但无需权限的URL
    
            # 需要登录但无需权限的URL
            NO_PERMISSION_LIST = [
                '/index/',
                '/logout/',
            ]
    
    9. 通过中间件进行权限校验
    
        # 权限校验
        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.middlewares.rbac.RbacMiddleware',
        ]
    
        # 白名单,无需登录就可以访问
        VALID_URL_LIST = [
            '/login/',
            '/admin/.*'
        ]
    
    
    10. 粒度到按钮级别的控制
    
            {% extends 'layout.html' %}
            {% load rbac %}
    
            {% block content %}
                <div class="luffy-container">
                    <div class="btn-group" style="margin: 5px 0">
    
                        {% if request|has_permission:'host_add' %}
                            <a class="btn btn-default" href="{% memory_url request 'host_add' %}">
                                <i class="fa fa-plus-square" aria-hidden="true"></i> 添加主机
                            </a>
                        {% endif %}
    
                    </div>
                    <table class="table table-bordered table-hover">
                        <thead>
                        <tr>
                            <th>主机名</th>
                            <th>IP</th>
                            <th>部门</th>
                            {% if request|has_permission:'host_edit' or request|has_permission:'host_del' %}
                                <th>操作</th>
                            {% endif %}
    
                        </tr>
                        </thead>
                        <tbody>
                        {% for row in host_queryset %}
                            <tr>
                                <td>{{ row.hostname }}</td>
                                <td>{{ row.ip }}</td>
                                <td>{{ row.depart.title }}</td>
                                {% if request|has_permission:'host_edit' or request|has_permission:'host_del' %}
                                    <td>
                                        {% if request|has_permission:'host_edit' %}
                                            <a style="color: #333333;" href="{% memory_url request 'host_edit' pk=row.id %}">
                                                <i class="fa fa-edit" aria-hidden="true"></i></a>
                                        {% endif %}
                                        {% if request|has_permission:'host_del' %}
                                            <a style="color: #d9534f;" href="{% memory_url request 'host_del' pk=row.id %}"><i
                                                    class="fa fa-trash-o"></i></a>
                                        {% endif %}
                                    </td>
                                {% endif %}
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>
                </div>
    
            {% endblock %}
    
    
    
    
    总结,目的是希望在任意系统中应用权限系统。
        - 用户登录 + 用户首页 + 用户注销 业务逻辑
        - 项目业务逻辑开发
            注意:开发时候灵活的去设置layout.html中的两个inclusion_tag
                <div class="pg-body">
                    <div class="left-menu">
                        <div class="menu-body">
                            {% multi_menu request %}  # 开发时,去掉;上下时,取回。
                        </div>
                    </div>
                    <div class="right-body">
                        <div>
                            {% breadcrumb request %} # 开发时,去掉;上下时,取回。
                        </div>
                        {% block content %} {% endblock %}
                    </div>
                </div>
        - 权限信息的录入
        - 配置文件
            # 注册APP
            INSTALLED_APPS = [
                ...
                'rbac.apps.RbacConfig'
            ]
            # 应用中间件
            MIDDLEWARE = [
                ...
                'rbac.middlewares.rbac.RbacMiddleware',
            ]
    
            # 业务中的用户表
            RBAC_USER_MODLE_CLASS = "app01.models.UserInfo"
            # 权限在Session中存储的key
            PERMISSION_SESSION_KEY = "luffy_permission_url_list_key"
            # 菜单在Session中存储的key
            MENU_SESSION_KEY = "luffy_permission_menu_key"
    
            # 白名单
            VALID_URL_LIST = [
                '/login/',
                '/admin/.*'
            ]
    
            # 需要登录但无需权限的URL
            NO_PERMISSION_LIST = [
                '/index/',
                '/logout/',
            ]
    
            # 自动化发现路由中URL时,排除的URL
            AUTO_DISCOVER_EXCLUDE = [
                '/admin/.*',
                '/login/',
                '/logout/',
                '/index/',
            ]
    
        - 粒度到按钮级别的控制
    
    
    
    
    
    
    """
    View Code
  • 相关阅读:
    斐波纳契数列
    实现刮刮乐的效果
    简易版美图秀秀
    js 宏任务和微任务
    作业3 阅读
    作业2 结对子作业
    做汉堡
    练习一
    Java设计模式十八:代理模式(Proxy)
    Java设计模式二十:适配器模式(Adapter)
  • 原文地址:https://www.cnblogs.com/ysging/p/13026883.html
Copyright © 2011-2022 走看看