今日内容:
1. 保留原搜索条件
2. 二级菜单
3. 导航条
4. 权限组件【使用文档】
内容详细:
1. 保留原搜索条件【独立】
实现代码:
simple_tag,目的是生成跳转的URL时,要携带当前的条件。
例如:
访问此地址 http://127.0.0.1:8000/crm/depart/list/?page=7,那么编辑和删除的URL应该是:
http://127.0.0.1:8000/crm/depart/edit/64/?_filter=page%3D7
http://127.0.0.1:8000/crm/depart/del/64/?_filter=page%3D7
@register.simple_tag
def memory_url(request,name,*args,**kwargs):
"""
生成URL(含条件)
:return:
"""
# /crm/depart/edit/45/?_filter=page=5&age=1
base_url = reverse(name,args=args,kwargs=kwargs)
if not request.GET:
return base_url
new_query_dict = QueryDict(mutable=True)
new_query_dict['_filter'] = request.GET.urlencode() # _filter=pagesdf5sdfagesdfsdf1
url = "%s?%s" % (base_url, new_query_dict.urlencode(),)
return url
视图调用函数生成原来的URL
例如:
在添加/编辑/删除页面完成操作之后,调用此方法生成回到列表页面的URL。
def memory_reverse(request,name,*args,**kwargs):
"""
反向生成带条件的URL
:return:
"""
url = reverse(name,args=args,kwargs=kwargs)
filter = request.GET.get('_filter')
if not filter:
return url
return "%s?%s" % (url, filter,)
在项目中应用时:
在模板中:
{% load crm %}
<a href="{% memory_url request 'depart_edit' row.id %}"><i class="fa fa-pencil-square-o" aria-hidden="true"></i></a>
在视图中:
def depart_del(request,nid):
"""
删除部门
:param request:
:param nid:
:return:
"""
models.Department.objects.filter(id=nid).delete()
return redirect(memory_reverse(request, 'depart_list'))
2. 支持二级菜单
a. 表结构的修改
- 6张表
b. 支持菜单的数据结构
[
{'id':1,'title':'xx','url':'...'},
{'id':1,'title':'xx','url':'...'},
{'id':1,'title':'xx','url':'...'},
{'id':1,'title':'xx','url':'...'},
]
[
{
id:1,
title:'父菜单1',
children:[
{'id':1,'title':'部门列表','url':...},
{'id':5,'title':'用户列表','url':...},
]
},
{
id:2,
title:'父菜单2',
children:[
]
},
]
RBAC组件:
- 中间件
- 初始化函数
- filter/inclusion_tag
- js
- css
补充:
- 有序字典
3. 对于无法做菜单的权限,访问时,应该让那个菜单默认选中?
4. 导航条
- 数据库自关联
- 反射
- inclusion_tag
3. 权限组件【使用文档】
1. 新项目创建一个Project【接口测试管理】
2. 拷贝rbac app 到新项目中。
3. 删除rbac/migrations目录下的所有问题(除__init__.py除外)
4. 接口测试管理的表结构设计。
- 用户表需要继承 rabc 中的用户表,例如:
class UserInfo(AbstractUserInfo):
"""
用户表
"""
email = models.EmailField(verbose_name='邮箱')
- settings中注册 app,例如:
INSTALLED_APPS = [
...
'api.apps.ApiConfig',
'rbac.apps.RbacConfig',
]
- 数据库迁移
5. 业务开发
- 将模板中 【动态生成菜单】 【导航条】 功能去掉。
{% get_menu request %}
{% get_breadcrumb request %}
- API开发
- 保留原搜索条件
- 分页
- 编辑/删除/添加 模板页面的重用。
- ModelForm 定制BootStrap样式
- 错误信息中文显示
- 用户管理
..
6. 使用 django admin进行权限信息的录入【权限信息】
- 创建admin的超级用户
- 权限分配
- 菜单表
- 权限表
- 角色表
- 权限角色关系表
7. 业务中添加用户管理【用户信息】
- 用户增删改查
- 用户和角色关系
简单:admin
8. 用户登录
- 视图函数示例:
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
user = request.POST.get('username')
pwd = request.POST.get('password')
user_object = models.UserInfo.objects.filter(username=user, password=pwd).first()
if not user_object:
return render(request, 'login.html', {'error': '用户名或密码错误'})
# 权限初始化
init_permission(user_object,request)
return redirect(reverse('index'))
- HTML示例:
<div style="width: 400px;margin: 0 auto;margin-top: 100px;">
<form class="form-horizontal" method="post">
{% csrf_token %}
<div class="form-group">
<label for="user" class="col-sm-3 control-label">用户名</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="user" name="username">
</div>
</div>
<div class="form-group">
<label for="pwd" class="col-sm-3 control-label">密码</label>
<div class="col-sm-9">
<input type="password" class="form-control" id="pwd" name="password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-primary">登 录</button>
<span style="color: red">{{ error }}</span>
</div>
</div>
</form>
</div>
- settings.py 配置文件:
- 保存权限信息:
RBAC_SESSION_PERMISSION_KEY = 'asdfu3nrsluidfowljer'
- 保存菜单信息:
RBAC_SESSION_MENU_KEY = 'jJASHDIOywejhASD'
9. 应用中间件
- 白名单处理
- 免权限校验的URL
- 权限校验
settings.py
# 中间件应用
MIDDLEWARE = [
....
'rbac.middleware.rbac.RbacMiddleware'
]
# 白名单(无需登录/无需权限校验)
RBAC_VALID_LIST = [
'/api/login/',
'/admin.*'
]
# 免校验(需登录/无需权限校验)
RBAC_NO_PERMISSION_LIST = [
'/api/index/'
]
# 父权限的名称(用于做菜单的默认展开)
RBAC_CURRENT_PARENT_NAME = "dufs3sffdsdfsdf"
# 导航条
RBAC_RECORD_LIST = "ffsf3fsdfsdf"
10. 在 layout.html 中应用 动态菜单和导航条。
{% get_menu request %}
{% get_breadcrumb request %}
11. 粒度控制到按钮
{% extends 'layout.html' %}
{% load api %}
{% load rbac %}
{% block content %}
<div style="margin: 5px;">
<div>
{% if request|has_permission:'interface_add' %}
<a href="{% memory_url request 'interface_add' %}" class="btn btn-primary">添加</a>
{% endif %}
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>序号</th>
<th>接口名称</th>
<th>URL</th>
{% if request|has_permission:'interface_edit' or request|has_permission:'interface_delete' %}
<th>选项</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for row in queryset %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ row.title }}</td>
<td>{{ row.url }}</td>
{% if request|has_permission:'interface_edit' or request|has_permission:'interface_delete' %}
<td>
{% if request|has_permission:'interface_edit' %}
<a href="{% memory_url request 'interface_edit' row.id %}">编辑</a>
{% endif %}
{% if request|has_permission:'interface_delete' %}
<a href="{% memory_url request 'interface_delete' row.id %}">删除</a>
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
<ul class="pagination">
{{ pager.page_html|safe }}
</ul>
</div>
{% endblock %}
总结:
- ModelForm
- 手写:样式 & 错误信息
- 重写 __init__ 方法 + 使用super 调用父类的 __init__方法
- 配置文件:zh-hans
- 自定义钩子方法:
def clean_字段名(..):
pass
注意:编辑时应该加入一个 instance 参数。
- 数据库中如果有 choice ,在模板中显示中文
gender = models.CharField(verbose_name='性别',choices=((1,'男'),(2,'女')))
- obj.get_gender_display()
- django请求生命周期
- 中间件的应用
- django配置文件
- from 项目 import settings 【用户定义】
- from django.conf import settings 【用户定义】+ Django内置配置
注意:配置文件中的值必须要大写
- 正则表达式 + re.match 模块
- 反射
setattr(request,'xxxxxxxxx',1) # request.xxxxxxxxx = 1
- 不能为空
... filter(字段__isnull = False)
- 去重
... filter(字段__isnull = False).distinct()
- ORM中Model表进行自关联
- 有序字典 (OrderdDict)
- django中的session会对用户赋值的数据进行序列化再保存
request.session['k1'] = {'xxx':123} # 正确
request.session['k1'] = QuerySet() # 错误
- 自定义模板方法
- simple_tag, 记录原搜索条件
- filter,粒度到按钮控制
- inclusion_tag ,动态生成菜单+导航条
- 记录原搜索条件
- memory_url
- memory_reverse
- 分页
- django admin
- 创建用户
- admin中显示中文表名称
class Menu(models.Model):
"""
菜单表
"""
title = models.CharField(verbose_name='菜单',max_length=32)
icon = models.CharField(verbose_name='图标',max_length=32)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = '菜单表'
- 定制admin中显示的列
class MenuAdmin(admin.ModelAdmin):
list_display = ['title','icon']
admin.site.register(models.Menu,MenuAdmin)
- ORM中创建“抽象类” abstract = True
class AbstractUserInfo(models.Model):
"""
用户表
"""
username = models.CharField(verbose_name='用户名', max_length=32)
password = models.CharField(verbose_name='密码', max_length=64)
roles = models.ManyToManyField(verbose_name='拥有的角色',to=Role)
class Meta:
abstract = True # 当前类不会再数据库迁移时候生成相关的表了,他来当 “基类”,给业务表中的用户表使用