权限组件(8):一级菜单的展示、增删改查和保留原参数

效果图:

一、路由配置

rbac/urls.py

from django.urls import re_path
from rbac.views import menu

urlpatterns = [
    ...
    # 菜单管理
    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/edit/(?P<pk>\d+)‘, menu.menu_edit, name=‘menu_edit‘),
    re_path(r‘^menu/del/(?P<pk>\d+)‘, menu.menu_del, name=‘menu_del‘),
    ...
]

二、forms表单验证

rbac/forms/menu.py

from django import forms
from django.utils.safestring import mark_safe

from rbac import models

ICON_LIST = [
    [‘fa-hand-scissors-o‘, ‘<i aria-hidden="true" class="fa fa-hand-scissors-o"></i>‘],
    [‘fa-hand-spock-o‘, ‘<i aria-hidden="true" class="fa fa-hand-spock-o"></i>‘],
    [‘fa-hand-stop-o‘, ‘<i aria-hidden="true" class="fa fa-hand-stop-o"></i>‘],
    [‘fa-handshake-o‘, ‘<i aria-hidden="true" class="fa fa-handshake-o"></i>‘],
    [‘fa-hard-of-hearing‘, ‘<i aria-hidden="true" class="fa fa-hard-of-hearing"></i>‘],
    [‘fa-hashtag‘, ‘<i aria-hidden="true" class="fa fa-hashtag"></i>‘],
    [‘fa-hdd-o‘, ‘<i aria-hidden="true" class="fa fa-hdd-o"></i>‘],
    [‘fa-headphones‘, ‘<i aria-hidden="true" class="fa fa-headphones"></i>‘],
    [‘fa-heart‘, ‘<i aria-hidden="true" class="fa fa-heart"></i>‘],
    [‘fa-heart-o‘, ‘<i aria-hidden="true" class="fa fa-heart-o"></i>‘],
    [‘fa-heartbeat‘, ‘<i aria-hidden="true" class="fa fa-heartbeat"></i>‘],
    [‘fa-history‘, ‘<i aria-hidden="true" class="fa fa-history"></i>‘],
    [‘fa-home‘, ‘<i aria-hidden="true" class="fa fa-home"></i>‘],
    [‘fa-hotel‘, ‘<i aria-hidden="true" class="fa fa-hotel"></i>‘],
    [‘fa-hourglass‘, ‘<i aria-hidden="true" class="fa fa-hourglass"></i>‘],
    [‘fa-hourglass-1‘, ‘<i aria-hidden="true" class="fa fa-hourglass-1"></i>‘],
    [‘fa-hourglass-2‘, ‘<i aria-hidden="true" class="fa fa-hourglass-2"></i>‘],
    [‘fa-hourglass-3‘, ‘<i aria-hidden="true" class="fa fa-hourglass-3"></i>‘],
    [‘fa-hourglass-end‘, ‘<i aria-hidden="true" class="fa fa-hourglass-end"></i>‘],
    [‘fa-hourglass-half‘, ‘<i aria-hidden="true" class="fa fa-hourglass-half"></i>‘],
    [‘fa-hourglass-o‘, ‘<i aria-hidden="true" class="fa fa-hourglass-o"></i>‘],
    [‘fa-hourglass-start‘, ‘<i aria-hidden="true" class="fa fa-hourglass-start"></i>‘],
    [‘fa-i-cursor‘, ‘<i aria-hidden="true" class="fa fa-i-cursor"></i>‘],
    [‘fa-id-badge‘, ‘<i aria-hidden="true" class="fa fa-id-badge"></i>‘],
    [‘fa-id-card‘, ‘<i aria-hidden="true" class="fa fa-id-card"></i>‘],
    [‘fa-id-card-o‘, ‘<i aria-hidden="true" class="fa fa-id-card-o"></i>‘],
    [‘fa-image‘, ‘<i aria-hidden="true" class="fa fa-image"></i>‘],
    [‘fa-mail-reply-all‘, ‘<i aria-hidden="true" class="fa fa-mail-reply-all"></i>‘],
    [‘fa-reply‘, ‘<i aria-hidden="true" class="fa fa-reply"></i>‘],
    [‘fa-reply-all‘, ‘<i aria-hidden="true" class="fa fa-reply-all"></i>‘],
    [‘fa-retweet‘, ‘<i aria-hidden="true" class="fa fa-retweet"></i>‘],
    [‘fa-wrench‘, ‘<i aria-hidden="true" class="fa fa-wrench"></i>‘]]

"""
 mark_safe:
 Explicitly mark a string as safe for (HTML) output purposes. The returned
 object can be used everywhere a string is appropriate.
"""
for item in ICON_LIST:
    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‘}
            )
        }

三、视图函数

from django.shortcuts import HttpResponse, render, redirect, reverse

from rbac import models
from rbac.forms.menu import MenuModelForm
from rbac.service.urls import memory_reverse

