一、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、二级菜单的默认显示、动态显示按钮权限的更多相关文章

  1. django中动态生成二级菜单

    一.动态显示二级菜单 1.修改权限表结构 (1)分析需求,要求左侧菜单如下显示: 客户管理: 客户列表 账单管理: 账单列表 (2)修改rbac下的models.py,修改后代码如下: from dj ...

  2. (转载)ExpandableListView 安卓二级菜单

    ExpandableListView 安卓二级菜单   ExpandableListView可以显示一个视图垂直滚动显示两级列表中的条目,这不同于列表视图(ListView).ExpandableLi ...

  3. iview可收缩侧边菜单实现(支持二级菜单)

    想用iview做一个可以伸缩的侧边菜单栏,效果如下: 1.侧边栏收缩前:可以通过点击菜单分类展开子菜单项: 2.可以让用户点击图标动态收缩菜单栏: 3.侧边栏收缩后:只显示菜单分类的图标,鼠标放置在菜 ...

  4. Spring Cloud实战: 基于Spring Cloud Gateway + vue-element-admin 实现的RBAC权限管理系统,实现网关对RESTful接口方法权限和自定义Vue指令对按钮权限的细粒度控制

    一. 前言 信我的哈,明天过年. 这应该是农历年前的关于开源项目 的最后一篇文章了. 有来商城 是基于 Spring Cloud OAuth2 + Spring Cloud Gateway + JWT ...

  5. Spring Cloud实战 | 第十一篇:Spring Cloud Gateway 网关实现对RESTful接口权限控制和按钮权限控制

    一. 前言 hi,大家好,这应该是农历年前的关于开源项目 的最后一篇文章了. 有来商城 是基于 Spring Cloud OAuth2 + Spring Cloud Gateway + JWT实现的统 ...

  6. vue做页面按钮权限--分析

    import * as types from '../mutation-types' const state = { btnCode: getBtnCode(), } const mutations ...

  7. 巨蟒django之权限7:动态生成一级&&二级菜单

    内容回顾: . 权限的控制 . 表结构设计 存权限的信息 用户表 - name 用户名 - pwd 密码 - roles 多对多 角色表 - name - permissions 多对多 权限表 - ...

  8. Django - 权限(3)- 动态显示二级菜单

    一.动态显示二级菜单 上篇随笔中,我们实现了动态显示一级菜单,现在考虑这样一种情况,用户的菜单权限比较多,这个时候全部并列展现在左侧菜单就不合适了,所以,现在有这样一个需求,即把用户的菜单权限分类,划 ...

  9. django自定义rbac权限组件(二级菜单)

    一.目录结构 二.表结构设计 model.py from django.db import models # Create your models here. class Menu(models.Mo ...

随机推荐

  1. redis 服务相关

    一 什么是redis Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开 ...

  2. vim插件管理器的安装和配置-windows

    # vim插件管理器的安装和配置-windows ### 前言------------------------------ vim做一框功能强大的编辑器,扩展功能令人称奇,插件机制非常灵活- 本篇推荐 ...

  3. love2d教程33--window模块

    --love的window模块比较简单,直接贴代码了function love.load() io.stdout:setvbuf("no") -- 设置io为无缓存模式 --获取显 ...

  4. JVM Specification 9th Edition (4) Chapter 3. Compiling for the Java Virtual Machine

    Chapter 3. Compiling for the Java Virtual Machine 内容列表 3.1. Format of Examples 3.2. Use of Constants ...

  5. openssl基础

    OpenSSL 是一个安全套接字层密码库,囊括主要的密码算法.常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用. OpenSSL is an open source ...

  6. flutter 环境

    以下内容copy 于 https://www.cnblogs.com/lovelyYakir/p/7610396.html 原文请参考此 我只是做个记录 第一步:安装Git 你需要安装Git作为Flu ...

  7. Service Mesh服务网格新生代--Istio(转)

    万字解读:Service Mesh服务网格新生代--Istio  官网地址:https://preliminary.istio.io/zh/docs/concepts/security/ Servic ...

  8. ddd 架构设计——abp

    一.为什么要分层 分层架构是所有架构的鼻祖,分层的作用就是隔离,不过,我们有时候有个误解,就是把层和程序集对应起来,就比如简单三层架构中,在你的解决方案中,一般会有三个程序集项目:XXUI.dll.X ...

  9. 【BZOJ】3300: [USACO2011 Feb]Best Parenthesis(模拟)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3300 这个细节太多QAQ 只要将所有的括号'('匹配到下一个')'然后dfs即可 简单吧,,, #i ...

  10. linux 知识点拾遗

    文件名称 在 Linux 底下,每个档案或文件夹的文件名称最长能够到达 255 的字符,加上完整路径时,最长可达 4096 个字符; 因为 Linux 在文字接口下的一些指令操作关系,一般来说,您在设 ...