Django - 权限(4)- queryset、二级菜单的默认显示、动态显示按钮权限
一、queryset
Queryset是django中构建的一种数据结构,ORM查询集往往是queryset数据类型,我们来进一步了解一下queryset的特点。
1、可切片
使用Python 的切片语法来限制查询集记录的数目。它等同于SQL 的LIMIT 和OFFSET 子句。
>>> Entry.objects.all()[:5] # (LIMIT 5)
>>> Entry.objects.all()[5:10] # (OFFSET 5 LIMIT 5)
不支持负的索引(例如Entry.objects.all()[-1])。通常,查询集的切片返回一个新的查询集(它不会再执行sql查询语句)。
2、可迭代
articleList = models.Article.objects.all()
for article in articleList:
print(article.title)
3、惰性查询
查询集是惰性执行的 —— 创建查询集不会带来任何数据库的访问。你可以将过滤器保持一整天,直到查询集需要求值时,Django 才会真正运行这个查询。
queryResult = models.Article.objects.all() # not hits database
print(queryResult) # hits database
for article in queryResult:
print(article.title) # hits database
一般来说,只有在“请求”查询集的结果时才会到数据库中去获取它们。当你确实需要结果时,查询集通过访问数据库来求值。
4、缓存机制
每个查询集都包含一个缓存来最小化对数据库的访问。理解它是如何工作的将让你编写最高效的代码。
在一个新创建的查询集中,缓存为空。首次对查询集进行求值 —— 同时发生数据库查询 ——Django 将保存查询的结果到查询集的缓存中并返回明确请求的结果(例如,如果正在迭代查询集,则返回下一个结果)。接下来对该查询集的求值将重用缓存的结果。
请牢记这个缓存行为,因为对查询集使用不当的话,它会坑你的。例如,下面的语句创建两个查询集,对它们求值,然后扔掉它们:
print([a.title for a in models.Article.objects.all()])
print([a.create_time for a in models.Article.objects.all()])
这意味着相同的数据库查询将执行两次,显然倍增了你的数据库负载。同时,还有可能两个结果列表并不包含相同的数据库记录,因为在两次请求期间有可能有Article被添加进来或删除掉。为了避免这个问题,只需保存查询集并重新使用它,如下:
queryResult = models.Article.objects.all()
print([a.title for a in queryResult])
print([a.create_time for a in queryResult]
何时查询集不会被缓存?
查询集不会永远缓存它们的结果。当只对查询集的部分进行求值时会检查缓存,如果这个部分不在缓存中,那么接下来查询返回的记录都将不会被缓存。所以,这意味着使用切片或索引来限制查询集将不会填充缓存。例如,重复获取查询集对象中一个特定的索引将每次都查询数据库,如下:
>>> queryset = Entry.objects.all()
>>> print queryset[5] # Queries the database
>>> print queryset[5] # Queries the database again
然而,如果已经对全部查询集求值过,则将检查缓存,如下:
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print queryset[5] # Uses cache
>>> print queryset[5] # Uses cache
下面是一些其它例子,它们会使得全部的查询集被求值并填充到缓存中:
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)
注意:简单地打印查询集不会填充缓存,如下:
queryResult = models.Article.objects.all()
print(queryResult) # hits database
print(queryResult) # hits database
5、exists()与iterator()方法
(1)exists()
简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些数据!为了避免这个,可以用exists()方法来检查是否有数据,如下:
if queryResult.exists():
# SELECT (1) AS "a" FROM "blog_article" LIMIT 1; args=()
print("exists...")
(2)iterator()
当queryset非常巨大时,cache会成为问题。
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就将其丢弃。如下:
objs = Book.objects.all().iterator()
# iterator()可以一次只从数据库获取少量数据,这样可以节省内存
for obj in objs:
print(obj.title)
# BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
for obj in objs:
print(obj.title)
当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询。
总结:queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能会造成额外的数据库查询。因此,要根据场景灵活使用。
二、二级菜单的默认显示
1、修改my_tags.py文件,代码如下:
@register.inclusion_tag("menu.html")
def get_menu_styles(request):
permission_menu_dict = request.session.get("permission_menu_dict")
print("permission_menu_dict", permission_menu_dict) for val in permission_menu_dict.values():
for item in val["children"]:
val["class"] = "hide"
ret = re.search("^{}$".format(item["url"]), request.path)
if ret:
val["class"] = "" return {"permission_menu_dict": permission_menu_dict}
2、修改menu.html文件,代码如下:
<div class="multi-menu">
{% for item in permission_menu_dict.values %}
<div class="item">
<div class="title">
<i class="{{ item.menu_icon }}"></i>{{ item.menu_title }}
</div>
<div class="body {{ item.class }}">
{% for foo in item.children %}
<a href="{{ foo.url }}">{{ foo.title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
三、动态显示按钮权限
除了菜单权限,还有按钮权限,比如添加用户(账单),编辑用户(账单),删除用户(账单),这些不是菜单选项,而是以按钮的形式在页面中显示,但不是所有的用户都有所有的按钮权限,我们需要在用户不拥有这个按钮权限时就不要显示这个按钮,下面介绍一下思路和关键代码。
1、在permission表中增加一个字段name,permission模型类如下:
class Permission(models.Model):
"""
权限表
"""
url = models.CharField(verbose_name='含正则的URL', max_length=32)
title = models.CharField(verbose_name='标题', max_length=32)
menu = models.ForeignKey(verbose_name='标题', to="Menu", on_delete=models.CASCADE, null=True)
name = models.CharField(verbose_name='url别名', max_length=32, default="") def __str__(self):
return self.title
2、将权限别名列表注入session,setsession.py中代码如下:
def initial_session(user_obj, request):
"""
将当前登录人的所有权限url列表和
自己构建的所有菜单权限字典和
权限表name字段列表注入session
:param user_obj: 当前登录用户对象
:param request: 请求对象HttpRequest
"""
# 查询当前登录人的所有权限列表
ret = Role.objects.filter(user=user_obj).values('permissions__url',
'permissions__title',
'permissions__name',
'permissions__menu__title',
'permissions__menu__icon',
'permissions__menu__id').distinct()
permission_list = []
permission_names = []
permission_menu_dict = {}
for item in ret:
# 获取用户权限列表用于中间件中权限校验
permission_list.append(item['permissions__url'])
# 获取权限表name字段用于动态显示权限按钮
permission_names.append(item['permissions__name']) menu_pk = item['permissions__menu__id']
if menu_pk:
if menu_pk not in permission_menu_dict:
permission_menu_dict[menu_pk] = {
"menu_title": item["permissions__menu__title"],
"menu_icon": item["permissions__menu__icon"],
"children": [
{
"title": item["permissions__title"],
"url": item["permissions__url"],
}
],
}
else:
permission_menu_dict[menu_pk]["children"].append({
"title": item["permissions__title"],
"url": item["permissions__url"],
})
print('权限列表', permission_list)
print('菜单权限', permission_menu_dict)
# 将当前登录人的权限列表注入session中
request.session['permission_list'] = permission_list
# 将权限表name字段列表注入session中
request.session['permission_names'] = permission_names
# 将当前登录人的菜单权限字典注入session中
request.session['permission_menu_dict'] = permission_menu_dict
3、自定义过滤器,my_tags.py中部分代码如下:
@register.filter
def has_permission(btn_url, request):
permission_names = request.session.get("permission_names")
return btn_url in permission_names
4、使用过滤器,模板(如customer_list.html)中部分权限按钮代码如下:
<div class="btn-group">
{% load my_tags %}
{% if "customer_add"|has_permission:request %}
<a class="btn btn-default" href="/customer/add/">
<i class="fa fa-plus-square" aria-hidden="true"></i> 添加客户
</a>
{% endif %}
</div>
注意:permission表中新增name字段与url的别名没有关系,当然也可以起一样的名字,心里明白他们其实并无关系即可。
Django - 权限(4)- queryset、二级菜单的默认显示、动态显示按钮权限的更多相关文章
- django中动态生成二级菜单
一.动态显示二级菜单 1.修改权限表结构 (1)分析需求,要求左侧菜单如下显示: 客户管理: 客户列表 账单管理: 账单列表 (2)修改rbac下的models.py,修改后代码如下: from dj ...
- (转载)ExpandableListView 安卓二级菜单
ExpandableListView 安卓二级菜单 ExpandableListView可以显示一个视图垂直滚动显示两级列表中的条目,这不同于列表视图(ListView).ExpandableLi ...
- iview可收缩侧边菜单实现(支持二级菜单)
想用iview做一个可以伸缩的侧边菜单栏,效果如下: 1.侧边栏收缩前:可以通过点击菜单分类展开子菜单项: 2.可以让用户点击图标动态收缩菜单栏: 3.侧边栏收缩后:只显示菜单分类的图标,鼠标放置在菜 ...
- Spring Cloud实战: 基于Spring Cloud Gateway + vue-element-admin 实现的RBAC权限管理系统,实现网关对RESTful接口方法权限和自定义Vue指令对按钮权限的细粒度控制
一. 前言 信我的哈,明天过年. 这应该是农历年前的关于开源项目 的最后一篇文章了. 有来商城 是基于 Spring Cloud OAuth2 + Spring Cloud Gateway + JWT ...
- Spring Cloud实战 | 第十一篇:Spring Cloud Gateway 网关实现对RESTful接口权限控制和按钮权限控制
一. 前言 hi,大家好,这应该是农历年前的关于开源项目 的最后一篇文章了. 有来商城 是基于 Spring Cloud OAuth2 + Spring Cloud Gateway + JWT实现的统 ...
- vue做页面按钮权限--分析
import * as types from '../mutation-types' const state = { btnCode: getBtnCode(), } const mutations ...
- 巨蟒django之权限7:动态生成一级&&二级菜单
内容回顾: . 权限的控制 . 表结构设计 存权限的信息 用户表 - name 用户名 - pwd 密码 - roles 多对多 角色表 - name - permissions 多对多 权限表 - ...
- Django - 权限(3)- 动态显示二级菜单
一.动态显示二级菜单 上篇随笔中,我们实现了动态显示一级菜单,现在考虑这样一种情况,用户的菜单权限比较多,这个时候全部并列展现在左侧菜单就不合适了,所以,现在有这样一个需求,即把用户的菜单权限分类,划 ...
- django自定义rbac权限组件(二级菜单)
一.目录结构 二.表结构设计 model.py from django.db import models # Create your models here. class Menu(models.Mo ...
随机推荐
- redis命令_SETEX
SETEX key seconds value 将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位). 如果 key 已经存在, SETEX 命令将覆写旧值 ...
- java -jar Incompatible argument to function
原因分析:jar包版本问题 解决方法:到工程中查看代码引用的jar包版本是多少,然后升级jar包,就可以了!
- 跟着百度学PHP[14]-PDO-优化驱动
使用方法设置预定义变量 PDO的方法/属性 PDO::beginTransaction — Initiates a transaction PDO::commit — Commits a transa ...
- spring mvc RedirectAttributes
org.springframework.web.servlet.mvc.support.RedirectAttributes org.springframework.web.servlet.mvc.s ...
- 快使用阿里云的maven仓库
自从开源中国的maven仓库挂了之后就一直在用国外的仓库,慢得想要砸电脑的心都有了.如果你和我一样受够了国外maven仓库的龟速下载?快试试阿里云提供的maven仓库,从此不在浪费生命…… 仓库地址: ...
- Android插件化开发之OpenAtlas生成插件信息列表
上一篇文章.[Android插件化开发之Atlas初体验]( http://blog.csdn.net/sbsujjbcy/article/details/47446733),简单的介绍了使用Atla ...
- LeetCode 第 231 题 (Power of Two)
LeetCode 第 231 题 (Power of Two) Given an integer, write a function to determine if it is a power of ...
- iOS项目开发实战——自己定义圆形进度提示控件
iOS中默认的进度条是水平方向的进度条,这往往不能满足我们的需求. 可是我们能够自己定义类似的圆形的进度提示控件,主要使用iOS中的画图机制来实现. 这里我们要实现一个通过button点击然后圆形进度 ...
- jquery XHTML
传送门:异步编程系列目录…… 示例源码:触碰jQuery:AJAX异步详解.rar AJAX 全称 Asynchronous JavaScript and XML(异步的 JavaScript 和 X ...
- js转译html标签
api返回的接口数据是这样的"<div>测试数据</div>" js拼接html时会过滤掉<div></div> 所以需要对< ...