内容回顾 1. 二级菜单 1. 建立个Menu的表,专门用来存一级菜单 2. 权限通过外键和Menu关联,权限通过show字段来控制是否显示成单独的二级菜单 3. jQuery绑定点击事件(左侧菜单的示例) 4. 给Menu菜单分配权重,实现自定义菜单的显示顺序 5. 二级菜单默认选中,其一级菜单默认展开 2. 面包屑导航 中国/广东/深圳/南山区 [目录, 一级菜单, 二级菜单,三级菜单] 目录: { 一级菜单: { 二级菜单:{ 三级菜单: { 'pid' : 3, 'title': '三级菜单', 'url: '/xx/' } } } } 1. request.bread_crumb = [] --> 每次请求来都生成一个自己的面包屑导航 3. 权限粒度细分到按钮 1. request.session['permission_list'] = ['customer/edit/(?P<cid>\d+)/', ...] 2. request.session['permission_list'] = [{'url': 'customer/edit/(?P<cid>\d+)/', ...] 3. request.session['permission_dict'] = { 'web:customer_edit': {'url': 'customer/edit/(?P<cid>\d+)/', 'menu_id': 1} } 4. 自定义filter实现是否显示的判断 {{ request|has_permission:'web:customer_edit' }} 2. 今日内容 1. CRM一样的操作再来三遍 1. 角色 2. 菜单 3. 权限 2. 把icon爬下来让用户直接选 1. 安装requests:http://cn.python-requests.org/zh_CN/latest/ pip install requests 2. 安装beautifulsoup4:https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/ pip install beautifulsoup4 3. MenuForm重写icon字段为单选框,将选项设置为上面爬取得图标列表
一.角色增删改查
1.角色页面展示创建
需求实现如下图效果:其实它就是一个查询角色表,用一个table显示出来。

先在settings中把权限校验的中间件注释掉#'rbac.midd.......',此时它会提示request对象没有request.bread_crumb属性。(因为此前是在中间件中对它做了赋值,注释后面再取就取不到),且layout.html中也注释掉
{# {% load rbac %}#}
{# {% show_menu request %}#}
{# {% bread_crumb request %}#}
(1)rbac/urls.py中:
app_name = '[rbac]'
url('^role/$',views.role_list, name='role_list'),
(2)luffy_permission/urls.py中导入二级路由把rbac开头的都跑到rbac的url中:
url(r'^rbac', include('rbac.urls', namespace='rbac')),
(3)rbac/views.py:
def role_list(request):
queryset = Role.objects.all() #查询数据库中所有角色的权限信息
return render(request, 'rbac/role_list.html', {'role_list': queryset})
(4)rbac/templates/rbac/role_list.html:
导入layout.html布局模版。并替换它content的block块。并在其中写一表格--加边框加条形状(外边套一div标签).tbody中有一个权限就生成一个tr(序号,名称,数量,操作---models.py中由角色查到它的权限是点permissions拿到的是manytomany,所以所有权限应该是permissions.all,点all得到的是一queryset,所以点count就能得到数量。操作中有一编辑是a标签btn,且btn超小,有图标i标签表示.还有一删除操作),
<td>
<a href=""><i class="fa fa-pencil"></i></a>
<a href=""><i class="fa fa-trash-o"></i></a>
</td>
登录后输入如下url就出现如下角色的基本页面效果了:
2.在上述效果中实现完整的增删改查角色页面--编辑和添加:
(1)rbac/urls.py中:添加和编辑用一个视图
app_name = '[rbac]' urlpatterns = [ url('^role/$',views.role_list, name='role_list'), url('^role_add/$',views.op_role, name='role_add'), url('^role_edit(\d+)/$',views.op_role, name='role_edit'), url('^role_del/(\d+)$',views.role_del, name='role_del'), ]
(2)rbac/views.py中:
添加和编辑二合一视图,添加和编辑页面op_role.html页面返回一form表单让他去填--用form,
from rbac.forms import RoleForm, MenuForm, PermissionForm # 添加和编辑 def op_role(request, edit_id=None): edit_obj = Role.objects.filter(pk=edit_id).first() form_obj = RoleForm(instance=edit_obj) if request.method == 'POST': #一点提交 form_obj = RoleForm(request.POST, instance=edit_obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('rbac:role_list')) return render(request, 'rbac/op_role.html', {'form_obj': form_obj}) # 删除 def role_del(request, del_id): Role.objects.filter(pk=del_id).delete() return redirect(reverse('rbac:role_list'))
(3)rbac/templates/rbac/role_list.html:
{% extends 'layout.html' %}
{% block content %}
<div>
<a class="btn btn-success" href="{% url 'rbac:role_add' %}"><i class="fa fa-plus-circle"></i>添加</a>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>#</th>
<th>角色名称</th>
<th>权限数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for role in role_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ role.title }}</td>
<td>{{ role.permissions.count }}</td>
<td>
<a href="{% url 'rbac:role_edit' role.id %}"><i class="fa fa-pencil"></i></a>
<a href="{% url 'rbac:role_del' role.id %}"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
(5)rbac/forms.py中:
from rbac.models import Role, Menu, Permission # Role的Model Form class RoleForm(forms.ModelForm): class Meta: model = Role fields = '__all__' #自定样式 def __init__(self, *args, **kwargs): super(RoleForm, self).__init__(*args, **kwargs) for field in self.fields.values(): #self.fields是ordedendict有序的字典,values是form表单中每一个字段对象 field.widget.attrs.update({'class': 'form-control'})
(6)rbac/templates/rbac/op_role.html:
div标签中写一form表单,循环,有一个字段就生成一个label标签,
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h2>添加角色</h2>
<form action="" method="post">
{% csrf_token %}
{% for field in form_obj %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }} {#会自动生成input标签#}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<div class="form-group">
<button type="submit" class="btn btn-success">提交</button>
</div>
</form>
</div>
{% endblock %}
最后就实现如下效果了:

二.菜单和权限的展示
需求:功能一:想实现如下效果:菜单和权限放一起了一个列表里,访问rbac/menu_list/左边显示菜单右边显示权限。功能二:且点击客户管理时,在右侧显示只显示客户管理菜单的所有权限--a标签(a标签上显示当前url并后面跟一?menu_id=,那查权限时不是查询所有的权限,而是根据这个id查权限)。点击帐单管理,右侧只显示帐单管理的所有权限。功能三:且点中它时显示高亮,把此行tr的背景色换下就可--当前url中的menu_id值和当前选中行的id一样则给高亮显示--<tr {% if request|is_this_menu:menu.id %} class="bg-success"{% endif %}>。---注意对象点id得到是一数字,而request参数里带的永远都是字符串,所以我在rbac/templatetags/rbac.py中自定义filter。
(1)rbac/urls.py:
app_name = '[rbac]' urlpatterns = [ # 角色管理 url(r'^role_list/$', views.role_list, name='role_list'), url(r'^role_add/$', views.op_role, name='role_add'), url(r'^role_edit/(\d+)/$', views.op_role, name='role_edit'), url(r'^role_del/(\d+)/$', views.role_del, name='role_del'), # 菜单管理 url(r'^menu_list/$', views.menu_list, name='menu_list'), url(r'^menu_add/$', views.op_menu, name='menu_add'), url(r'^menu_edit/(\d+)/$', views.op_menu, name='menu_edit'), url(r'^menu_del/(\d+)/$', views.menu_del, name='menu_del'), # 权限管理 url(r'^permission_add/$', views.op_permission, name='permission_add'), url(r'^permission_edit/(\d+)/$', views.op_permission, name='permission_edit'), url(r'^permission_del/(\d+)/$', views.permission_del, name='permission_del'), ]
(2)rbac/views.py
菜单列表视图中,把所有的菜单和权限拿出来--查两份表。
做判断,有无menu_id
# 菜单列表 def menu_list(request): # 先查询出所有的菜单 menu_queryset = Menu.objects.all() menu_id = request.GET.get('menu_id') if menu_id: # 表示根据菜单去查询它对应的权限 permission_queryset = Permission.objects.filter(menu_id=menu_id) else: # 查询所有的权限 permission_queryset = Permission.objects.all() return render(request, 'rbac/menu_list.html', {'menu_list': menu_queryset, 'permission_list': permission_queryset}) def op_menu(request, edit_id=None): pass def menu_del(request, del_id): pass def op_permission(request, edit_id=None): pass def permission_del(request, del_id): pass
(3)rbac/templates/rbac/menu_list.html
面板,左边占4--col-me-4右边占8---col-me-8。栅格系统。样式为淡蓝色--panel-info
for循环menu_list,生成一个个具体的菜单,路由反向解析--url 'rbac:menu_edit' menu.id。
循环权限permission_list。给菜单标题加一a标签--<a href="{% url 'rbac:menu_list' %}?menu_id={{ menu.id }}">。
{% extends 'layout.html' %}
{% block content %}
<div class="container-fluid">
<h2>菜单和权限管理</h2>
<div class="row">
<div class="col-md-4">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-book"></i> 菜单管理
<a class="pull-right" href="{% url 'rbac:menu_add' %}"><i class="fa fa-plus-circle"></i></a>
</h3>
</div>
<div class="panel-body">
<table class="table table-bordered">
<thead>
<tr>
<th>#</th>
<th>菜单名称</th>
<th>图标</th>
<th>权重</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for menu in menu_list %}
{% load rbac %}
<tr {% if request|is_this_menu:menu.id %} class="bg-success"{% endif %}>
<td>{{ forloop.counter }}</td>
<td><a href="{% url 'rbac:menu_list' %}?menu_id={{ menu.id }}">{{ menu.title }}</a></td>
<td><i class="fa {{ menu.icon }}"></i></td>
<td>{{ menu.weight }}</td>
<td>
<a href="{% url 'rbac:menu_edit' menu.id %}"><i class="fa fa-pencil"></i></a>
|
<a href="{% url 'rbac:menu_del' menu.id %}"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-8">
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title"><i class="fa fa-group"></i> 权限管理
<a class="pull-right" href="{% url 'rbac:permission_add' %}"><i class="fa fa-plus-circle"></i></a>
</h3>
</div>
<div class="panel-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>#</th>
<th>权限名称</th>
<th>URL</th>
<th>路由别名</th>
<th>是否显示</th>
<th>所属菜单</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for permission in permission_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ permission.title }}</td>
<td>{{ permission.url }}</td>
<td>{{ permission.name }}</td>
<td>{{ permission.show }}</td>
<td>{{ permission.menu.title }}</td>
<td>
<a href="{% url 'rbac:permission_edit' permission.id %}"><i class="fa fa-pencil"></i></a>
|
<a href="{% url 'rbac:permission_del' permission.id %}"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

(4)rbac/templatetags/rbac.py:自定义判断菜单的高亮显示
# 判断菜单的高亮显示
@register.filter() #给它注册一下
def is_this_menu(request, menu_id):
m_id = request.GET.get('menu_id', 0)
return str(m_id) == str(menu_id)
三.菜单图标选项
http://www.fontawesome.com.cn/---图标库
功能一:上述效果中的菜单管理和权限管理加图标。<i class="fa fa-book"></i> 菜单管理。<i class="fa fa-group"></i> 权限管理。功能二:在每一个菜单下面加添加按钮并且往右加a标签<a class="pull-right" href="{% url 'rbac:permission_add' %}"><i class="fa fa-plus-circle">。
功能三:点添加图标就添加对应菜单和对应权限。功能四:在添加菜单时它的图标可以把bootstrap中所有图标拿过来显示在添加菜单页面,这样我直接手动勾选想要的图标即可--新建一爬虫脚本。所以如下图效果中是把icon这一列(input框)变成一选项选起来就行--forms.py的MenuForm中改即可。

(1)rbac/views.py中:
op_menu视图中,返回op_menu.html页面--所以需要form表单。
def op_menu(request, edit_id=None): edit_obj = Menu.objects.filter(pk=edit_id).first() form_obj = MenuForm(instance=edit_obj) if request.method == 'POST': form_obj = MenuForm(request.POST, instance=edit_obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('rbac:menu_list')) return render(request, 'rbac/op_menu.html', {'form_obj': form_obj, 'edit_id': edit_id}) def menu_del(request, del_id): Menu.objects.filter(pk=del_id).delete() return redirect(reverse('rbac:menu_list')) def op_permission(request, edit_id=None): edit_obj = Permission.objects.filter(pk=edit_id).first() form_obj = PermissionForm(instance=edit_obj) if request.method == 'POST': form_obj = PermissionForm(request.POST, instance=edit_obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('rbac:menu_list')) return render(request, 'rbac/op_permission.html', {'form_obj': form_obj, 'edit_id': edit_id}) def permission_del(request, del_id): Permission.objects.filter(pk=del_id).delete() return redirect(reverse('rbac:menu_list'))
(2)rbac/forms.py中:
from django.utils.safestring import mark_safe # Menu的Model Form class MenuForm(forms.ModelForm): class Meta: model = Menu fields = '__all__' def __init__(self, *args, **kwargs): super(MenuForm, self).__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs.update({'class': 'form-control'}) # 把icon图标这个字段由input框变成单选框 self.fields['icon'] = forms.ChoiceField( widget=forms.widgets.RadioSelect, choices=((i[0], mark_safe(i[1])) for i in ICON_CHOICES) )
(3)rbac/templates/rbac/op_menu.html
判断有没有传入一edit_id。
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h2>{% if edit_id %}编辑菜单{% else %}添加菜单{% endif %}</h2>
<form action="" method="post">
{% csrf_token %}
{% for field in form_obj %}
<div class="form-group clearfix">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<div class="form-group">
<button type="submit" class="btn btn-success">提交</button>
</div>
</form>
</div>
{% endblock %}
(4)rbac/templates/rbac/op_permissio.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h2>{% if edit_id %}编辑权限信息{% else %}添加权限信息{% endif %}</h2>
<form action="" method="post">
{% csrf_token %}
{% for field in form_obj %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<div class="form-group">
<button type="submit" class="btn btn-success">提交</button>
</div>
</form>
</div>
{% endblock %}
(5)luffy_permission/icon爬虫.py: pip install requests -i https://pypi.douban.com/simple/
pip install BeautifulSoup4 -i https://pypi.douban.com/simple/
此文件模块就是拿到所有icon图标的列表icon_list,列表里有小列表:图标的文本内容(样式名),i标签,如下
[['fa-address-book', '<i aria-hidden="true" class="fa fa-address-book"></i>'],......]]
#!/usr/bin/env python # -*- coding:utf-8 -*- from django.utils.safestring import mark_safe import requests from bs4 import BeautifulSoup # 使用requests模块发送get请求去访问如下url,拿到一响应对象并用utf-8编码 response = requests.get( url='http://fontawesome.dashgame.com/', ) response.encoding = 'utf-8' # 使用BS4模块去解析获取的网页内容--response.text是它会把你当前拿过来网页的text文本用一大的字符串,去用指定的beautifulsoup模块做一下解析,生成一beautifulsoup对象 #find是查找页面上所有带id属性web-application的标签并赋值给web对象, soup = BeautifulSoup(response.text, 'html.parser') web = soup.find(attrs={'id': 'web-application'}) icon_list = [] #在web里按照class属性是fa-hover去查找,然后把其中的i标签拿出来,并把它的class name拿出来,放到列表中。 for item in web.find_all(attrs={'class': 'fa-hover'}): tag = item.find('i') class_name = tag.get('class')[1] icon_list.append([class_name, str(tag)]) print(icon_list)
浙公网安备 33010602011771号