模块七 crm项目课程笔记


crm 通用组件

1、权限 组件 (通用)

  • 为什么需要权限控制 不同的人有不同的角色看到不同信息 现实场景需要
  • 为什么要开发权限组件 积累 通用功能 用的很多
  • web 什么是权限? url路由地址 == 权限 一个人权限 对应 的url多少 多对多关系 权限表结构 迭代表结构设计
第一版 2张表 基于用户的表结构设计  问题同类用户修改权限比较麻烦   每个人都需要去修改相应的权限权限过多的时候修改比较麻烦
 person
uid 
name
dep
age
permission = mtm(to='permissions')

权限 permissions
pid=
name=
url = 

解决方法不在以用户为基础而是以角色一类人统一修改角色的权限修改了角色对应的一类人权限也会相应修改基于角色的表结构设计 第二版
user
uid
name
age
role = foreignkey role ==  MTM role  一个人有多个角色  一个角色有多个人  

role
rid
name
permission = MTM permissions

permissions
pid
name
url

user = role = permissions

RBAC 基于角色的权限控制

project类

大目标:crm系统

  • 权限 通用组件
  • stark组件
  • crm业务

步骤:

1创建django projectluffy_permission
2创建两个APP
- rbac  权限公共组件
- web  销售管理系统
3app rbac  == app01 
-- 将权限相关的表编写到此app的models.py中
4app web
- 将销售管理系统的表写到此app的models.py中
- 销售系统的业务相关代码 luffy_permission(示例二)
5两个app的整合 
销售管理系统中的URL
客户管理
账单管理
5.1 基于admin权限信息的录入
5.2 基于admin权限和角色信息的分配
6. 快速完成一个基本权限控制   在实例代码  开始开发
中间件ORMsession   视图views
1登录页面是否有权限访问
2post请求用户登录检验是否合法
3获取当前用户相关的所有权限病放入session
4再次想服务端发起请求 http:www.xx/xxx后端编写中间件对用户当前访问url进行权限的判断是否在session中== session表中

7功能的完善 将权限相关功能放到rbac app下以便以后组件的应用
- 用户登录和权限初始化拆分  权限函数模块封装 services
    - 配置文件应用  session_key放在setting中
总结67  属于进行权限控制
8.动态菜单功能
- 一级菜单    
- 问题 如何实现动态显示菜单 结合目前rbac和表结构   拿到对应菜单名url == 模板展示循环判断有就加一个没有就不显示对应的菜单名和url   ==  怎么拿到对应菜单名 简单来说直接在权限表中增加标注是否菜单的字段这样就可以判断是否是菜单
设计
权限  菜单的关系   增加/查看可以  编辑/删除不行   权限包含菜单    一个权限 == 一个菜单  其实是一对一关系   直接增加一列判断是否是菜单  
session视图中间件

写代码

1、models permissions 添加menu字段
2、创建session 保存菜单列表
3、登录拿到权限之后通过session获取显示菜单
4、从模板中拿到对应菜单循环展示

a.  表结构修改 + 录入菜单数据 
b. 获取菜单信息并保存到session
c. 模板中显示菜单信息 session
ps:inclusion_tag("class.html")
扩展:访问谁就在谁里面显示     默认选中
null = True  blank = True admin中可以为空 
default = False
font awesome  图片 icon  


- 二级菜单
评论树  父id   
a. session中存储的菜单信息的结构  之前是[{},{}] 循环列表 get(key)

{
1:{
"title":"",
 icon:"",
children:[
{'title':'客户列表''url':'/customer/list/'},
{'title':'客户列表''url':'/customer/list/'},
]
},

2:{
"title":"",
 icon:"",
children:[
{'title':'个人资料''url':'/userinfo/list/'},
]
},

}
b. 数据库表结构  
一级没有urlurl在二级新增一级菜单menu表二级菜单与一级的关系是一对多一个一级包含多个二级
如果二级是null  说明没有一级不是菜单如果二级是非null说明有二级菜单对应的一级菜单mid  
c. 页面显示二级菜单
inclusion_tag中循环显示

