一级菜单

1.model设计

from django.db import models            
class Permission(models.Model):
    url = models.CharField(max_length=64, verbose_name='权限')
    title = models.CharField(max_length=32, verbose_name='标题')  # 备注这个url是做什么的
    icon = models.CharField(max_length=64, null=True, blank=True,verbose_name='图标') # 存放菜单前面的图标
    is_menu = models.BooleanField(default=False) # 用来判断(此url)是否是菜单
            
    def __str__(self):
        return self.title
            
    class Meta:
        verbose_name_plural = '权限管理'
            
            
class Role(models.Model):
    name = models.CharField(max_length=32, verbose_name='角色')
    permissions = models.ManyToManyField(Permission, verbose_name='角色拥有的权限', blank=True)
            
    def __str__(self):
        return self.name
            
    class Meta:
        verbose_name_plural = '角色管理'
            
            
class User(models.Model):
    username = models.CharField(max_length=32, verbose_name='用户名',unique=True)
    password = models.CharField(max_length=32, verbose_name='密码')
    roles = models.ManyToManyField(Role, verbose_name='用户的角色', blank=True)
            
    def __str__(self):
        return self.username
            
    class Meta:
        verbose_name_plural = '用户管理'
models.py

注意:数据迁移后,去admin后台界面,设置菜单

2. views.py文件 登录函数中登陆成功后获取当前用户的权限的所有信息(url,title,icon,is_menu)

如:

per = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
                                                                    'permissions__title',
                                                                    'permissions__icon',
                                                                    'permissions__is_menu',
                                                                    ).distinct()
print(per)

## 打印per的数据
QuerySet[{
             'permissions__url': '/customer/list/',
             'permissions__title': '客户列表',
             'permissions__icon': 'fa-connectdevelop',
             'permissions__is_menu': True},
         {
             'permissions__url': '/customer/add/',
             'permissions__title': '添加客户',
             'permissions__icon': None,
             'permissions__is_menu': False},
         {
             'permissions__url': '/customer/edit/(?P<cid>\\d+)/',
             'permissions__title': '编辑客户',
             'permissions__icon': None,
             'permissions__is_menu': False},
         {
             'permissions__url': '/customer/del/(?P<cid>\\d+)/',
             'permissions__title': '删除客户',
             'permissions__icon': None,
             'permissions__is_menu': False},
         {
             'permissions__url': '/payment/list/',
             'permissions__title': '账单列表',
             'permissions__icon': 'fa-code-fork',
             'permissions__is_menu': True},
         {
             'permissions__url': '/payment/add/',
             'permissions__title': '添加账单',
             'permissions__icon': None,
             'permissions__is_menu': False},
         {
             'permissions__url': '/payment/edit/(?P<pid>\\d+)/',
             'permissions__title': '编辑账单',
             'permissions__icon': None,
             'permissions__is_menu': False},
         {
             'permissions__url': '/payment/del/(?P<pid>\\d+)/',
             'permissions__title': '删除账单',
             'permissions__icon': None,
             'permissions__is_menu': False
         }]
views.py

3、从上述中的数据中挑出菜单列表

# 菜单列表
            menu = []
    
            # 权限列表
            permission = []
    
            for i in per:
                permission.append({
                    'url': i.get('permissions__url')
                })
                if i.get('permissions__is_menu'): # 如果permissions__is_menu为True就是菜单,放到菜单列表中
                    menu.append({
                        'url': i.get('permissions__url'),
                        'title': i.get('permissions__title'),
                        'icon': i.get('permissions__icon')
                    })
View Code

4. 为了使两个列表(session中作为值)在session中对应的键的名字可以由用户改变,在名字中设置两个常量

settings.py文件:

# 存放权限的session的key
PERMISSION_SESSION_KEY = 'permission'

# 存放菜单的session的key
MENU_SESSION_KEY = 'menu'
View Code

5. 将两个列表放到session中

