• rbac组件权限分配之一级菜单


    示例:

     

     

     

     第一步:路由创建

    rbac/urls.py

    from django.urls import re_path
    from rbac.views import role
    from rbac.views import user
    from rbac.views import menu
    
    urlpatterns = [
        # 角色管理
        re_path(r'^role/list/$', role.role_list, name="role_list"),
        re_path(r'^role/add/$', role.role_add, name="role_add"),
        re_path(r'^role/edit/(?P<pk>\d+)/$', role.role_edit, name="role_edit"),
        re_path(r'^role/del/(?P<pk>\d+)/$', role.role_del, name="role_del"),
        # 用户管理
        re_path(r'^user/list/$', user.user_list, name="user_list"),
        re_path(r'^user/add/$', user.user_add, name="user_add"),
        re_path(r'^user/edit/(?P<pk>\d+)/$', user.user_edit, name="user_edit"),
        re_path(r'^user/del/(?P<pk>\d+)/$', user.user_del, name="user_del"),
        re_path(r'^user/reset/password/(?P<pk>\d+)/$', user.user_reset_pwd, name="user_reset_pwd"),
        # 一级菜单管理
        re_path(r'^menu/list/$', menu.menu_list, name="menu_list"),
        re_path(r'^menu/add/$', menu.menu_add, name="menu_add"),
        re_path(r'^menu/eidt/(?P<pk>\d+)/$', menu.menu_edit, name="menu_edit"),
        re_path(r'^menu/del/(?P<pk>\d+)/$', menu.menu_del, name="menu_del"),
    ]

    第二步:ModelForm书写

    rbac/forms/menu.py

    from django import forms
    from rbac import models
    from django.utils.safestring import mark_safe
    
    
    ICON_LIST = [
        ["fa-address-book", '<i class="fa fa-address-book" aria-hidden="true"></i>'],
        ["fa-adjust", '<i class="fa fa-adjust" aria-hidden="true"></i>'],
        ["fa-area-chart", '<i class="fa fa-area-chart" aria-hidden="true"></i>'],
        ["fa-ban", '<i class="fa fa-ban" aria-hidden="true"></i>'],
        ["fa-at", '<i class="fa fa-at" aria-hidden="true"></i>'],
        ["fa-car", '<i class="fa fa-car" aria-hidden="true"></i>'],
        ["fa-university", '<i class="fa fa-university" aria-hidden="true"></i>'],
        ["fa-bar-chart", '<i class="fa fa-bar-chart" aria-hidden="true"></i>'],
        ["fa-bell", '<i class="fa fa-bell" aria-hidden="true"></i>'],
        ["fa-bell-slash", '<i class="fa fa-bell-slash" aria-hidden="true"></i>'],
        ["fa-birthday-cake", '<i class="fa fa-birthday-cake" aria-hidden="true"></i>'],
        ["fa-book", '<i class="fa fa-book" aria-hidden="true"></i>'],
        ["fa-bookmark", '<i class="fa fa-bookmark" aria-hidden="true"></i>'],
        ["fa-bug", '<i class="fa fa-bug" aria-hidden="true"></i>'],
        ["fa-calendar-check-o", '<i class="fa fa-calendar-check-o" aria-hidden="true"></i>'],
        ["fa-cart-plus", '<i class="fa fa-cart-plus" aria-hidden="true"></i>'],
        ["fa-check-circle", '<i class="fa fa-check-circle" aria-hidden="true"></i>'],
        ["fa-times", '<i class="fa fa-times" aria-hidden="true"></i>'],
        ["fa-cloud-download", '<i class="fa fa-cloud-download" aria-hidden="true"></i>'],
        ["fa-cog", '<i class="fa fa-cog" aria-hidden="true"></i>'],
        ["fa-comments", '<i class="fa fa-comments" aria-hidden="true"></i>'],
        ["fa-copyright", '<i class="fa fa-copyright" aria-hidden="true"></i>'],
        ["fa-database", '<i class="fa fa-database" aria-hidden="true"></i>'],
        ["fa-envelope", '<i class="fa fa-envelope" aria-hidden="true"></i>'],
        ["fa-folder-open", '<i class="fa fa-folder-open" aria-hidden="true"></i>'],
        ["fa-thumbs-up", '<i class="fa fa-thumbs-up" aria-hidden="true"></i>'],
        ["fa-thumbs-down", '<i class="fa fa-thumbs-down" aria-hidden="true"></i>'],
        ["fa-users", '<i class="fa fa-users" aria-hidden="true"></i>'],
        ["fa-star", '<i class="fa fa-star" aria-hidden="true"></i>'],
        ["fa-handshake-o", '<i class="fa fa-handshake-o" aria-hidden="true"></i>'],
        ["fa-camera-retro", '<i class="fa fa-camera-retro" aria-hidden="true"></i>'],
        ["fa-fire", '<i class="fa fa-fire" aria-hidden="true"></i>'],
        ["fa-user-circle", '<i class="fa fa-user-circle" aria-hidden="true"></i>'],
    ]
    for item in ICON_LIST:
        # mark_safe()也可以让前端页面直接渲染标签
        item[1] = mark_safe(item[1])
    
    
    class MenuModelForm(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"}),
            }
    
    
    """
    icon字段,使用的是单选输入框,choices会循环一个可迭代对象,每个循环元素的第一个值作为单选框的value的值,第二个值
    作为前端显示值,如:<input type="radio" value="value的值">前端显示值
    """

    第三步:生成带搜索条件的url和反向解析带搜索条件的url

    rbac/service/urls.py

    from django.urls import reverse
    from django.http import QueryDict
    
    
    def memory_reverse(request, name, *args, **kwargs):
        """
        反向生成URL
            http://127.0.0.1:8000/rbac/menu/add/?_filter=mid%3D1
            1. 在url中将原来的搜索条件获取,如:_filter后的值
            2. reverse生成原来的url,如:/menu/list/
            3. http://127.0.0.1:8000/rbac/menu/list/?mid=1
        :param request:
        :param name:
        :param args:
        :param kwargs:
        :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
    
    
    def memory_url(request, name, *args, **kwargs):
        """
        生成带有原搜索条件的URL(替代了模板中的url)
        http://127.0.0.1:8000/rbac/menu/list/?mid=1
        http://127.0.0.1:8000/rbac/menu/add/?_filter=mid%3D1
        :param request:
        :param name:
        :return:
        """
        basic_url = reverse(name, args=args, kwargs=kwargs)
        # 当前url无参数
        if not request.GET:
            return basic_url
        query_dict = QueryDict(mutable=True)
        query_dict["_filter"] = request.GET.urlencode()  # 如果直接在request.GET中设置,是会报错的,所以需要用到QueryDict
        url = "%s?%s" % (basic_url, query_dict.urlencode())
        return url

    第四步:simple_tag的创建,再次封装memory_url,让其在模板中能够使用

    rbac/templatetags/rbac.py

    from django.template import Library
    from rbac.service import urls
    
    
    register = Library()
    
    
    @register.simple_tag
    def memory_url(request, name, *args, **kwargs):
        """
        生成带有原搜索条件的URL(替代了模板中的url)
        :param request:
        :param name:
        :return:
        """
        return urls.memory_url(request, name, *args, **kwargs)

    第五步:视图函数的书写

    rbac/views/menu.py

    from django.shortcuts import render, redirect, HttpResponse
    from rbac import models
    from rbac.forms.menu import MenuModelForm
    from rbac.service.urls import memory_reverse
    
    
    def menu_list(request):
        """
        菜单和权限列表
        :param request:
        :return:
        """
        menus = models.Menu.objects.all().order_by("pk")
        # menu_list.html的菜单名称是a标签,此a标签的href在当前url后面加了mid参数
        # 取到该参数后,传给模板,模板与所循环一级菜单的id进行if比较,如果相等,那么加上'active'表示选中
        menu_id = request.GET.get("mid")
        return render(request, "rbac/menu_list.html", {"menus": menus, "mid": menu_id})
    
    
    def menu_add(request):
        """
        添加一级菜单
        :param request:
        :return:
        """
        if request.method == "GET":
            form = MenuModelForm()
            return render(request, "rbac/change.html", {"form": form})
        form = MenuModelForm(data=request.POST)
        if form.is_valid():
            form.save()
            # memory_reverse将之前带有原搜索条件的url取出并拼接
            # /rbac/menu/add/?_filter=mid%3D1 ----> /rbac/menu/list/?mid=1
            return redirect(memory_reverse(request, "rbac:menu_list"))
        return render(request, "rbac/change.html", {"form": form})
    
    
    def menu_edit(request, pk):
        """
        编辑一级菜单
        :param request:
        :param pk:
        :return:
        """
        obj = models.Menu.objects.filter(id=pk).first()
        if not obj:
            return HttpResponse("菜单不存在...")
        if request.method == "GET":
            form = MenuModelForm(instance=obj)
            return render(request, "rbac/change.html", {"form": form})
        form = MenuModelForm(instance=obj, data=request.POST)
        if form.is_valid():
            form.save()
            return redirect(memory_reverse(request, "rbac:menu_list"))
        return render(request, "rbac/change.html", {"form": form})
    
    
    def menu_del(request, pk):
        """
        删除一级菜单
        :param request:
        :param pk:
        :return:
        """
        url = memory_reverse(request, "rbac:menu_list")
        if request.method == "GET":
            return render(request, "rbac/delete.html", {"cancel_url": url})
        models.Menu.objects.filter(id=pk).delete()
        return redirect(url)

    第六步:模板的创建

    rbac/templates/rbac/menu_list.html

    {% extends 'layout.html' %}
    
    {% load rbac %}
    
    {% block css %}
        <style>
            {# 选中样式css #}
            tr.active {
                border-left: 3px solid #fdc00f;
            }
        </style>
    {% endblock %}
    
    {% block content %}
        <div class="luffy-container">
            <div class="col-md-3">
                <div class="panel panel-default">
                    <!-- Default panel contents -->
                    <div class="panel-heading">
                        <i class="fa fa-book" aria-hidden="true"></i>
                        一级菜单
                        <a href="{% memory_url request 'rbac:menu_add' %}" class="right btn btn-success btn-xs"
                           style="padding: 2px 8px; margin: -3px;">
                            <i class="fa fa-plus-circle" aria-hidden="true"></i>
                            新建
                        </a>
                    </div>
    
                    <!-- Table -->
                    <table class="table">
                        <thead>
                        <tr>
                            <th>名称</th>
                            <th>图标</th>
                            <th>选项</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for menu in menus %}
                            {# safe也可以转换类型,将数值类型转为了字符串类型 #}
                            <tr class="{% if menu.id|safe == mid %}active{% endif %}">
                                <td>
                                    <a href="?mid={{ menu.id }}">{{ menu.title }}</a>
                                </td>
                                <td>
                                    <i class="fa {{ menu.icon }}" aria-hidden="true"></i>
                                </td>
                                <td>
                                    <a style="color: #333333;" href="{% memory_url request 'rbac:menu_edit' pk=menu.id %}">
                                        <i class="fa fa-edit" aria-hidden="true"></i></a>
                                    <a style="color: #d9534f;" href="{% memory_url request 'rbac:menu_del' pk=menu.id %}"><i
                                            class="fa fa-trash-o"></i></a>
                                </td>
                            </tr>
                        {% endfor %}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    {% endblock %}

    第七步:将渲染的图标进行样式更改

    rbac/templates/rbac/change.html

    {% extends 'layout.html' %}
    {% block css %}
        {# 图标样式css #}
        <style>
            ul {
                list-style-type: none;
                padding: 0;
            }
            ul li {
                float: left;
                padding: 10px;
                padding-left: 0;
                width: 80px;
            }
            ul li i {
                font-size: 18px;
                margin-left: 5px;
                color: #6d6565;
            }
        </style>
    {% endblock %}
    {% block content %}
        <div class="luffy-container">
            <form class="form-horizontal" method="post" novalidate>
                {% csrf_token %}
                {% for foo in form %}
                    <div class="form-group">
                        <label class="col-sm-2 control-label">{{ foo.label }}</label>
                        <div class="col-sm-8">
                            {{ foo }}
                            <span style="color: red">{{ foo.errors.0 }}</span>
                        </div>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-8">
                        <input type="submit" value="保存" class="btn btn-primary">
                    </div>
                </div>
            </form>
        </div>
    {% endblock %}

    总结:

      - 此次的难点在于默认选中这个功能,将带有搜索条件的url打包成一个指定参数,然后跳转到指定页面时,取出搜索条件将其默认选中效果展示;搜索功能自己想了一下(点击分页按钮搜索条件没有了),后面有时间验证是否正确

    """
    搜索如何保留原搜索条件?
       book/list/
       视图函数:
          先判断select是否有值,如果有--利用select的值进行查询得到queryset分页传给前端,并把select的值也传给前端
          如果没有--那么查询全部的queryset分页传给前端即可
       模板:
          搜索的input框使用get请求,如:name=select,value=搜索值;url:book/list/?select=搜索值
          分页按钮的a标签,?page=变量&select=后端传入变量
          重置按钮的url可以是/book/list/
    """

      - 将生成带有搜索条件的url和解析带有搜索条件的url两个功能函数放到了一块,但是生成有搜索条件的url功能函数需要在模板中调用,所以写成了simple_tag,在simple_tag中再导入此功能函数即可;

  • 相关阅读:
    省选后蛤蛤纪事
    About me
    第一篇blog
    震惊!山东一高中生学习锯木板,原因竟是...
    斯特林数相关
    省选后数学学习
    SDOI 2020游记
    奶茶推荐
    Goodbye 2019
    golang 并发锁的陷阱
  • 原文地址:https://www.cnblogs.com/xuewei95/p/15863285.html
Copyright © 2020-2023  润新知