def menu_list(request):
    """
    菜单和权限列表
    :param request:
    :return:
    """
    menu_queryset = models.Menu.objects.all()

    menu_id = request.GET.get(‘mid‘)

    context = {
        ‘menu_list‘: menu_queryset,
        ‘menu_id‘: menu_id
    }

    return render(request, ‘rbac/menu_list.html‘, context)

def menu_add(request):
    """
     菜单和权限列表
    :param request:
    :return:
    """
    if request.method == ‘GET‘:
        forms = MenuModelForm()
        return render(request, ‘rbac/change.html‘, {‘forms‘: forms})

    forms = MenuModelForm(data=request.POST)
    if forms.is_valid():
        forms.save()
        url = memory_reverse(request, ‘rbac:menu_list‘)
        return redirect(url)

    return render(request, ‘rbac/change.html‘, {‘forms‘: forms})

def menu_edit(request, pk):
    """
    编辑一级菜单
    :param request:
    :param pk:
    :return:
    """
    menu_obj = models.Menu.objects.filter(id=pk).first()

    if not menu_obj:
        return HttpResponse(‘菜单不存在‘)

    if request.method == ‘GET‘:
        forms = MenuModelForm(instance=menu_obj)
        return render(request, ‘rbac/change.html‘, {‘forms‘: forms})

    forms = MenuModelForm(data=request.POST, instance=menu_obj)
    if forms.is_valid():
        forms.save()
        url = memory_reverse(request, ‘rbac:menu_list‘)
        return redirect(url)
    return render(request, ‘rbac/change.html‘, {‘forms‘: forms})

def menu_del(request, pk):
    """
    删除一级菜单
    :param request:
    :param pk:
    :return:
    """
    menu_list_url = memory_reverse(request, ‘rbac:menu_list‘)

    if request.method == ‘GET‘:
        return render(request, ‘rbac/delete.html‘, {‘cancel‘: menu_list_url})

    models.Menu.objects.filter(id=pk).delete()

    return redirect(menu_list_url)

memory_reverse的功能是当用户完成增删改返回列表页的时候,还带有原参数,这样回列表页的时候还会默认选中用户刚刚选中的参数

四、保留原参数