登录函数,第三步后面

from django.conf import settings
request.session[settings.PERMISSION_SESSION_KEY] = permission
request.session[settings.MENU_SESSION_KEY] = menu
View Code

6. 权限验证的中间件中获取session中的PERMISSION_SESSION_KEY进行权限验证

permission = request.session.get(settings.PERMISSION_SESSION_KEY)
# 权限验证(url,中间件中获取的当前url地址如:通过request.path_info获取)
for i in permission:
    if re.match(r'^{}$'.format(i['url']), url):
    return
View Code

7. incluesion_tags(模板中调用函数)

目的:用于前端菜单的展示而编写的函数
函数作用:为菜单添加样式,然后通过装饰器交给特定的模板,
具体如下:

my_tags.py文件

# -*- coding: utf-8 -*-
# __author__ = "maple"
            
from django import template
from django.conf import settings
import re
register = template.Library()
@register.inclusion_tag('menu_tag.html') # 讲给这个模板进行渲染
def menu(request):
    url = request.path_info  # 获取当前url地址
    menu_list = request.session[settings.MENU_SESSION_KEY]  # 获取session中的菜单url地址

    # 添加激活的样式
    for i in menu_list:
        if re.match(f'^{i["url"]}$',url): # 判断,如果当前页面地址栏中的url地址是菜单中的地址
            i['class'] = 'active'         # 就给菜单中的地址加上active类选择器。
            break    
            return {'menu_list':menu_list}          
            
    #此时,menu_list列表中的数据为:(前端页面当前停留在客户列表菜单上)
    [{
    'url': '/customer/list/',
    'title': '客户列表',
    'icon': 'fa-connectdevelop',
    'class': 'active'},
    {
    'url': '/payment/list/',
    'title': '账单列表',
    'icon': 'fa-code-fork'
    }]
View Code

8. menu_tag.html 渲染模板

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

9.将incluesion_tags应用到前端html页面中

<div class="left-menu">
<div class="menu-body">
{% load my_tags %} # my_tags为存放menu函数的文件名,
{% menu request %} # request为第七步中的menu函数的参数
</div>
</div>
View Code

注意:tags文件都放在templatetags目录中,目录名字不能变,位置随意。

二级菜单

1. models设计

from django.db import models


class Menu(models.Model):
    title = models.CharField(max_length=32, verbose_name='标题')
    icon = models.CharField(max_length=64, null=True, blank=True,verbose_name='图标')
    weight = models.IntegerField(default=0, verbose_name='权重')

    def __str__(self):
        return self.title


class Permission(models.Model):
    url = models.CharField(max_length=64, verbose_name='权限')
    title = models.CharField(max_length=32, verbose_name='标题')
    menu = models.ForeignKey(Menu, null=True, blank=True,verbose_name='菜单')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = '权限管理'