开发二级菜单  客户管理之动态 菜单

9点击非权限菜单的时候显示菜单并选中    默认展开非菜单url 
当点击某个不能成为菜单的权限时指定一个可以成为菜单的权限让其默认选中以及展开
a. 数据库设计 自关联  
b. 思路
- 登录做权限和菜单的初始化 
- 获取菜单信息
- 获取权限信息

导航条  
路径导航  路飞线上源码
permissions__title     == 显示二级标签  父集  二级关联  第一级
pid  不为null  显示二级标签对应的id  
permissions__pid_id   pid null 
permissions__pid__title   父权限信息    非二级标签   子集   第二级
permissions__pid__url
11权限粒度控制到按钮级别 
修改permissions_list dict  加入key  中间件 values()   ==   model permissions增加name别名列(唯一)  取代url地址  类似url name  {% URL  name %} 在url中设置name动态更新  
总结
- 权限控制
- 动态菜单
- 权限分配
问题以前你是如何做的权限分配给某个用户分配一个角色某个人分配某个权限
django admin进行录入
自己实现权限分配  不仅仅CURD  三级菜单

12权限分配
a.角色管理
知识点
- ModelForm    setting  == zh-hans    批量forms添加class  
- 根据 namespace   name的反向解析URL
- 模板的查找顺序   rbac目录分开
- 模板重用编辑 添加 公用    保存默认值  (instance=obj) 
b.用户管理
知识点
- ModelForm  
- 字段的自定制  
- 钩子方法 二次校验  两次密码验证
- 错误提示(中文)  error_message = {"required":"该字段不能为空"}   
error_messages = {
            'name': {
                'max_length': _("This writer's name is too long."),
            },
        } 
- 重写 __init__ 方法统一给所有字段添加属性 form-contorl

c.菜单和权限的管理
d.批量的权限操作
e.分配权限 
rbac组件文档 ==  主机管理
添加携带原搜索条件跳转回来也携带原搜索条件  id=xx&c=yy
二级菜单  
row.id|safe 转换为字符串safe ==渲染不转义否则会转义渲染标签
<tr class="{% if row.id|safe == menu_id %}active{% endif %}">

三级菜单 -- 权限 
url name 模板自定义tag 反向解析   

c 菜单和权限管理  多级菜单 
讲解
- 一级  
- 二级
- 三级
知识点
- 保留URL中的原搜索条件
- 模板中整型转换字符串  1|safe 做判断  
- ModelForm 定制radio
- ModelForm 显示默认值 
- ModelForm save之前对其instance进行修改 携带用户输入
- BootstrapModelForm的基类  方便定义统一样式

d.权限批量操作 
- formset 
- 什么是formset
form组件或modelForm用于做一个表单验证  html渲染  对应一个表的一行数据   formset用于做多个表单验证组件  == 多行数据
- 应用场景
批量操作
批量增加

- 如何来使用formset
批量添加
批量修改 


- 自动发现项目中的URL
- 问题给你一个项目请帮我获取当前项目中都有哪些URL以及name  rbac:permission_list
- 实现思路


错误显示中文  zh-hans
Field.blank¶
如果是 True ,该字段允许为空。默认为 False 。
注意,这与 null 不同。 null 纯属数据库相关,而 blank 则与验证相关。如果一个字段有 blank=True,表单验证将允许输入一个空值。如果一个字段有 blank=False,则该字段为必填字段。

2、stark组件   单张、多张表 CURD  组件  )(通用)
3、crm 业务 30表 CURD 教育 luffy
- 如何来使用formset
批量添加
批量修改 


- 自动发现项目中的URL
- 问题给你一个项目请帮我获取当前项目中都有哪些URL以及name  rbac:permission_list
- 实现思路


