CRM项目_权限系统_crm业务_ 其他
作者:互联网
CRM项目
1.权限系统
1.1问题
-
问:为什么程序需要权限控制?
答:生活中的权限限制,① 看灾难片电影《2012》中富人和权贵有权登上诺亚方舟,穷苦老百姓只有等着灾难的来临;② 屌丝们,有没有想过为什么那些长得漂亮身材好的姑娘在你身边不存在呢?因为有钱人和漂亮姑娘都是珍贵稀有的,稀有的人在一起玩耍和解锁各种姿势。而你,无权拥有他们,只能自己玩自己了。 程序开发时的权限控制,对于不同用户使用系统时候就应该有不同的功能,如:
-
普通员工、部门主管、总监、总裁
所以,只要有不同角色的人员来使用系统,那么就肯定需要权限系统。
-
-
问:为什么要开发权限组件?
答:假设你今年25岁,从今天开始写代码到80岁,每年写5个项目,那么你的一生就会写275个项目,保守估计其中应该有150+个都需要用到权限控制,为了以后不再重复的写代码,所以就开发一个权限组件以便之后55年的岁月中使用。 亲,不要太较真哦,你觉得程序员能到80岁么,哈哈哈哈哈哈哈 偷偷告诉你:老程序员开发速度快,其中一个原因是经验丰富,另外一个就是他自己保留了很多组件,新系统开发时,只需把组件拼凑起来基本就可以完成。
-
问:web开发中权限指的是什么?
答:web程序是通过 url 的切换来查看不同的页面(功能),所以权限指的其实就是URL,对url控制就是对权限的控制。
-
结论:一个人有多少个权限就取决于他有多少个URL的访问权限。
-
-
表的划分
-
用户表:id,name
-
角色表:id,title
-
用户角色关系表:id,userid,角色id
-
权限表:id,url
-
角色权限关系表:id,角色id,权限id
-
1.2 adimin
-
创建用户和使用
#创建admin user
python manage.py createsuperuser
#adimin中放置表
from django.contrib import admin
from app01 import models
class aa(admin.ModelAdmin):
#显示
list_display = ["title","url"]
#修改
list_editable = ["url"]
#将表在django自带的管理中显示出来。
#aa代表可以显示title和url
admin.site.register(models.表名,aa)
-
rbac/admin.py
from django.contrib import admin
# Register your models here.
from rbac import models
class PermissionAdmin(admin.ModelAdmin):
list_display = ["title","url"]
list_editable = ["url"]
#将表在django自带的管理中显示出来。
admin.site.register(models.Permission,PermissionAdmin)
admin.site.register(models.Role)
admin.site.register(models.UserInfo)
1.3 基本配置
-
mysite
-
__ init __.py
import pymysql
pymysql.install_as_MySQLdb()
-
settings.py
from pathlib import Path
import os
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"rbac.apps.RbacConfig",
"web.apps.WebConfig",
#注册app
]
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',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join("%s/%s" %(BASE_DIR,"rbac"),"template"),
os.path.join("%s/%s" %(BASE_DIR,"web"),"template")],
#写模板的路劲
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'mysite.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
#配置数据库
DATABASES = {
"default":{
"ENGINE":"django.db.backends.mysql",
"NAME":"CRM",
"USER":"root",
"PASSWORD":"1451964253",
"HOST":"localhost",
"PORT":3306,
}
}
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = 'static/'
STATICFILES_DIRS =os.path.join(BASE_DIR,"static"),
# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
from django.core.files.uploadhandler import MemoryFileUploadHandler
from django.core.files.uploadhandler import TemporaryFileUploadHandler
# List of upload handler classes to be applied in order.
FILE_UPLOAD_HANDLERS = [
'django.core.files.uploadhandler.MemoryFileUploadHandler',
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
]
# Maximum size, in bytes, of a request before it will be streamed to the
# file system instead of into memory.
# 允许内存中上传文件的大小
# 合法:InMemoryUploadedFile对象(写在内存) -> 上传文件小于等于 FILE_UPLOAD_MAX_MEMORY_SIZE
# 不合法:TemporaryUploadedFile对象(写在临时文件) -> 上传文件大于 FILE_UPLOAD_MAX_MEMORY_SIZE 且 小于 DATA_UPLOAD_MAX_MEMORY_SIZE
FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB
# Maximum size in bytes of request data (excluding file uploads) that will be
# read before a SuspiciousOperation (RequestDataTooBig) is raised.
# 允许上传内容的大小(包含文件和其他请求内容)
DATA_UPLOAD_MAX_MEMORY_SIZE = 2621440 # i.e. 2.5 MB
# Maximum number of GET/POST parameters that will be read before a
# SuspiciousOperation (TooManyFieldsSent) is raised.
# 允许的上传文件数
DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000
# Directory in which upload streamed files will be temporarily saved. A value of
# `None` will make Django use the operating system's default temporary directory
# (i.e. "/tmp" on *nix systems).
# 临时文件夹路径
FILE_UPLOAD_TEMP_DIR = None
# The numeric mode to set newly-uploaded files to. The value should be a mode
# you'd pass directly to os.chmod; see https://docs.python.org/3/library/os.html#files-and-directories.
# 文件权限
FILE_UPLOAD_PERMISSIONS = None
# The numeric mode to assign to newly-created directories, when uploading files.
# The value should be a mode as you'd pass to os.chmod;
# see https://docs.python.org/3/library/os.html#files-and-directories.
# 文件夹权限
FILE_UPLOAD_DIRECTORY_PERMISSIONS = None
-
urls.py
from django.contrib import admin
from django.urls import path,re_path
from django.conf.urls import include
urlpatterns = [
path('admin/', admin.site.urls),
re_path("^",include("web.urls")),
]
-
-
rbac
-
models.py
from django.db import models
# Create your models here.
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128)
#为了区分是否是菜单
is_menu = models.BooleanField(verbose_name="是否可做菜单",default=False)
icon = models.CharField(max_length=32,null=True,blank=True)
def __str__(self):
return self.title
class Role(models.Model):
"""
角色
"""
title = models.CharField(verbose_name='角色名称', max_length=32)
permissions = models.ManyToManyField(verbose_name='拥有的所有权限', to='Permission', blank=True)
def __str__(self):
return self.title
class UserInfo(models.Model):
"""
用户表
"""
name = models.CharField(verbose_name='用户名', max_length=32)
password = models.CharField(verbose_name='密码', max_length=64)
email = models.CharField(verbose_name='邮箱', max_length=32)
roles = models.ManyToManyField(verbose_name='拥有的所有角色', to='Role', blank=True)
def __str__(self):
return self.name
-
views.py
-
static
-
css
-
js
-
images
-
plugins
-
rbac
-
rabc.css
-
-
-
-
web
-
models.py
from django.db import models
# Create your models here.
class Customer(models.Model):
"""
客户表
"""
name = models.CharField(verbose_name='姓名', max_length=32)
age = models.CharField(verbose_name='年龄', max_length=32)
email = models.EmailField(verbose_name='邮箱', max_length=32)
company = models.CharField(verbose_name='公司', max_length=32)
class Payment(models.Model):
"""
付费记录
"""
customer = models.ForeignKey(verbose_name='关联客户', to='Customer',on_delete=models.CASCADE)
money = models.IntegerField(verbose_name='付费金额')
create_time = models.DateTimeField(verbose_name='付费时间', auto_now_add=True)
-
views
-
customer.py
import os
import mimetypes
from django.shortcuts import render, redirect
from django.http import FileResponse
from django.conf import settings
import xlrd
from web import models
from web.forms.customer import CustomerForm
def customer_list(request):
"""
客户列表
:return:
"""
data_list = models.Customer.objects.all()
return render(request, 'customer_list.html', {'data_list': data_list})
def customer_add(request):
"""
编辑客户
:return:
"""
if request.method == 'GET':
form = CustomerForm()
return render(request, 'customer_edit.html', {'form': form})
form = CustomerForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('/customer/list/')
return render(request, 'customer_edit.html', {'form': form})
def customer_edit(request, cid):
"""
新增客户
:return:
"""
obj = models.Customer.objects.get(id=cid)
if request.method == 'GET':
form = CustomerForm(instance=obj)
return render(request, 'customer_add.html', {'form': form})
form = CustomerForm(data=request.POST, instance=obj)
if form.is_valid():
form.save()
return redirect('/customer/list/')
return render(request, 'customer_add.html', {'form': form})
def customer_del(request, cid):
"""
删除客户
:param request:
:param cid:
:return:
"""
models.Customer.objects.filter(id=cid).delete()
return redirect('/customer/list/')
def customer_import(request):
"""
批量导入
:param request:
:return:
"""
if request.method == 'GET':
return render(request, 'customer_import.html')
context = {'status': True, 'msg': '导入成功'}
try:
customer_excel = request.FILES.get('customer_excel')
"""
打开上传的Excel文件,并读取内容
注:打开本地文件时,可以使用:workbook = xlrd.open_workbook(filename='本地文件路径.xlsx')
"""
workbook = xlrd.open_workbook(file_contents=customer_excel.file.read())
# sheet = workbook.sheet_by_name('工作表1')
sheet = workbook.sheet_by_index(0)
row_map = {
0: {'text': '客户姓名', 'name': 'name'},
1: {'text': '年龄', 'name': 'age'},
2: {'text': '邮箱', 'name': 'email'},
3: {'text': '公司', 'name': 'company'},
}
object_list = []
for row_num in range(1, sheet.nrows):
row = sheet.row(row_num)
row_dict = {}
for col_num, name_text in row_map.items():
row_dict[name_text['name']] = row[col_num].value
object_list.append(models.Customer(**row_dict))
models.Customer.objects.bulk_create(object_list, batch_size=20)
except Exception as e:
context['status'] = False
context['msg'] = '导入失败'
return render(request, 'customer_import.html', context)
def customer_tpl(request):
"""
下载批量导入Excel列表
:param request:
:return:
"""
tpl_path = os.path.join(settings.BASE_DIR, 'web', 'files', '批量导入客户模板.xlsx')
content_type = mimetypes.guess_type(tpl_path)[0]
print(content_type)
response = FileResponse(open(tpl_path, mode='rb'), content_type=content_type)
response['Content-Disposition'] = "attachment;filename=%s" % 'customer_excel_tpl.xlsx'
return response
-
payment.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from django.shortcuts import render, redirect
from web import models
from web.forms.payment import PaymentForm, PaymentUserForm
def payment_list(request):
"""
付费列表
:return:
"""
data_list = models.Payment.objects.all()
return render(request, 'payment_list.html', {'data_list': data_list})
def payment_add(request):
"""
编辑付费记录
:return:
"""
if request.method == 'GET':
form = PaymentForm()
return render(request, 'payment_edit.html', {'form': form})
form = PaymentForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('/payment/list/')
return render(request, 'payment_edit.html', {'form': form})
def payment_edit(request, pid):
"""
新增付费记录
:return:
"""
obj = models.Payment.objects.get(id=pid)
if request.method == 'GET':
form = PaymentForm(instance=obj)
return render(request, 'payment_add.html', {'form': form})
form = PaymentForm(data=request.POST, instance=obj)
if form.is_valid():
form.save()
return redirect('/payment/list/')
return render(request, 'payment_add.html', {'form': form})
def payment_del(request, pid):
"""
删除付费记录
:param request:
:param cid:
:return:
"""
models.Payment.objects.filter(id=pid).delete()
return redirect('/payment/list/')
-
-
urls.py
from django.contrib import admin
from django.urls import path,re_path
from web.views import customer
from web.views import payment
from web.views import account
urlpatterns = [
re_path("login/",account.login),
re_path(r'^customer/list/$', customer.customer_list),
re_path(r'^customer/add/$', customer.customer_add),
re_path(r'^customer/edit/(?P<cid>\d+)/$', customer.customer_edit),
re_path(r'^customer/del/(?P<cid>\d+)/$', customer.customer_del),
re_path(r'^customer/import/$', customer.customer_import),
re_path(r'^customer/tpl/$', customer.customer_tpl),
re_path(r'^payment/list/$', payment.payment_list),
re_path(r'^payment/add/$', payment.payment_add),
re_path(r'^payment/edit/(?P<pid>\d+)/$', payment.payment_edit),
re_path(r'^payment/del/(?P<pid>\d+)/$', payment.payment_del),
]
"""
客户列表:/customer/list/
添加客户:/customer/add/
删除客户:/customer/del/(?P<cid>\d+)/
修改客户:/customer/edit/(?P<cid>\d+)/
批量导入:/customer/import/
下载模板:/customer/tpl/
账单管理
账单列表:/payment/list/
添加账单:/payment/add/
删除账单:/payment/del/(?P<pid>\d+)/
修改账单:/payment/edit/<?P<pid>\d+/
"""
-
1.4 登录和添加session
-
mysite/settings.py
#自己配置的变量名为了以后方便操作。
PERMISSION_SESSION_KEY = "permission_list"
-
web/views/account.py
from django.shortcuts import render,redirect,HttpResponse
from rbac.service.init_permission import init_permission
from django.urls import reverse
from rbac import models
from django.forms import Form
from django.forms import fields
from django.conf import settings
#引入django的配置文件
class LoginForm(Form):
name = fields.CharField(max_length="32")
password = fields.CharField(max_length="32")
def login(request):
if request.method == "GET":
obj=LoginForm()
return render(request,"login.html",{"obj":obj})
obj = LoginForm(request.POST)
if not obj.is_valid():
return render(request,"login.html",{"obj":obj})
cls = models.UserInfo.objects.filter(**obj.cleaned_data).first()
if cls:
request.session["user_info"]=obj.cleaned_data
init_permission(request,cls)
return redirect("/customer/list/")
return render(request,"login.html",{"obj":obj})
# Create your views here.
"""
class JsonResponse:
def __init__(self,req,status,msg):
self.req = req
self.status = status
self.msg = msg
def render(self):
import json
ret = {
"status":self.status,
"msg":self.msg
}
return HttpResponse(json.dumps(ret))
"""
#return JsonResponse(request,True,"错误信息")
-
rbac/service/init_permission.py
from django.conf import settings
def init_permission(request,user):
#权限和菜单信息初始化,以后使用时,需要在登陆成功后调用该方法将权限和菜单信息放入session。
#拿到Userinfo表中roles字段与当前用户关联的角色id和title
#__isnull等于True是允许为空,反之不允许。
#distinct去重
#cls.roles.all().filter(permissions__url__isnull=False).values("id","title","permissions__url")
permission_queryset = user.roles.all().filter(permissions__url__isnull=False).values("permissions__url","permissions__title","permissions__is_menu","permissions__icon").distinct()
menu_list = []
permission_list = []
for row in permission_queryset:
permission_list.append({"permissions_url":row["permissions__url"]})
if row["permissions__is_menu"]:
menu_list.append({"title":row["permissions__title"],"is_menu":row["permissions__is_menu"],"icon":row["permissions__icon"],"url":row["permissions__url"]})
request.session[settings.PERMISSION_SESSION_KEY]=permission_list
request.session[settings.MENU_SESSION_KEY]=menu_list
#从数据库取出无法直接序列化转成python才可以。
#request.session[settings.PERMISSION_SESSION_KEY]=list(permission_list)
#models.UserInfo.objects.create(**obj.cleaned_data)
1.5 添加中间件
-
mysite/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"
#注册中间件
]
#自己配置的变量名为了以后方便操作。
MENU_SESSION_KEY = "menu_list"
#白名单
VALID_URL = [
"^/login/$",
"^/admin/.*"
]
-
rbac/middleware/rbac.py
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import redirect,HttpResponse
import re
class RbacMiddleware(MiddlewareMixin):
"""
权限控制中间件
"""
def process_request(self,request):
#1.获取当前请求url
current_url = request.path_info
#2.白名单处理
for reg in settings.VALID_URL:
if re.match(reg,current_url):
return None
#3.获取当前用户session中所有权限
permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
if not permission_list:
return redirect("/login/")
#4.进行权限校验
flag = False
for item in permission_list:
reg = "^%s$"% item.get("permissions_url")
if re.match(reg,current_url):
flag = True
break
if not flag:
return HttpResponse("无权访问")
1.6 编写一级菜单
-
rbac/template/rbac/menu.html
<div class="static-menu">
{% for item in menu_list %}
<a href="{{item.url}}" class="{{item.class}}">
<span class="icon-wrap"><i class="fa {{item.icon}}"></i></span>{{item.title}}</a>
{% endfor %}
</div>
-
rbac/templatetags/rbac.py
from django.template import Library
from django.conf import settings
import re
register = Library()
#先去拿模板,然后返回到调用的模板中。
@register.inclusion_tag("rbac/menu.html")
def menu(request):
menu_list = request.session.get(settings.MENU_SESSION_KEY)
#默认选中
for item in menu_list:
reg = "^%s$" % item["url"]
if re.match(reg,request.path_info):
item["class"] = "active"
return {"menu_list":menu_list}
1.7 编写二级菜单
-
rbac/models.py
from django.db import models
# Create your models here.
class Menu(models.Model):
"""
菜单表
"""
#需要创建唯一索引
title = models.CharField(max_length=32,unique=True)
icon = models.CharField(max_length=32)
#显示文字,不加的话是对象
def __str__(self):
return self.title
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128)
menu = models.ForeignKey(verbose_name="菜单",to="Menu",null=True,blank=True,on_delete=models.CASCADE)
def __str__(self):
return self.title
-
rbac/service/init_permission
from django.conf import settings
def init_permission(request,user):
permission_queryset = user.roles.all().filter(permissions__url__isnull=False).values(,"permissions__url","permissions__title","permissions__menu_id","permissions__menu__title","permissions__menu__icon").distinct()
menu_dict = {}#菜单+能成为菜单的权限,用于做菜单显示
permission_list = []#所有权限,用于做校验
for i in permission_queryset:
permission_list.append({,"url":i["permissions__url"],"pid":i["permissions__parent_id"]})
menu_id = i.get("permissions__menu_id")
if not menu_id:
continue
if menu_id in menu_dict:
menu_dict[menu_id]["children"].append({,"title":i["permissions__title"],"url":i["permissions__url"]})
else:
menu_dict[i["permissions__menu_id"]]={"title":i["permissions__menu__title"],
"icon":i["permissions__menu__icon"],
"children":[{,"title":i["permissions__title"],
"url":i["permissions__url"]}]}
request.session[settings.PERMISSION_SESSION_KEY]=permission_list
request.session[settings.MENU_SESSION_KEY]=menu_dict
-
rbac/templatetags/rbac.py
from django.template import Library
from django.conf import settings
from collections import OrderedDict
#导入有序字典
import re
register = Library()
@register.inclusion_tag("rbac/menu.html")
def menu(request):
menu_dict = request.session.get(settings.MENU_SESSION_KEY)
ordered_dict = OrderedDict()
#sorted(降序排列)加reverse=True是升序。
#python3.7以前字典是无序的所以为了,菜单的顺序不变。需要加上有序字典。
for key in sorted(menu_dict):
ordered_dict[key] = menu_dict[key]
menu_dict[key]["class"] = "hide"
#设置自动选中
for node in menu_dict[key]["children"]:
reg = "^%s$" %node["url"]
if re.match(reg,request.path_info):
node["class"] = "active"
menu_dict[key]["class"] = ""
return {"menu_dict":ordered_dict}
-
rbac/template/rbac/menu.html
<div class="multi-menu">
{% for item in menu_dict.values %}
<div class="item">
<div class="title"><span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span> {{ item.title }}</div>
<div class="body {{ item.class }}">
{% for per in item.children %}
<a class="{{ per.class }}" href="{{ per.url }}">{{ per.title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
1.8 非菜单归属之动态选中
-
rbac/models.py
from django.db import models
# Create your models here.
class Menu(models.Model):
"""
菜单表
"""
#需要创建唯一索引
title = models.CharField(max_length=32,unique=True)
icon = models.CharField(max_length=32)
#显示文字,不加的话是对象
def __str__(self):
return self.title
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128)
#是放属于某个一级菜单的id,他和menu互斥的不然后面没法判断。
parent = models.ForeignKey(verbose_name="父权限",to="permission",null=True,blank=True,on_delete=models.CASCADE)
menu = models.ForeignKey(verbose_name="菜单",to="Menu",null=True,blank=True,on_delete=models.CASCADE)
def __str__(self):
return self.title
-
rbac/middleware/rbac.py
servicefrom django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import redirect,HttpResponse
import re
class RbacMiddleware(MiddlewareMixin):
"""
权限控制中间件
"""
#4.进行权限校验
flag = False
for item in permission_list:
id = item["id"]
pid = item["pid"]
reg = "^%s$"% item.get("url")
if re.match(reg,current_url):
flag = True
if pid:
request.current_menu_id = pid
else:
request.current_menu_id = id
break
if not flag:
return HttpResponse("无权访问")
-
rbac/service/init_permission.py
from django.conf import settings
def init_permission(request,user):
permission_queryset = user.roles.all().filter(permissions__url__isnull=False).values(
"permissions__id",
"permissions__url",
"permissions__title",
"permissions__parent_id",
"permissions__menu_id",
"permissions__menu__title",
"permissions__menu__icon").distinct()
menu_dict = {}#菜单+能成为菜单的权限,用于做菜单显示
permission_list = []#所有权限,用于做校验
for i in permission_queryset:
permission_list.append({"id":i["permissions__id"],"url":i["permissions__url"],"pid":i["permissions__parent_id"]})
menu_id = i.get("permissions__menu_id")
if not menu_id:
continue
if menu_id in menu_dict:
menu_dict[menu_id]["children"].append({"id":i["permissions__id"],"title":i["permissions__title"],"url":i["permissions__url"]})
else:
menu_dict[i["permissions__menu_id"]]={"title":i["permissions__menu__title"],
"icon":i["permissions__menu__icon"],
"children":[{"id":i["permissions__id"],"title":i["permissions__title"],
"url":i["permissions__url"]}]}
request.session[settings.PERMISSION_SESSION_KEY]=permission_list
request.session[settings.MENU_SESSION_KEY]=menu_dict
-
rbac/templatetags/rbac.py
from django.template import Library from django.conf import settings from collections import OrderedDict import re #导入有序字典 register = Library() @register.inclusion_tag("rbac/menu.html") def menu(request): menu_dict = request.session.get(settings.MENU_SESSION_KEY) ordered_dict = OrderedDict() #sorted(降序排列)加reverse=True是升序。 for key in sorted(menu_dict): ordered_dict[key] = menu_dict[key] menu_dict[key]["class"] = "hide" #设置自动选中 for node in menu_dict[key]["children"]: if request.current_menu_id == node["id"]: node["class"] = "active" menu_dict[key]["class"] = "" return {"menu_dict":ordered_dict}
1.9 导航条
-
rbac/middleware/rbac.py
from django.utils.deprecation import MiddlewareMixin from django.conf import settings from django.shortcuts import redirect,HttpResponse import re class RbacMiddleware(MiddlewareMixin): """ 权限控制中间件 """ request.breadcrumb_list = [ {"title":"首页","url":"/"}, ] #4.进行权限校验 flag = False for item in permission_dict.values(): pid = item["pid"] id = item["id"] pname = item["pname"] reg = "^%s$"% item.get("url") print(reg,current_url) if re.match(reg,current_url): flag = True if pid: request.current_menu_id = pid request.breadcrumb_list.extend([ {"title":permission_dict[str(pid)]["title"],"url":permission_dict[str(pid)]["url"]}, {"title":item["title"],"url":item["url"]}, ]) else: request.current_menu_id = id request.breadcrumb_list.extend([ {"title":item["title"],"url":item["url"]},]) break if not flag: return HttpResponse("无权访问")
-
/rbac/templatetags/rbac.py
from django.template import Library from django.conf import settings from collections import OrderedDict import re #导入有序字典 register = Library() @register.inclusion_tag("rbac/breadcrumb.html") def breadcrumb(request): return {"breadcrumb_list":request.breadcrumb_list}
-
web/template/layout.html
{% load static %} <!--导入静态文件--> {% load rbac%} <!--事先导入--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>路飞学城</title> <!--应用静态文件中的其他文件--> <link rel="shortcut icon" href="{% static 'imgs/luffy-study-logo.png' %} "> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.css' %} "/> <link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %} "/> <link rel="stylesheet" href="{% static 'css/commons.css' %} "/> <link rel="stylesheet" href="{% static 'css/nav.css' %} "/> <link rel="stylesheet" href="{% static 'rbac/css/rbac.css' %} "/> </head> <body> <div class="pg-body"> <div class="left-menu"> <div class="menu-body"> {% menu request %} </div> </div> <div class="right-body"> <div> {% breadcrumb request %} </div> {% block content %} {% endblock %} </div> </div> <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script> <script src="{% static 'plugins/bootstrap/js/bootstrap.js' %}"></script> <script src="{% static 'rbac/js/rbac.js' %}"></script> {% block js %} {% endblock %} </body> </html>
-
rbac/template/rbac/breadcrumb.html
<ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;"> {% for i in breadcrumb_list %} {% if forloop.last %} <!--判断i的url是否有当前url--> <li class="active">{{i.title}}</li> {% else %} <li><a href="{{i.url}}">{{i.title}}</a></li> {% endif %} {% endfor %} </ol>
1.10 粒度控制到按钮
-
rbac/models.py
from django.db import models # Create your models here. class Menu(models.Model): """ 菜单表 """ #需要创建唯一索引 title = models.CharField(max_length=32,unique=True) icon = models.CharField(max_length=32) #显示文字,不加的话是对象 def __str__(self): return self.title class Permission(models.Model): """ 权限表 """ title = models.CharField(verbose_name='标题', max_length=32) url = models.CharField(verbose_name='含正则的URL', max_length=128) name = models.CharField(verbose_name='URL别名', max_length=32,null=True,blank=True) parent = models.ForeignKey(verbose_name="父权限",to="permission",null=True,blank=True,on_delete=models.CASCADE) menu = models.ForeignKey(verbose_name="菜单",to="Menu",null=True,blank=True,on_delete=models.CASCADE) def __str__(self): return self.title
-
rbac/service/init_permission
from django.conf import settings def init_permission(request,user): permission_queryset = user.roles.all().filter(permissions__url__isnull=False).values("permissions__id", "permissions__url", "permissions__name", "permissions__title", "permissions__parent_id", "permissions__parent__name", "permissions__menu_id", "permissions__menu__title", "permissions__menu__icon").distinct() menu_dict = {}#菜单+能成为菜单的权限,用于做菜单显示 permission_dict = {}#所有权限,用于做校验 for i in permission_queryset: permission_dict[i["permissions__name"]]={"id":i["permissions__id"],"title":i["permissions__title"],"url":i["permissions__url"],"pid":i["permissions__parent_id"],"pname":i["permissions__parent__name"],} menu_id = i.get("permissions__menu_id") if not menu_id: continue if menu_id not in menu_dict: menu_dict[i["permissions__menu_id"]]={"title":i["permissions__menu__title"], "icon":i["permissions__menu__icon"], "children":[{"id":i["permissions__id"],"title":i["permissions__title"], "url":i["permissions__url"]}]} else: menu_dict[menu_id]["children"].append({"id":i["permissions__id"],"title":i["permissions__title"],"url":i["permissions__url"]}) request.session[settings.PERMISSION_SESSION_KEY]=permission_dict request.session[settings.MENU_SESSION_KEY]=menu_dict
-
rbac/middleware/rbac.py
from django.utils.deprecation import MiddlewareMixin from django.conf import settings from django.shortcuts import redirect,HttpResponse import re class RbacMiddleware(MiddlewareMixin): """ 权限控制中间件 """ #4.进行权限校验 flag = False for item in permission_dict.values(): pid = item["pid"] id = item["id"] pname = item["pname"] reg = "^%s$"% item.get("url") print(reg,current_url) if re.match(reg,current_url): flag = True if pid: request.current_menu_id = pid request.breadcrumb_list.extend([ {"title":permission_dict[pname]["title"],"url":permission_dict[pname]["url"]}, {"title":item["title"],"url":item["url"]}, ]) else: request.current_menu_id = id request.breadcrumb_list.extend([ {"title":item["title"],"url":item["url"]},]) break if not flag: return HttpResponse("无权访问")
-
rbac/templatetags/rbac.py
from django.template import Library from django.conf import settings from collections import OrderedDict import re #导入有序字典 register = Library() @register.filter def has_permission(request,name): permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY) if name in permission_dict: return True
-
web/template/customer_list.html
{% extends 'layout.html' %} {% load rbac %} {% block content %} <div class="luffy-container"> <div class="btn-group" style="margin: 5px 0"> {% if request|has_permission:"customer_add" %} <a class="btn btn-default" href="{% url 'customer_add' %}"> <i class="fa fa-plus-square" aria-hidden="true"></i> 添加客户 </a> {% endif %} {% if request|has_permission:"customer_import" %} <a class="btn btn-default" href="{% url 'customer_import' %}"> <i class="fa fa-file-excel-o" aria-hidden="true"></i> 批量导入 </a> {% endif %} </div> <table class="table table-bordered table-hover"> <thead> <tr> <th>ID</th> <th>客户姓名</th> <th>年龄</th> <th>邮箱</th> <th>公司</th> {% if request|has_permission:"customer_del" or request|has_permission:"customer_edit"%} <th>选项</th> {% endif %} </tr> </thead> <tbody> {% for row in data_list %} <tr> <td>{{ row.id }}</td> <td>{{ row.name }}</td> <td>{{ row.age }}</td> <td>{{ row.email }}</td> <td>{{ row.company }}</td> {% if request|has_permission:"customer_del" or request|has_permission:"customer_edit"%} <td> {% if request|has_permission:"customer_edit"%} <a style="color: #333333;" href="{% url 'customer_edit' cid=row.id %}"> <i class="fa fa-edit" aria-hidden="true"></i></a> | {% endif %} {% if request|has_permission:"customer_del"%} <a style="color: #d9534f;" href="{% url 'customer_del' cid=row.id %}"><i class="fa fa-trash-o"></i></a> {% endif %} </td> {% endif %} </tr> {% endfor %} </tbody> </table> </div> {% endblock %}
1.11问题总结
-
权限有几张表?
-
简述权限流程?
-
为什么要把权限放入session?
-
静态文件和模块文件
-
相关技点。
-
二级菜单时,如何构造的数据结构?
-
非菜单的权限归属?
-
层级导航?
-
粒度控制到按钮?
-
如何实现的权限系统?
-
为什么要在中间件中做校验?
-
写出流程(思维导图)
2.crm业务
2.1 modelForm
-
views.py
from django import forms def user_list(request): user_queryset = models.User.objects.all() return render(request,"user_list.html",{"user_queryset":user_queryset}) class UserForm(forms.ModelForm): class Meta: #打开User表赋给model model = models.User #拿出所有字段 fields = "__all__" #也可以拿取指定的字段 #fields = ["name","depart"] #这里也可以写插件 widgets = { "name":forms.TextInput(attrs={"class":"form-con"}), "gender":forms.Select(attrs={"class":"form-con"}), "roles":forms.SelectMultiple(attrs={"class":"form-con"}), } #可以自己写错误信息 error_messages = { "name":{ "required":"用户名不能为空" } } def user_add(request): if request.method == "GET": form = UserForm() else: form = UserForm(request.POST) if form.is_valid(): print("通过验证") #它自动把你增加不管单表还是多表。 form.save() return redirect("/user/list") return render(request,"user_add.html",{"form":form}) def user_edit(request,uid): obj = models.User.objects.filter(id=uid).first() if request.method == "GET": #设置默认值 form = UserForm(instance=obj) return render(request,"user_edit.html",{"form":form}) else: form = UserForm(request.POST,instance=obj) if form.is_valid(): #如果你要修改,需要加上instance=obj,应为save内部需要判断。 #不然怎么去区分。 form.save() return redirect("/user/list") return render(request,"user_edit.html",{"form":form})
-
models.py
from django.db import models class Depart(models.Model): caption = models.CharField(max_length=32) class User(models.Model): name = models.CharField(max_length=32) depart = models.ForeignKey(to="Depart",on_delete=models.CASCADE) gender_choices = ( (1,"男"), (2,"女") ) gender = models.IntegerField(choices=gender_choices,default=1) roles = models.ManyToManyField(to="Role")
2.2 简单化角色权限管理
-
mysite/urls.py
from django.contrib import admin from django.urls import path,re_path from django.conf.urls import include urlpatterns = [ path('admin/', admin.site.urls), re_path("^",include("web.urls")), #加上namespace后它下面的都会加上rbac:xxx的前缀 re_path("^rbac/",include("rbac.urls",namespace="rbac")), ]
-
rbac/urls.py
from django.urls import path,re_path from rbac.views import role from rbac.views import menu #在最外面的urls里面写了前缀,在这里必须声明 app_name = "rbac" urlpatterns = [ re_path(r'^role/list/$', role.role_list,name="role_list"), re_path(r'^role/add/$', role.role_add,name="role_add"), re_path(r'^role/edit/(?P<rid>\d+)/$', role.role_edit,name="role_edit"), re_path(r'^menu/list/$', menu.menu_list,name="menu_list"), re_path(r'^menu/add/$', menu.menu_add,name="menu_add"), ]
-
rbac/views/role.py
from django.shortcuts import render,redirect #用于反向生成url本质上都是调用的这里。 from django.urls import reverse from rbac import models # Create your views here. def role_list(request): role_queryset = models.Role.objects.all() return render(request,"rbac/role_list.html",{"role_queryset":role_queryset}) from django import forms class RoleModelForm(forms.ModelForm): class Meta: model = models.Role fields = ["title"] widgets = { "title":forms.TextInput(attrs={"class":"form-control"}) } def role_add(request): if request.method == "GET": form = RoleModelForm() else: form = RoleModelForm(request.POST) if form.is_valid(): form.save() #这样写后就可以反向生成了。 return redirect(reverse("rbac:role_list")) return render(request,"rbac/role_add.html",{"form":form}) def role_edit(request,rid): obj=models.Role.objects.filter(id=rid).first() if not obj: return HttpResponse("角色不存在") if request.method == "GET": form = RoleModelForm(instance=obj) else: form = RoleModelForm(request.POST,instance=obj) if form.is_valid(): form.save() return redirect("/rbac/role/list/") return render(request,"rbac/role_edit.html",{"form":form})
-
rbac/views/menu.py
from django.shortcuts import render,redirect #用于反向生成url本质上都是调用的这里。 from django.urls import reverse from rbac import models # Create your views here. def menu_list(request): menu_queryset = models.Menu.objects.all() mid = request.GET.get("mid") if mid: permission_queryset = models.Permission.objects.filter(menu_id=mid) else: permission_queryset = [] return render( request, "rbac/menu_list.html", { "menu_queryset":menu_queryset, "permission_queryset":permission_queryset }) from django.utils.safestring import mark_safe ICON_LIST = [ ['fa-hand-scissors-o', '<i aria-hidden="true" class="fa fa-hand-scissors-o"></i>'], ['fa-hand-spock-o', '<i aria-hidden="true" class="fa fa-hand-spock-o"></i>'], ['fa-hand-stop-o', '<i aria-hidden="true" class="fa fa-hand-stop-o"></i>'], ['fa-handshake-o', '<i aria-hidden="true" class="fa fa-handshake-o"></i>'], ['fa-hard-of-hearing', '<i aria-hidden="true" class="fa fa-hard-of-hearing"></i>'], ['fa-hashtag', '<i aria-hidden="true" class="fa fa-hashtag"></i>'], ['fa-hdd-o', '<i aria-hidden="true" class="fa fa-hdd-o"></i>'], ['fa-headphones', '<i aria-hidden="true" class="fa fa-headphones"></i>'], ['fa-heart', '<i aria-hidden="true" class="fa fa-heart"></i>'], ['fa-heart-o', '<i aria-hidden="true" class="fa fa-heart-o"></i>'], ['fa-heartbeat', '<i aria-hidden="true" class="fa fa-heartbeat"></i>'], ['fa-history', '<i aria-hidden="true" class="fa fa-history"></i>'], ['fa-home', '<i aria-hidden="true" class="fa fa-home"></i>'], ['fa-hotel', '<i aria-hidden="true" class="fa fa-hotel"></i>'], ['fa-hourglass', '<i aria-hidden="true" class="fa fa-hourglass"></i>'], ['fa-hourglass-1', '<i aria-hidden="true" class="fa fa-hourglass-1"></i>'], ['fa-hourglass-2', '<i aria-hidden="true" class="fa fa-hourglass-2"></i>'], ['fa-hourglass-3', '<i aria-hidden="true" class="fa fa-hourglass-3"></i>'], ['fa-hourglass-end', '<i aria-hidden="true" class="fa fa-hourglass-end"></i>'], ['fa-hourglass-half', '<i aria-hidden="true" class="fa fa-hourglass-half"></i>'], ['fa-hourglass-o', '<i aria-hidden="true" class="fa fa-hourglass-o"></i>'], ['fa-hourglass-start', '<i aria-hidden="true" class="fa fa-hourglass-start"></i>'], ['fa-i-cursor', '<i aria-hidden="true" class="fa fa-i-cursor"></i>'], ['fa-id-badge', '<i aria-hidden="true" class="fa fa-id-badge"></i>'], ['fa-id-card', '<i aria-hidden="true" class="fa fa-id-card"></i>'], ['fa-id-card-o', '<i aria-hidden="true" class="fa fa-id-card-o"></i>'], ['fa-image', '<i aria-hidden="true" class="fa fa-image"></i>'], ['fa-mail-reply-all', '<i aria-hidden="true" class="fa fa-mail-reply-all"></i>'], ['fa-reply', '<i aria-hidden="true" class="fa fa-reply"></i>'], ['fa-reply-all', '<i aria-hidden="true" class="fa fa-reply-all"></i>'], ['fa-retweet', '<i aria-hidden="true" class="fa fa-retweet"></i>'], ['fa-wrench', '<i aria-hidden="true" class="fa fa-wrench"></i>']] for item in ICON_LIST: item[1] = mark_safe(item[1]) from django import forms class MenuModelForm(forms.ModelForm): class Meta: model = models.Menu fields = ["title","icon"] widgets = { "title":forms.TextInput(attrs={"class":"form-control"}), 'icon': forms.RadioSelect( choices=ICON_LIST, attrs={'class': 'clearfix'} ) } def menu_add(request): if request.method == "GET": form = MenuModelForm() else: form = MenuModelForm(request.POST) if form.is_valid(): form.save() #这样写后就可以反向生成了。 return redirect(reverse("rbac:menu_list")) return render(request,"rbac/menu_add.html",{"form":form}) """ def role_edit(request,rid): obj=models.Role.objects.filter(id=rid).first() if not obj: return HttpResponse("角色不存在") if request.method == "GET": form = RoleModelForm(instance=obj) else: form = RoleModelForm(request.POST,instance=obj) if form.is_valid(): form.save() return redirect("/rbac/role/list/") return render(request,"rbac/role_edit.html",{"form":form}) """
-
html
layout.html {% load static %} <!--导入静态文件--> {% load rbac%} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>路飞学城</title> <!--应用静态文件中的其他文件--> <link rel="shortcut icon" href="{% static 'imgs/luffy-study-logo.png' %} "> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.css' %} "/> <link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %} "/> <link rel="stylesheet" href="{% static 'css/commons.css' %} "/> <link rel="stylesheet" href="{% static 'css/nav.css' %} "/> <link rel="stylesheet" href="{% static 'rbac/css/rbac.css' %} "/> <style> body { margin: 0; } .no-radius { border-radius: 0; } .no-margin { margin: 0; } .pg-body > .left-menu { background-color: #EAEDF1; position: absolute; left: 1px; top: 48px; bottom: 0; width: 220px; border: 1px solid #EAEDF1; overflow: auto; } .pg-body > .right-body { position: absolute; left: 225px; right: 0; top: 48px; bottom: 0; overflow: scroll; border: 1px solid #ddd; border-top: 0; font-size: 13px; min-width: 755px; } .navbar-right { float: right !important; margin-right: -15px; } .luffy-container { padding: 15px; } </style> {% block css %}{% endblock %} </head> <body> <div class="pg-header"> <div class="nav"> <div class="logo-area left"> <a href="#"> <img class="logo" src="{%static 'imgs/logo.svg'%}"> <span style="font-size: 18px;">路飞学城 </span> </a> </div> <div class="left-menu left"> <a class="menu-item">资产管理</a> <a class="menu-item">用户信息</a> <a class="menu-item">路飞管理</a> <div class="menu-item"> <span>使用说明</span> <i class="fa fa-caret-down" aria-hidden="true"></i> <div class="more-info"> <a href="#" class="more-item">管他什么菜单</a> <a href="#" class="more-item">实在是编不了</a> </div> </div> </div> <div class="right-menu right clearfix"> <div class="user-info right"> <a href="#" class="avatar"> <img class="img-circle" src="{% static 'imgs/default.png'%}"> </a> <div class="more-info"> <a href="#" class="more-item">个人信息</a> <a href="#" class="more-item">注销</a> </div> </div> <a class="user-menu right"> 消息 <i class="fa fa-commenting-o" aria-hidden="true"></i> <span class="badge bg-success">2</span> </a> <a class="user-menu right"> 通知 <i class="fa fa-envelope-o" aria-hidden="true"></i> <span class="badge bg-success">2</span> </a> <a class="user-menu right"> 任务 <i class="fa fa-bell-o" aria-hidden="true"></i> <span class="badge bg-danger">4</span> </a> </div> </div> </div> <div class="pg-body"> <div class="left-menu"> <div class="menu-body"> {#{% menu request %}#} </div> </div> <div class="right-body"> <div> {#{% breadcrumb request %}#} </div> {% block content %} {% endblock %} </div> </div> <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script> <script src="{% static 'plugins/bootstrap/js/bootstrap.js' %}"></script> <script src="{% static 'rbac/js/rbac.js' %}"></script> {% block js %} {% endblock %} </body> </html> role_list.html {% extends "layout.html"%} {% block content %} <div class="luffy-container"> <div class="btn-group" style="margin: 5px 0"> <a href="{% url 'rbac:role_add' %}" class="btn btn-default" > <i class="fa fa-plus-square" aria-hidden="true"></i> 添加客户 </a> </div> <table class="table table-bordered table-hover"> <thead> <tr> <th>ID</th> <th>角色</th> <th>选项</th> <th>个数</th> </tr> </thead> <tbody> {% for row in role_queryset %} <tr> <td>{{ row.id }}</td> <td>{{ row.title }}</td> <td>{{ row.permissions.count }}</td> <td> <a style="color: #333333;" href="{% url 'rbac:role_edit' rid=row.id %}"> <i class="fa fa-edit" aria-hidden="true" ></i></a> | <a style="color: #d9534f;" ><i class="fa fa-trash-o" ></i></a> </td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %} role_add.html {% extends 'layout.html' %} {% block content %} <div class="luffy-container"> <form class="form-horizontal clearfix" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group col-sm-6 clearfix"> <label class="col-sm-3 control-label">{{ field.label }}</label> <div class="col-sm-9"> {{ field }} <span style="color:firebrick;">{{ field.errors.0 }}</span> </div> </div> {% endfor %} <div class="form-group col-sm-12"> <div class="col-sm-6"> <div class="col-sm-offset-3"> <button type="submit" class="btn btn-primary">提 交</button> </div> </div> </div> </form> </div> {% endblock %} role_edit.html {% extends 'layout.html' %} {% block content %} <div class="luffy-container"> <form class="form-horizontal clearfix" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group col-sm-6 clearfix"> <label class="col-sm-3 control-label">{{ field.label }}</label> <div class="col-sm-9"> {{ field }} <span style="color:firebrick;">{{ field.errors.0 }}</span> </div> </div> {% endfor %} <div class="form-group col-sm-12"> <div class="col-sm-6"> <div class="col-sm-offset-3"> <button type="submit" class="btn btn-primary">提 交</button> </div> </div> </div> </form> </div> {% endblock %} menu_list.html {% extends 'layout.html' %} {% block css %} <style> tr.root{ background-color:#fif7fd; } </style> {% endblock %} {% block content %} <div class="col-sm-3"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-universal-access" aria-hidden="true"></i>菜单管理 <a href="{% url 'rbac:menu_add' %}" class = "btn btn-success btn-xs" style="padding:apx 8px;margin:-3px;float:right"> <i class="fa fa-plus-circle" aria-hidden="true"></i> 新建 </a> </div> <table class="table"> <thead> <th>名称</th> <th>图标</th> <th>选项</th> </thead> <tbody> {% for row in menu_queryset %} <tr><!--不加路劲自动跳到当前页面--> <td><a href="?mid={{row.id}}">{{row.title}}</a></td> <td><i class="fa {{row.icon}}" aria-hidden="true"></i></td> <td> <a style="color: #333333;" href="{% url 'rbac:role_edit' rid=row.id %}"> <i class="fa fa-edit" aria-hidden="true" ></i></a> | <a style="color: #d9534f;" ><i class="fa fa-trash-o" ></i></a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> <div class="col-sm-9"> <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"><i class="fa fa-universal-access" aria-hidden="true"></i>菜单管理</div> <table class="table"> <thead> <th>id</th> <th>name</th> </thead> <tbody> {% for row in permission_queryset %} <tr class="root"> <td>{{row.title}}</td> <td>{{row.url}}</td> <td> <a style="color: #333333;"> <i class="fa fa-edit" aria-hidden="true" ></i></a> | <a style="color: #d9534f;" ><i class="fa fa-trash-o" ></i></a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> {% endblock %} menu_add.html {% extends 'layout.html' %} {% block content %} <div class="luffy-container"> <form class="form-horizontal clearfix" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group col-sm-6 clearfix"> <label class="col-sm-3 control-label">{{ field.label }}</label> <div class="col-sm-9"> {{ field }} <span style="color:firebrick;">{{ field.errors.0 }}</span> </div> </div> {% endfor %} <div class="form-group col-sm-12"> <div class="col-sm-6"> <div class="col-sm-offset-3"> <button type="submit" class="btn btn-primary">提 交</button> </div> </div> </div> </form> </div> {% endblock %}
### 2.3 forloop - 在{% for %}循环内部,可以访问一个名为forloop的模板变量。这个变量有若干属性,通过它们可以获知循环进程的一些信息。 ```python forloop.counter forloop.counter 的值是一个整数,表示循环的次数。这个属性的值从 1 开始,因此第一次循环时,forloop.counter 等于 1 。 {% for item in todo_list %} <p>{{ forloop.counter }}: {{ item }}</p> {% endfor %} forloop.counter0 forloop.counter0 与 forloop.counter 类似,不过是从零开始的。第一次循环时,其值为 0 。 forloop.revcounter forloop.revcounter的值是一个整数,表示循环中剩余的元素数量。第一次循环时, forloop.revcounter 的值是序列中要遍历的元素总数。最后一次循环时, forloop.revcounter的值为 1 。 forloop.revcounter0 forloop.revcounter0 与 forloop.revcounter类似,不过索引是基于零的。第一次循环时, forloop.revcounter0的值是序列中元素数量减去一。最后一次循环时, forloop.revcounter0 的值为 0 。 forloop.first forloop.first 是个布尔值,第一次循环时为 True 。需要特殊处理第一个元素时很方便: {% for object in objects %} {% if forloop.first %} <li class="first"> {% else %} <li> {% endif %} {{ object }} </li> {% endfor %} forloop.last forloop.last是个布尔值,最后一次循环时为True 。经常用它在一组链接之间放置管道符号: {% for link in links %} {{ link }}{% if not forloop.last %} | {% endif %} {% endfor %} 上述模板代码的输出可能是: Link1 | Link2 | Link3 | Link4 此外,还经常用它在一组单词之间放置逗号: <p>Favorite places:</p> {% for p in places %} {{ p }}{% if not forloop.last %}, {% endif %} {% endfor %} forloop.parentloop 在嵌套的循环中, forloop.parentloop引用父级循环的 forloop 对象。下面举个例子: {% for country in countries %} <table> {% for city in country.city_list %} <tr> <td>Country #{{ forloop.parentloop.counter }}</td> <td>City #{{ forloop.counter }}</td> <td>{{ city }}</td> </tr> {% endfor %} </table> {% endfor %} 小贴士 forloop 变量只在循环内部可用。模板解析器遇到 {% endfor %} 时, forloop 随之消失。 上下文和 forloop 变量 在 {% for %} 块中,现有变量会让位,防止覆盖 forloop 变量。Django 把移动的上下文放到 forloop.parentloop 中。通常,你无须担心,但是如果有名为 forloop 的模板变量(不建议这 么做),在 {% for %} 块中会重命名为 forloop.parentloop 。 ———————————————— 版权声明:本文为CSDN博主「似水@流年」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/mr_hui_/article/details/88310822
2.4 输入框互斥
-
models.py
from django.db import models # Create your models here. class Menu(models.Model): """ 菜单表 """ #需要创建唯一索引 title = models.CharField(max_length=32,unique=True) icon = models.CharField(max_length=32) #显示文字,不加的话是对象 def __str__(self): return self.title class Permission(models.Model): """ 权限表 """ title = models.CharField(verbose_name='标题', max_length=32) url = models.CharField(verbose_name='含正则的URL', max_length=128) name = models.CharField(verbose_name='URL别名', max_length=32,null=True,blank=True) #limit_choices_to在显示前做筛选,显示parent为空的 parent = models.ForeignKey(verbose_name="父权限",to="permission",null=True,blank=True,on_delete=models.CASCADE,limit_choices_to={"parent__isnull":True}) menu = models.ForeignKey(verbose_name="菜单",to="Menu",null=True,blank=True,on_delete=models.CASCADE) def __str__(self): return self.title class Role(models.Model): """ 角色 """ title = models.CharField(verbose_name='角色名称', max_length=32) permissions = models.ManyToManyField(verbose_name='拥有的所有权限', to='Permission', blank=True) def __str__(self): return self.title
-
role.py
from django.shortcuts import render,redirect #用于反向生成url本质上都是调用的这里。 from django.urls import reverse from rbac import models # Create your views here. def role_list(request): role_queryset = models.Role.objects.all() return render(request,"rbac/role_list.html",{"role_queryset":role_queryset}) from django import forms class RoleModelForm(forms.ModelForm): class Meta: model = models.Role fields = ["title"] widgets = { "title":forms.TextInput(attrs={"class":"form-control"}) } error_messages = {"title":{"required":"用户名不能为空"}} #help_texts编写帮助信息 help_texts = {"title":"sdf"} """ 菜单和权限只能选一个,这个函数中没有。 只是没出写了,随便放个位置 def clean(self): menu = self.cleaned_data.get("menu") parent = self.cleaned_data.get("parent") if menu and parent: self.add_error("menu","菜单和权限只能选一个") """ def role_add(request): if request.method == "GET": form = RoleModelForm() else: form = RoleModelForm(request.POST) if form.is_valid(): form.save() #这样写后就可以反向生成了。 return redirect(reverse("rbac:role_list")) return render(request,"rbac/role_add.html",{"form":form})
-
html
{% extends 'layout.html' %} {% block content %} <div class="luffy-container"> <form class="form-horizontal clearfix" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group col-sm-6 clearfix"> <label class="col-sm-3 control-label">{{ field.label }}</label> <div class="col-sm-9"> {{ field }}{{field.help_text}} <span style="color:firebrick;">{{ field.errors.0 }}</span> </div> </div> {% endfor %} <div class="form-group col-sm-12"> <div class="col-sm-6"> <div class="col-sm-offset-3"> <button type="submit" class="btn btn-primary">提 交</button> </div> </div> </div> </form> </div> {% endblock %}
2.5 formset
-
views.py
#可以实现多次打印和多次输入,从而实现批量操作 from django.shortcuts import render from django import forms class UserForm(forms.Form): id = forms.CharField(required=True) user = forms.CharField() pwd = forms.CharField() email = forms.CharField() """ # Create your views here. def index(request): #额外设置extra设置它的打印多少个 UserFormSet = forms.formset_factory(UserForm,extra=4) if request.method == "GET": formset = UserFormSet() return render(request,"index.html",{"formset":formset}) formset = UserFormSet(request.POST) if formset.is_valid(): for row in formset.cleaned_data: print(row) return render(request,"index.html",{"formset":formset}) """ def index(request): UserFormSet = forms.formset_factory(UserForm,extra=0) if request.method == "GET": formset = UserFormSet(initial=[{"id":1,"user":"alex","pwd":"123","email":"alex@qq.com"}]) return render(request,"index.html",{"formset":formset}) formset = UserFormSet(request.POST) if formset.is_valid(): for row in formset.cleaned_data: id = row.pop("id") #models.User.objects.filter(id=id).update(**row) return render(request,"index.html",{"formset":formset})
-
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <title></title> <style> .hide{ display:none; } </style> </head> <body> <form method="POST"> {{formset.management_form}} <!--写上他才可以使用formset_factory--> <table> <tr> <th>用户名</th> <th>密码</th> <th>邮箱</th> </tr> {% for form in formset %} <tr> {% for field in form %} {% if forloop.first %} <td class="hide" >{{field}}</td> {% else %} <td>{{field}} {{field.errors.0}}</td> {% endif %} {% endfor %} </tr> {% endfor %} </table> <input type="submit" value='提交'> </form> </body> </html>
3.其他
3.1 家庭树
-
views.py
#这个里面最重要的一点就是,内存中存储的位置是固定的 #我觉得你们应该可以看懂,那个是固定的。 comment_list = [ {"id":1,"title":"asdf","pid":None}, {"id":1,"title":"asdf","pid":1}, {"id":1,"title":"asdf","pid":2}, {"id":1,"title":"asdf","pid":3}, ] #每一条中加一个children #id为键转为字典 comment_dict = {} for item in comment_list: item["children"] = [] comment_dict[item["id"]] = item result = [] for row in comment_list: if not row["pid"]: result.append(row) else: pid = row["pid"] comment_dict[pid]["children"].append(row) print(result) """ [ {'id': 1, 'title': 'asdf', 'pid': None, 'children': [ {'id': 2, 'title': 'asdf', 'pid': 1, 'children': [ {'id': 3, 'title': 'asdf', 'pid': 2, 'children': [ {'id': 4, 'title': 'asdf', 'pid': 3, 'children': []}]}]}]}] """
标签:__,CRM,models,menu,request,django,import,权限,crm 来源: https://www.cnblogs.com/fxy1024/p/16386173.html