class Role(models.Model):
    name = models.CharField(max_length=32, verbose_name='角色')
    permissions = models.ManyToManyField(Permission, verbose_name='角色拥有的权限', blank=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '角色管理'


class User(models.Model):
    username = models.CharField(max_length=32, verbose_name='用户名', unique=True)
    password = models.CharField(max_length=32, verbose_name='密码')
    roles = models.ManyToManyField(Role, verbose_name='用户的角色', blank=True)

    def __str__(self):
        return self.username

    class Meta:
        verbose_name_plural = '用户管理'
models.py

2. settings中配置seesion中的键的名字,让key更灵活

# 存放权限的session的key
PERMISSION_SESSION_KEY = 'permission'

# 存放菜单的session的key
MENU_SESSION_KEY = 'menu'
settings.py

3. 用户登录后,获取登录用户的权限以及菜单信息,并放入session中

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        obj = models.User.objects.filter(username=username, password=password).first()
        if not obj:
            error = '用户名或密码错误'
            return render(request, 'login.html', locals())

        # 获取权限的信息
        per = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
                                                                            'permissions__title',
                                                                            'permissions__menu__title',
                                                                            'permissions__menu__icon',
                                                                            'permissions__menu__id',
                                                                            ).distinct()
        #-----------------------------------------------------------------
        # 菜单列表(后面转列表)
        dic = {}
        # 权限列表
        permission = []
        for i in list(per):
            # 获取当前用户的权限列表
            print(i, type(i))
            permission.append({
                'url': i.get('permissions__url')
            })
            # 获取当前菜单列表
            if i['permissions__menu__id']:
                if dic.get(i['permissions__menu__id']):
                    dic[i['permissions__menu__id']]['children'].append(
                        {'url': i['permissions__url'], 'title': i['permissions__title']})
                else:
                    dic[i['permissions__menu__id']] = {'title': i['permissions__menu__title'],
                                                       'icon': i['permissions__menu__icon'],
                                                       'children': [
                                                           {'url': i['permissions__url'],
                                                            'title': i['permissions__title']}
                                                       ]
                                                       }

        request.session[settings.PERMISSION_SESSION_KEY] = permission
        request.session[settings.MENU_SESSION_KEY] = dic
        #-----------------------------------------------------------------
        request.session['is_login'] = 1
        return redirect('index')
    return render(request, 'login.html', locals())
views.py

4. 中间件中提取权限列表

# -*- coding: utf-8 -*-
# __author__ = "maple"
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import redirect, HttpResponse
import re


class AuthMiddleware(MiddlewareMixin):
    def process_request(self, request):
        url = request.path_info
        # 白名单 :登陆、注册、admin
        for i in settings.WHITE_LIST:
            if re.match(i, url):
                return

        # 登陆验证
        is_login = request.session.get('is_login')
        if is_login != 1:
            return redirect('login')

        # 免认证 登录之后所有人都能访问的地址
        for i in settings.PUBLIC_LIST:
            if re.match(i, url):
                return

        # 权限验证
        permission = request.session.get(settings.PERMISSION_SESSION_KEY)
        for i in permission:
            if re.match(r'^{}$'.format(i['url']), url):
                return
        return HttpResponse('没有权限,请联系管理员!')
中间件.py

5.自定义标签获取菜单信息,通过函数,动态生成二级菜单

# -*- coding: utf-8 -*-
# __author__ = "maple"
from django import template
from django.conf import settings
import re

register = template.Library()

@register.inclusion_tag('menu_tag.html')
def menu(request):
    url = request.path_info
    menu_list = request.session[settings.MENU_SESSION_KEY]

    # 添加激活的样式
    for k, v in menu_list.items():
        print(k,v)
        """
        v ={'title': '客户管理', 
        'icon': 'fa-connectdevelop', 
        'children': [{'url': '/customer/list/', 'title': '客户列表'}]}
        """
        for i in v["children"]:
            if re.match(r'^{}$'.format(i["url"]), url):
                i["class"] = 'active'
                break
    return {'menu_list': menu_list}
my_tags.py
<div class="static-menu">
    {% for one_menu_id,two_menu in menu_list.items %}
        <a href="" >
            <span class="icon-wrap"><i class="fa {{ two_menu.icon }}"></i></span> {{ two_menu.title }}</a>
        {% for child in two_menu.children %}
            <a href="{{ child.url }}" class="{{ child.class }}">
                <span class="icon-wrap"><i class="fa {{ child.icon }}"></i></span> {{ child.title }}</a>
        {% endfor %}
    {% endfor %}
</div>
menu_tag.html

7.应用自定义标签到项目中

    <div class="left-menu">
        <div class="menu-body">
            {% load my_tags %}
            {% menu request %}
        </div>
    </div>
View Code

8.使用jQuery实现隐藏二级菜单,点击一级菜单,出现二级菜单

略....................

目录分布

 

 

 

posted on 2020-01-09 20:01  kindvampire  阅读(829)  评论(0编辑  收藏  举报