错误显示中文  zh-hans
1获取项目中所有权限  set1
2去数据库中获取已经录入的所有权限  set2 

情况一自动发现有  > 数据库无 ==  实现批量添加  ps:通过name进行对比  set1 - set2 = 添加  formset
情况二数据库多 > 自动发现 - 实现批量删除  name进行对比   set2 - set1 = 删除  formset
情况三自动发现 == 数据库  - 实现批量更新   set3 = set1 & set2 =  更新  formset 
列表生成式 []  三元表达式  条件成立 if 条件 else 不成立   
菜单 == 权限

权限批量操作
- formset  ModelFormSet
- 自动发现项目URL 递归 正则
- 唯一约束的错误信息
-  批量添加

权限分配
- 展示用户角色权限信息
- 选择用户角色时页面上默认权限
- 角色权限的分配 保存

录入权限信息

菜单  
能做菜单的权限  
不能做菜单的权限

list dict 可变类型  引用 == 内存地址   
dict  if key in dict  ==> hash 

a标签 get请求 url ?row.id 
form  button提交 ajax 异步 不刷新提交   action="?type=x1"  == 隐藏input标签  
{% if row.id|safe == user_id %}  safe转换为字符串     blog显示标签 safe  js注入  safe CSRF 
objects.filter().exist()

知识点总结:

  • 数据类型设置引用
  • M2M 关系表的更新

权限控制到按钮,templates中的做f判断 分配权限 测试 登录 查看权限

RBAC 编写使用文档 主机管理程序
1将RBAC组件拷贝到项目
2将RBAC/migrations目录中的数据迁移记录删除
3进行业务开发
3.1 对于用户表的的处理   o2o  将用户表拆分到两张表中跟auth组件类似采用django_admin
缺点用户表数据分散
优点利用上rbac中的用户管理的功能 
3.2 用户表整合在一张表中推荐
class Meta:
# django以后再做数据库迁移时,不再为Userinfo类创建相关的表以及表结构
# 此类可以当做 ”父类“ 被其他Model类继承
abstract = True
优点将所有用户信息放入到一张表业务的用户表中
缺点在rbac中所有关于用户表的操作不能使用了
注意rbac中两处使用了用户表
- 用户管理 删除
- 权限分配时用户列表 读取业务中的用户表即可

严重提醒
Role 不要加引号

对于rbac中代码修改:

  • 1、在URL中将用户表的增删改查和修改密码功能删除
  • 2、在权限分配时,读取用户表变成通过配置文件来进行指定并导入

  • 3.3 业务开发:

    • 用户表的CURD
    • 主机表的增删改查

如果要使用rbac中的模板,则需要将模板中的 导航条 + 菜单 去掉,当业务开发完成之后,上线之前在拿回来 a 跳转 href = "" memory_reverse 带上搜索条件

PS:
控制到按钮级别,没有权限就不显示,只有有权限才显示对应功能
用户登录验证 只是把权限信息保存到session,之前做的是auth验证通过登录(自动实现session保存) 否则需要自己做session的增删 == 自己实现session登录操作
流程
业务系统中用户表结构设计 
将业务系统中用户表的路径写到配置文件中 
- 用于RBAC分配权限时读取业务表中的用户信息
业务逻辑开发
- 将所有的路由都设置一个name
- 用于反向生成URL以及粒度控制到按钮的权限控制
权限信息的录入
- 在URL中添加rbac路由分发 注意 必须设置namespace
- rbac提供的地址进行操作
相关配置自动发现URL排除的URL列表

编写用户登录逻辑 权限初始化
相关的配置权限和菜单的session key

编写一个首页的逻辑
相关配置需要登录但无需权限的URL

通过中间件进行权限校验
权限校验
白名单无需登录就可以访问

粒度到按钮的配置

总结:目的希望在任意系统中应用权限系统,

  • 用户登录 + 用户首页 + 用户注销
  • 项目的业务逻辑开发 注意:开发时灵活的去设置layout.html中的两个inclusion_tag