rbac/templatestags/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:
    :param args:
    :param kwargs:
    :return:
    """
    return urls.memory_url(request, name, *args, **kwargs)
...

rbac/service/urls.py

from django.http import QueryDict

from django.shortcuts import reverse

def memory_url(request, name, *args, **kwargs):
    # reverse用法:reverse(‘name‘, kwargs={‘pk‘: 1})
    # reverse用法:reverse(‘name‘, args=(1,))
    basic_url = reverse(name, args=args, kwargs=kwargs)
    # 当前url中无参数
    if not request.GET:
        return basic_url

    old_params = request.GET.urlencode()  # 获取url中的参数

    query_dict = QueryDict(mutable=True)  # 提供转义功能
    query_dict[‘_filter‘] = old_params

    # urlencode帮我们自动转义。
    # 如果不用urlencode,&符号会把这个参数分割成两个参数:_filter=mid=2 和 age=99
    return ‘%s?%s‘ % (basic_url, query_dict.urlencode())  # _filter=mid=2&age=99

def memory_reverse(request, name, *args, **kwargs):
    """
    反向生成URL
        http://127.0.0.1:8000/rbac/menu/edit/1/?_filter=mid%3D4
        1. 在URL获取原来的搜索条件获取(filter后的值)
        2. reverse生成原来的URL,如:/menu/list/
        3. /menu/list/?mid%3D4

    :param request:
    :param name:
    :param args:
    :param kwargs:
    :return:
    """

    url = reverse(name, args=args, kwargs=kwargs)
    original_parmas = request.GET.get(‘_filter‘)
    if original_parmas:
        url = ‘%s?%s‘ % (url, original_parmas)
    return url

由于需要传的参数超过了两个,所以需要用simple_tag。memory_url在模板用的。memory_reverse是在视图函数中反向解析的时候用的。

五、模板

模板层新增了菜单列表的模板,增、改页面由于新增了许多图标,所以也有一些小的变动。
rbac/templates/menu_list.html

{% extends ‘layout.html‘ %}
{% load rbac %}

<style>
    tr.active {
        border-left: 3px solid #fdc00f;
    }
</style>

{% 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 menu_list %}
                        <!-- 管道符可以将后端传来的整型,转换成字符串 -->
                        <tr class="{% if menu.id|safe == menu_id %}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; font-size:18px"
                                   href="{% memory_url request ‘rbac:menu_edit‘ pk=menu.id %}">
                                    <i class="fa fa-edit" aria-hidden="true"></i>
                                </a>

                                <a style="color: red; font-size:18px"
                                   href="{% memory_url request ‘rbac:menu_del‘ pk=menu.id %}">
                                    <i class="fa fa-trash-o" aria-hidden="true"></i>
                                </a>
                            </td>
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
    </div>

{% endblock content %}

需要用模板语法判断用户选中的菜单并给其一个active的样式

rbac/templates/change.html

{% extends ‘layout.html‘ %}

{% block 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 css %}

{% block content %}

    <div class="luffy-container">
        <form class="form-horizontal" action="" method="post" novalidate>
            {% csrf_token %}
            {% for field in forms %}
                <div class="form-group">
                    <label class="col-sm-2 control-label" for="{{ field.auto_id }}">{{ field.label }}</label>
                    <div class="col-sm-8">
                        {{ field }}
                        <span style="color:red;">{{ field.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 content %}

原文地址:https://www.cnblogs.com/lshedward/p/10505622.html

时间: 2024-11-11 23:28:54

权限组件(8):一级菜单的展示、增删改查和保留原参数的相关文章

MVC+Bootstrap 企业通用框架搭建--左侧导航菜单的实现--导航菜单的增删改查(3)

补充上面的点击菜单表信息: using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks; namespace SqlServer.Entity{ public class Menu_Operation { private int _operation_id; public int Operation_id { get { return

yii2-basic后台管理功能开发之二:创建CRUD增删改查

昨天实现了后台模板的嵌套,今天我们可以试着创建CRUD模型啦 刚开始的应该都是“套用”,不再打算细说,只把关键的地方指出来. CRUD即数据库增删改查操作.可以理解为yii2为我们做了一个组件,来实现基本的增删改查视图和操作. 1.创建数据库表 2.用gii生成model模型 3.用gii生成CRUD 需要注意的是CRUD生成的控制器的namespace,要和当前所在目录保持一致.否组路由会报错,找不到该页面等信息.

Mybatis使用之简单的增删改查

Mybatis使用之简单的增删改查 一:简介 主要记录最简单的数据的增删改查.下一章会有各个操作详细一点的配置说明.以Author表为例(见上一博客).Author表没有关联任何其他表.也没有特殊字段. 二:映射规则 2.1.映射文件中的sql方法与对应的XxxMapper接口中的方法映射规则: a)映射文件的namespace的值是XxxMapper接口的全限定名.即包名+接口名称 b)映射文件中表示增删改查的标签(select.insert.delete.update)的id的值是接口中方法

【黑马Android】(04)数据库的创建和sql语句增删改查/LinearLayout展示列表数据/ListView的使用和BaseAdater/内容提供者创建

数据库的创建和sql语句增删改查 1. 加载驱动. 2. 连接数据库. 3. 操作数据库. 创建表: create table person( _id integer primary key, name varchar(20), age integer ); 添加: insert into person(name, age) values('lisi', 19); 删除: delete from person where _id = 1; 修改: update person set name =

popup的简单应用举例(具体在增删改查组件中用到)以及补充的知识点

popup的简单应用举例(具体在增删改查组件中用到)以及补充的知识点 一.首先说一下自执行函数 1. 立即执行函数是什么?也就是匿名函数 立即执行函数就是 声明一个匿名函数 马上调用这个匿名函数 2.popup的举例 点击,弹出一个新的窗口.保存完事,页面不刷新数据就返回了.(点击admin的+,弹出的框就是用popup来做的) 具体操作步骤: 1.urls.py urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/',

基于MVC和Bootstrap的权限框架解决方案 二.添加增删改查按钮

上一期我们已经搭建了框架并且加入了列表的显示, 本期我们来加入增删改查按钮 整体效果如下 HTML部分,在HTML中找到中意的按钮按查看元素,复制HTML代码放入工程中 <a class="btn btn-small element" data-original-title="新增" href="/Customer/Add" data-toggle="tooltip" data-placement="top&q

JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查

前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来.最近项目打算正式将ko用起来,于是乎对ko和bootstraptable做了一些封装,在此分享出来供园友们参考.封装思路参考博客园大神萧秦,如果园友们有更好的方法,欢迎讨论. KnockoutJS系列文章: JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一) JS组件

(写给像我一样刚离开校园进入公司的小菜鸟)在领域架构下,如何实现简单的展示页面以及增删改查(第一步)

第一次到公司,现在进行的项目已经开始了有一段时间了,底层架构早已搭建好,经过一段时间熟悉,现在为自己记录一下 大致结构如此图所示,我们需要完成的任务只需要涉及三个类库Domain,Model,Web 流程如下: 1.Model: 1.1 Entities文件=>>新建实体模型 2.Domain: 2.0 Domain类库下有个模型工厂文件件ModelCreateFactory在里面添加实体模型与数据模型转换的TSETZAaaaModelFactory.cs 代码如下 1 using Syste

JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(二)

前言:上篇 JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一) 介绍了下knockout.js的一些基础用法,由于篇幅的关系,所以只能分成两篇,望见谅!昨天就觉得应该快点完成下篇,要不然有点标题党的感觉,思及此,博主心有不安,于是加班赶出了下篇.如果你也打算用ko去做项目,且看看吧! 一.效果预览 其实也没啥效果,就是简单的增删改查,重点还是在代码上面,使用ko能够大量节省界面DOM数据绑定的操作.下面是整个整个增删改查逻辑的js代码: 页面效果: 二.