from django.db import models
class A(models.Model):
name = models.CharField(max_length=32)
class B(models.Model):
title = models.CharField(max_length=32)
fk = models.ForeignKey(to="A")
# 跨表操作
a. all()
b_list = models.B.objects.all()
for item in b_list:
item.id
item.name
item.fk_id
item.fk
item.fk.name
item.fk.id
b. values()
b_list = models.B.objects.values('id','name','fk_id','fk__name')
for item in b_list:
item['id']
item['name']
item['fk_id']
item['fk__name']
c. values_list()
b_list = models.B.objects.values_list('id','name','fk_id','fk__name')
for item in b_list:
item[0] # id
item[1] # name
item[2] # fk_id
item[3] # fk__name
d. 查找名称是"Jack"的用户所有B表中的数据
models.B.objects.filter(fk__name="Jack").all()
from django.db import models
class A(models.Model):
name = models.CharField(max_length=32)
class B(models.Model):
title = models.CharField(max_length=32)
m2m = models.ManyToMany(to="A")
PS: 自动会生成第3张表
a. 在A和B表中各插入2条数据
models.A.objects.create(name="Jack")
models.A.objects.create(name="Mary")
models.A.objects.create(title="IT")
models.A.objects.create(title="CTO")
b. CTO和['Jack','Mary']创建关系
obj = models.B.objects.get(title="CTO")
obj.m2m.add(1) # 此处可以写id也可以写关联的A表中的obj
obj.m2m.add(2)
c. 查找CTO的关联的人
obj = models.B.objects.get(title="CTO")
obj.m2m.all() # 得到一个QuerySet列表,内容为A表中的对象
class MiddleWare:
# 所有的resquest请求都需要经过该方法,且该方法返回值为None时,继续请求下一个中间件
def process_request(self,request):
pass
def process_response(self,request,response):
pass
注: 中间件编写完成后需要在settings文件中进行注册使用,注册时注意中间件顺序
格式:
re.match(pattern, string, flags=0)
print(re.match('com','comwww.runcomoob').group())
print(re.match('com','Comwww.runcomoob',re.I).group())
执行结果如下:
com
com
</br>
5个类6张表
class Menu(models.Models):
"""
菜单表
"""
title = models.CharField(max_length=32,verbose_name='菜单标题')
# django admin后台显示用
class Meta:
verbose_name_plural = "菜单表"
# 重写__str__方法,实例化后的对象将以字符串的形式展示,但实际是一个obj,所以,请不要相信你的眼睛,必要时使用type(arg)进行验证
def __str__(self):
return self.title
class Group(models.Model):
"""
权限组
"""
caption = models.CharField(max_length=32, verbose_name="组名称")
menu = models.ForeignKey(to="Menu", default=1, blank=True, verbose_name="关联的菜单")
class Meta:
verbose_name_plural = "权限组"
def __str__(self):
return self.caption
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(max_length=32, verbose_name="标题")
url = models.CharField(max_length=128, verbose_name="含正则的URL")
# menu_gp为null说明是title为菜单项
menu_gp = models.ForeignKey(to="Permission", null=True, blank=True, verbose_name="默认选中的组内权限ID", related_name="pm")
code = models.CharField(max_length=16, verbose_name="权限码")
group = models.ForeignKey(to="Group", blank=True, verbose_name="所属组")
class Meta:
verbose_name_plural = "权限表"
def __str__(self):
return self.title
class User(models.Model):
"""
用户表
"""
username = models.CharField(max_length=32, verbose_name="用户名")
password = models.CharField(max_length=64, verbose_name="密码")
email = models.CharField(max_length=32, verbose_name="邮箱")
roles = models.ManyToManyField(to="Role", blank=True, verbose_name="用户关联的角色")
class Meta:
verbose_name_plural = "用户表"
def __str__(self):
return self.username
class Role(models.Model):
"""
角色表
"""
title = models.CharField(max_length=32, verbose_name="角色名称")
permissions = models.ManyToManyField(to="Permission", blank=True, verbose_name="角色关联的权限")
class Meta:
verbose_name_plural = "角色表"
def __str__(self):
return self.title
在文件末尾添加配置信息
vim projectname/settings.py
# ########################### 权限管理相关 ###########################3
PERMISSION_MENU_KEY = "asdkjalsdf9uajsdf"
PERMISSION_URL_DICT_KEY = "iujmsufnsdflsdkf"
VALID_URL= [
'^/login/',
"^/admin*"
]
#cat rbac/service/init_permission.py
from django.conf import settings
def init_permission(request,user):
"""
用户权限信息初始化,获取当前用户所有权限信息,并保存到Session中
此处的request以及user参数均为对象,user为登陆成功时在数据库中查询到的user对象
:param request:
:param user:
:return:
"""
# 去空去重
permission_list = user.roles.filter(permissions__id__isnull=False).values(
'permissions__id',
'permissions__title', # 用户列表
'permissions__url',
'permissions__code',
'permissions__menu_gp_id', # 组内菜单ID,Null表示是菜单
'permissions__group_id', # 权限的组ID
'permissions__group__menu_id', # 当前权限所在组的菜单ID
'permissions__group__menu__title', # 当前权限所在组的菜单名称
).distinct()
# 菜单相关配置,在inclusion_tag中使用
menu_permission_list= []
for item in permission_list:
tpl = {
'id': item['permissions__id'],
'title': item['permissions__title'],
'url': item['permissions__url'],
'menu_gp_id': item['permissions_menu_gp_id'],
'menu_id': item['permissions__group__menu_id'],
'menu_title': item['permissions__group__menu__title']
}
menu_permission_list.append(tpl)
request.session[settings.PERMISSION_MENU_KEY] = menu_permission_list
# 形如
"""
{"url": "/host/","menu_title": "主机管理","title": "主机列表","id": 1,"menu_gp_id": null,"menu_id": 1},
{"url": "/host/add/","menu_title": "主机管理","title": "添加主机","id": 2,"menu_gp_id": 1,"menu_id": 1},
{"url": "/host/(\\d+)/delete/","menu_title": "主机管理","title": "删除主机","id": 3,"menu_gp_id": 1,"menu_id": 1},
{"url": "/host/(\\d+)/change/","menu_title": "主机管理","title": "修改主机","id": 4,"menu_gp_id": 1,"menu_id": 1}
{"url": "/userinfo/","menu_title": "用户管理","title": "用户列表","id": 5,"menu_gp_id": null,"menu_id": 2},
{"url": "/userinfo/add/","menu_title": "用户管理","title": "添加用户","id": 6,"menu_gp_id": 5,"menu_id": 2},
......
"""
# 权限相关,中间件使用
permission_dict = {}
for item in permission_list:
group_id = item['permissions__group_id']
code = item['permissions__code']
url = item['permissions__url']
if group_id in permission_dict:
permission_dict[group_id]['codes'].append(code)
permission_dict[group_id]['urls'].append(url)
else:
permission_dict[group_id] = {"codes": [code, ], "urls": [url, ]}
request.session[settings.PERMISSION_URL_DICT_KEY] = permission_dict
# 形如
"""
{
"1": {
"codes": ["list","add","delete","edit"],
"urls": ["/host/","/host/add/","/host/(\\d+)/delete/","/host/(\\d+)/change/"]
},
"2": {
"codes": ["list","add","delete","change"],
"urls": ["/userinfo/","/userinfo/add/","/userinfo/(\\d+)/delete/","/userinfo/(\\d+)/change/"]
}
}
"""
注: 用户登陆成功后进行初始化权限信息,在处理用户权限时需要进行数据去重
# 菜单List request.session[settings.PERMISSION_MENU_KEY]
[
{
"url": "/host/",
"menu_title": "主机管理",
"title": "主机列表",
"id": 1,
"menu_gp_id": null,
"menu_id": 1
},
{
"url": "/host/add/",
"menu_title": "主机管理",
"title": "添加主机",
"id": 2,
"menu_gp_id": 1,
"menu_id": 1
},
{
"url": "/host/(\\d+)/delete/",
"menu_title": "主机管理",
"title": "删除主机",
"id": 3,
"menu_gp_id": 1,
"menu_id": 1
},
{
"url": "/host/(\\d+)/change/",
"menu_title": "主机管理",
"title": "修改主机",
"id": 4,
"menu_gp_id": 1,
"menu_id": 1
},
{
"url": "/userinfo/",
"menu_title": "用户管理",
"title": "用户列表",
"id": 5,
"menu_gp_id": null,
"menu_id": 2
},
{
"url": "/userinfo/add/",
"menu_title": "用户管理",
"title": "添加用户",
"id": 6,
"menu_gp_id": 5,
"menu_id": 2
},
{
"url": "/userinfo/(\\d+)/delete/",
"menu_title": "用户管理",
"title": "删除用户",
"id": 7,
"menu_gp_id": 5,
"menu_id": 2
},
{
"url": "/userinfo/(\\d+)/change/",
"menu_title": "用户管理",
"title": "修改用户",
"id": 8,
"menu_gp_id": 5,
"menu_id": 2
}
]
# 权限Dict request.session[settings.PERMISSION_URL_DICT_KEY]
{
"1": {
"codes": [
"list",
"add",
"delete",
"edit"
],
"urls": [
"/host/",
"/host/add/",
"/host/(\\d+)/delete/",
"/host/(\\d+)/change/"
]
},
"2": {
"codes": [
"list",
"add",
"delete",
"change"
],
"urls": [
"/userinfo/",
"/userinfo/add/",
"/userinfo/(\\d+)/delete/",
"/userinfo/(\\d+)/change/"
]
}
}
cat rbac/middleware/rbac.py
from django.shortcuts import redirect,HttpResponse
from django.conf import settings
# 在后续版本中可能会被废弃,故在此直接引入
#from django.utils.deprecation import MiddlewareMixin
class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
class RbacMiddleware(MiddlewareMixin):
def process_request(self,request):
# 1. 当前请求URL
current_request_url = request.path_info
# 2. 处理白名单,如login及admin页面需开放访问权限,根据实际情况而定
for url in settings.VALID_URL_LIST:
if re.match(url,current_request_url):
return None
# 3. 获取session中保存的权限信息
permission_dict = request.session.get(settings.PERMISSION_MENU_LIST)
if not permission_dict:
# 登陆页面
return redirect(settings.RBAC_LOGIN_URL)
flag = False
for group_id, values in permission_dict.items():
for url in values['urls']:
regex = settings.URL_FORMAT.format(url)
if re.match(regex, current_request_url):
flag = True
break
if flag:
break
if not flag:
# 无权访问页面,可以直接redirect
return HttpResponse('无权访问')
cat /templates/menu_tpl.html
{% for k,item in page_menu.items %}
<div class="item">
<div class="header">{{ item.menu_title }}</div>
<div class="permission {{ item.opened }}">
{% for v in item.children %}
<a href="{{ v.url }}" class="{{ v.opened }}">{{ v.title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
cat rbac/templatetags/rbac.py
from django.template import Library
from django.conf import settings
import re
register = Library()
@register.inclusion_tag('menu_tpl.html')
def menu_html(request):
page_menu = {}
current_url = request.path_info
permission_menu_list = request.session[settings.PERMISSION_MENU_LIST]
for item in permission_menu_list:
url = item['url']
regex = settings.URL_FORMAT.format(url)
if re.match(regex, current_url):
item['opened'] = True
menu_id = item['menu_id']
opened = "active" if item.get('opened') else ""
child = {'title': item['title'], 'url': item['url'], 'opened': opened}
if menu_id in page_menu:
page_menu[menu_id]['children'].append(child)
if opened:
page_menu[menu_id]['opened'] = opened
else:
page_menu[menu_id] = {'opened': opened, 'menu_title': item['menu_title'], 'children': [child, ]}
return {'page_menu':page_menu}
# 上文中的menu_html函数依赖request参数,此处需要传入
{% load rbac %}
{% menu_html request %}
注: 自定义tags只支持传入1个参数
project/settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'rbac.middleware.rbac.RbacMiddleware',
]
在Django Admin中维护rbac的权限系统并使用
至此,基于role实现的rbac组件基本开发完成,在Django中作为app引入在settings文件中注册后就可以使用生效了