开发时 去掉 上线时,加回

  • 权限信息录入
  • 配置文件
  • 粒度控制到按钮级别
2stark组件   单张多张表 CURD  组件  )(通用
介绍是一个帮助开发者加快实现数据库表的增删改查 + 
目标10s中完成一张表的增删改查
前戏
1django项目启动时自定义执行某个py文件
 django启动时且在读取项目中路由加载之前执行某个py文件
在任意app的apps.py中国Config类中定义ready方法并调用autodiscover_modules     导入模块 ==执行xxx.py文件
django在启动时就会去已注册的所有app目录下找 xxxx.py并自动导入
如果执行两次是因为django内部自动重启导致
Python manage.py runserver 0.0.0.0:8080 --noreload 
提示
如果xxx.py执行代码向某个神奇的地方放入了一些值之后路由加载时再去某个神奇的地方读取到原来设置的值
2单例模式   文件导入的方式
一个
实例对象

通过利用Python模块导入的特性python中如果已经导入过的文件再次被重新导入时Python不会在重新解释一遍而是选择从内存中直接将原来导入的值拿来使用
导入多次使用的都是最开始创建的对象不会重复导入

提示
如果以后存在一个单例模式对象可以先在此对象中放入一些值然后在其他文件中导入该对象通过对象再次将值获取到

3django路由分发的本质 include  函数主要返回有三个元素的元祖   
方式一
方式二
方式三 在源码内部读取路由时 urls.patterns  getattr()    直接路由分发

01

开始:

  • 1、创建django project
  • 2、创建基础业务表
  • 3、对以上的三张表做增删改查
    • a.分析
      • 为每张表创建4个url
      • 为每张表创建4个视图函数
        • b 为APP中的每个model类自动创建URL以及相关视图
2个面向对象脚本
1、不同对象封装的self不同,obj封装自己对象的数据  
2、继承 基类,相同方法无需重复编写
多态,多种形态
提取基类  自定制显示

前缀、添加url  
prev 生成URL前缀
钩子 url选择  只显示几个  再做一次路由分发  url分发
 - 修改url
 - 增加url 

b 为APP中每个model类自动创建URL以及相关视图函数
- 动态生成URL
- 将视图提取到基类
- URL分发 扩展 & 后缀
- 为URL设置别名  name  
app名称_表名称_list
注意继承  找到是那个类的对象,先从对应类开始找,没有在找父类的方法 
- URL别名进行重新生成
stark组件开发之列表页面定制列
c. 定制页面显示的列
用户对象  __str__ 打印出来   显示列  不是obj
list_display = ['name','age']   页面上要显示列
1处理表格的表头  类似admin的获取verbose_name 
2处理表的内容 

注意 self 是谁的对象就从那个类开始查找

- 未定义list_display字段的页面默认显示对象  头显示表名
- 扩展点   获取页面上应该显示的列   根据用户角色不同显示不同的列
>>> a = [1,2,3]
>>> a.extend(['name','age'])  extend() 把列表批量添加新列表中
>>> a
[1, 2, 3, 'name', 'age']
- 为页面显示的列预留一个钩子函数
定制显示编辑和删除按钮  return make_safe('<a>编辑</a>')  以安全的方式显示 == 标签显示 模板中用 |safe 来显示标签  转化字符串 |safe   
- 为页面提供自定义函数 
函数和方法 
函数  Foo.func(self,name)   .func  函数 
方法  obj.func(name)    obj.func 方法
- 应用
反向生成url  

定制字段

02

get_classes_display() model choice 字段显示:

04

choice字段想显示中文,就必须使用obj.get_表名_display() 3个厂商 choice 阿里云/华为云/ucloud云

页面优化 
d.应用模板样式(基于bootstrap)
e.django 分页 Page  
根据URL中获取的 page=3 
1、根据用户访问的页码,计算出数据库索引位置
2、生成HTML的页码
自定义分页实现
添加列表分页功能 



f.页面添加
不同用户显示添加按钮
权限判断 
参数  has_add_btn = True
钩子 get_add_btn()
- 如何显示添加按钮
- 添加按钮的URL
 根据别名反向生成URL 类似之前 reverse生成对应的url  
 权限中记录原记录搜索条件
QueryDict(mutable=True)  可修改默认不能修改 
QueryDict.urlencode(safe=None)
返回一个查询字符串格式的数据字符串

装饰器@来引用 
类中def使用装饰器  都可以在类里面也可以自定义装饰器直接在调用的地方引用也是一样的注意
@xx  == func = xx(func)  可以写成 xx(func) 

import types
from functools import wraps

class Profiled:
    def __init__(self, func):
        wraps(func)(self)
        self.ncalls = 0

    def __call__(self, *args, **kwargs):
        self.ncalls += 1
        return self.__wrapped__(*args, **kwargs)

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return types.MethodType(self, instance)

@Profiled
def add(x, y):
    return x + y

class Spam:
    @Profiled
    def bar(self, x):
        print(self, x)
添加页面
自定义ModelForm   clean_all  errors[0]
使用ModelForm保存数据之前预留的钩子方法  可以配置显示字段和添加之前设置默认值

编辑
注意get带条件
- 编辑按钮(删除按钮)
- 页面操作  ModelForm instance=obj.pk 当前编辑的默认值填充  

删除
CURD 



其他常用功能
a. 排序
b. 模糊搜索
- 实现思路:在页面上设置form表单,搜索:以get形式提交请求到后台,后台获取数据进行筛选过滤 (最简单实现方式form表单实现)
后端获取到关键字之后,根据定义的列进行查找 (多列按照或进行查询)
定义钩子方法
1、如果search_list中没有值,则不显示搜索框
2、获取用户提交的关键字
3、构造条件
filter多条件默认and  
- Q 对象  多条件查询   与或非  

c. 批量操作
- 添加CheckBox列
- 生成批量操作的按钮
自定义套路def get_action_list  self.action_list
思路 用if判断,批量删除 == 对应func  批量修改 == 对应func  
实际 用函数对象实现  func.text = 批量删除   func.text = 批量编辑
模板中{{ func.text }} 会自动加() 执行
函数对象获取函数名   func.__name__
name="pk"  ==> request.post.getlist('pk','')
value = "{{ func.name }}" ==> request.GET.get('aciton')  ==> name="action"  获取的是{{ func.name }}值
name定义是get获取的变量,对应name的值就是value的值
如果想要定制执行成功后的返回值,那么就为action定义return 值即可
d. 组合搜索
- 什么是组合搜索
- 如果实现组合搜索
- 实现思路  根据字段找到其关联数据  choiceFKM2M  
- 第一步配置   默认配置
search_group=[
{'field':'gender','db_condition':{'id__gt':2},}
]
根据权限不同做不同的操作
- 第二步根据配置获取关联数据
django 1.11 获取字段对应的外键表对象  
field_object.rel.model.objects.filter(**item['db_condtion'])
- 第三步根据配置获取关联数据含条件
封装到类中对象实现不是简单的通过字段 or 元祖进行传值
封装条件 Myoption
- 第四步在页面上显示组合搜索的按钮
- 将Queryset和元祖进行统一的封装
- 第五步 为组合搜索按钮生成URL  
- 生成URL时不影响其他组的条件
query_dict.urlencode(),text   request.GET.urlencode()
-  条件的筛选
-  多选

stark组件总结:

  • 页面: 列表、编辑、添加、删除
  • 模糊搜索、批量操作、组合搜索

第三部分 crm 业务

1、项目背景

以教育机构(老男孩)为背景crm项目,undo  系统主要为  销售部、运营部、教质部  提供平台,对他们的工作进行量化
有利于公司信息化建设    
销售部,qq群 内部转化    公户私户可以相互踢出 
- 公户  公共客户 
- 私户  我的客户  <= 150人  + 跟进记录 + 入班申请(财务审批)
运营部,seo sem 转化  
- 录入客户信息 (公户)
教质部,
- 考勤 
- 学员访谈  升学 转介绍
- 积分管理  问答 工作介绍
- 转班申请  留级 

学员 查看信息 CURD

扩展性更强

2、项目开发

2.1.概览
  • 基础业务处理
  • 校区管理
  • 部门管理
  • 用户管理
  • 课程管理
  • 开班管理
  • 客户管理
  • 公户
  • 私户
  • 学员管理
  • 考勤
  • 谈话记录
  • 积分
  • RBAC组件

先功能,最后在看model,串起来在看

2.2 开发
2.2.1 创建项目  python3 manage.py  makemigrations  migrate   
2.2.2 校区管理  设置stark默认显示,编辑删除在一列显示   CURD 快速实现    setting 中APP注册 rbac 执行顺序,layout.html 模板页面重复读取到菜单报错,调整顺序即可  
class School(models.Model):
pass 
2.2.3 部门管理
2.2.4 用户管理  开发自己的组件,方便快速实现通用功能  
- 用户的基础操作   
   添加页面确认密码    
   编辑不显示密码 删除密码字段  
   密码密文显示


2种方式:
方法一
  add_or_change=None
  if add_or_change == "add":
xx
  elif add_or_change == ‘change’:
       xx 

方法二
  is_add=False 

在UserInfoAddModelForm(StarkModelForm)中定义钩子函数
def clean_confiem_password(self):
pass

05

注意必须加返回值否则self.clean_data('password')就获取不到值在UserInfoAddModelForm中添加钩子函数

def clean(self):
    password = self.clean_data['password']
    self.clean_data['password'] = gen_md5(password)
    return self.cleaned_data
重置密码
用form不是ModelForm  不需要 数据库中的已有数据密码不需要instance展示   跟数据库无关采用forms
Auth 替代 request.session.get == 登录验证
密码更新到数据库  
通过pk=id 查到对应obj
obj.save()
之前有个问题如果用bookobj=models.objects.create() 会出现唯一重名的情况 要做字段唯一判断先创建对象
obj = models.books(xx="xx")
obj.save()
用户页面功能增加
  • 模糊搜索
  • 组合搜索 条件 多选
课程管理和代码拆分
班级管理  基础操作和定制  
models中字段属性   不清楚
related_name = "classes"   # 反向关联   参数来覆盖名字entry_set(相当于别名的作用)以起到相同的作用
limit_choice_to={"depart_id__in":[6,7]}   # 限制对应字段  ModelForm中显示时候的筛选条件  
blank = True    可以为空  blank 是针对表单的如果 blank=True表示你的表单填写该字段的时候可以不填比如 admin 界面下增加 model 一条记录的时候直观的看到就是该字段不是粗体
null = True    自己字段为空  null 是针对数据库而言如果 null=True, 表示数据库的该字段可以为空
显示时间M2M 字段 == 可以定制颜色 
基于limit_choice_to 关联FK或M2M 进行筛选
- 班级管理时间插件的应用stark组件新增datetimepicker组件  
4个步骤1引入css  2引入js  3js代码块 4div块
本地化location 只保留中文
  • 2.2.7 客户管理
    • 公户
公户基础管理
列表页面展示   注意 重写get_queryset()
录入客户信息  重写 model_form_class = PublicCustomerModelForm
class PublicCustomerModelForm(StarkModelForm):
class meta:
model = models.Customer
exclude = ['consultant',]
查看跟进记录
创建一张表 ConsultRecord(models.Model)
创建url 对应 templates  引用之前带参数的跳转显示自定义的模板
图标 font awesome  bootstrap    其他图标工具  iconfont 
申请到私户批量操作
select xx from xx  where xx  for update ; 
申请 == 审批 直接申请就通过了把对应公户直接转变成当前用户的私户    都能看到公户客户自己申请到自己的私户中
数据库锁  行级 表级锁 
事务 with transition: 
加锁select_for_update()  
私户个数限制 < 150 
session中读取用户id转变成私户的客户
用户登录公户申请到私户   crm 114
  • 私户
跟进记录管理
增加跟进进度
v1.py  site = StarkSite()  # 单例模式
改变url参数  reverse_list_url 
form.instance.customer_id = customer_id 
form.save()
编辑 删除 
value.append(type(self),display_edit_del)  获取子类的方法   子类用就用子类的 没有就用父类的

解决 添加、编辑、删除 漏洞,非法修改URL id  
添加漏洞 定义response 显示非法操作
编辑跟进记录 查看到其他用户下的客户的跟进记录

缴费&报名

  • 业务分析
  • 学员 缴费
  • 课程顾问:提交缴费申请
  • 财务: 审核(状态更新、入班学习)
  • 代码实现
  • 表结构设计
  • 查看缴费列表
  • 添加缴费记录
学员信息 2种方式   每次都填 or 只填一次后面获取
对应课程顾问 和  学员id 自动携带   获取课程顾问对应学生客户的id, 也需要上面的防漏洞  
基本的缴费记录 有无学生信息的情况 显示不同  对应2个ModelForm 
get_model_form_class()
exists 是否存在 --> first() 查询是否存在,不存在 not exist  or  not first()     
多对多 班级对应课程id



- 缴费审批:
注意:这里应该去拿student表中的name,案例中直接拿的consume中的客户学生对象  通过 def __str__() 显示的对应学生名和电话信息 ===  改成从consume对象获取学生表中对应的真实name和电话
缴费记录
客户表
学生表
涉及多张表更新,不能直接更新当前表
self.model_class.objects.filter(id__in=pk_list).update(confirm_status=2)
for pk in pk_list:
pass

2.2.8 学员管理

  • 学生管理

    • 已报班级 M2M
    • 组合筛选 M2M 字段 重写func_text 字段
    • 模糊搜索
  • 积分管理

积分记录表
去除编辑和删除 
跟上面私户 客户顾问id == 编辑的学生id  
当前登录用户 == 添加分数学生id  
上面漏洞问题是否存在?直接通过url进行修改其他学生积分
  • 考勤管理
    • 上课记录 增删改查 参考 跟进记录
    • 考勤记录 学生考勤记录 手动创建 == 批量自动生成
课程管理,对课程,学生端没有 

不用create  直接 对象.save()  用于判断是唯一值
循环创建 create 更换为先创建[obj,obj]对象列表,在批量执行

07

- 批量生成考勤记录
- 批量设置考勤记录   
先生成考勤 url
通过url 显示考勤记录 
生成多选页面 ModelFormSet  修改字段 'record'  取代 __all__  
modelformset_factory(models.StudyRecord,form=StudyRecordModelForm,extra=0) 
rbac formset  
{{ form.instance.student }}  form.instance 从数据库拿到的数据对象.student 获取对应字段值 而不是生成input标签
展示formset 
批量更新 form 
{% csrf_token %}
{{ formset.management_form }}
获取提交数据 request.POST
if formset.is_valid():
formset.save()
form提交报错form-0-id       不知道对应操作行id  需要加入当前行id  {{ form.id }}  隐藏的input框当前行id
PS可以用弹出框 + ajax提交来实现替代form跳转 刷新 HTTPResponse返回数据

2.2.9 权限应用

  • 基本权限校验
    • 参考文档
  • 粒度控制到按钮

总结:

  • 留存组件:rbac组件、stark组件 31节 代码 1000行代码 * 对比模块9的项目还重要
  • crm业务 (讲出来)销售 渠道 保证信息化安全 公户 私户 **
crm练习 
从stark开始 ===》 crm 实现 ==》 rbac加入  
最后rbac重写
  • 3、crm 业务 30表 CURD 教育 luffy