CRM项目表结构设计、登录注册、展示数据、删除、分页、搜索、编辑跳转、自定义simple_tag、事务 + 行级锁、批量初始化学习记录、每次请求 都做权限的校验、动态生成一级菜单、动态生成二级菜单、非菜单的权限的归属的问题

CRM

客户关系管理系统

表结构的设计

  • 客户表

  • 用户表

  • 校区表

  • 部门表

销售

  • 跟进记录表

  • 报名表

  • 缴费表

班主任

  • 班级表

  • 课程记录表

  • 学习记录表

登录注册

注册—— modelform

 from django import forms
 
 class Form(forms.ModelForm):
  class Meta:
  model = models.xxx
  fields = "__all__"  # ['name','pwd']
  exclude = ['is_active']
 
  def clean_字段名(self)
  # 校验规则
         # 通过 返回当前字段的值
         # 不通过 抛出异常
         # raise ValidationError('xx')
         
       
    def clean(self)
  # 校验规则
         # 通过 返回所有字段的值 self.cleaned_data
         # 不通过 抛出异常
         # self.add_error('re_password', '两次密码不一致!!')
         # raise ValidationError('xx') "__all__"
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         # 自定义的操作
         for name, field in self.fields.items():
             # print(name, field)
             if isinstance(field, (forms.BooleanField, MultiSelectFormField)):
                 continue
             field.widget.attrs['class'] = 'form-control'
             
             field.choices = [(),()]
             
 # 新增
 def change(request):
     form_obj = Form()
     if request.method =='POST':
         form_obj = Form(request.POST)
         if form_obj.is_valid():  # 对数据做校验的
             form_obj.save()
             # 返回展示页面
 
     return render(request,'form.html',{'form_obj':form_obj})
       
     
 
 # 编辑
 def change(request,pk):
     obj = models.XXX.objects.filter(pk=pk).first()
     form_obj = Form(instance=obj)
     if request.method =='POST':
         form_obj = Form(request.POST,instance=obj)
         if form_obj.is_valid():  # 对数据做校验的
             form_obj.save()
             # 返回展示页面
 
     return render(request,'form.html',{'form_obj':form_obj})
     
 # form.html
 
 {% for field in form_obj %}
 
  {{ field.label }}  _>  提示
    {{ field.id_for_label }}  _>  input框的id
  {{ field }}  _>  input
    {{ field.errors.0 }}  _>  该字段的第一个错误
     
 {% endfor %}
 
 
 {{ form_obj.as_p }}
 {{ form_obj.errors }}   ——》 所有字段的错误
 {{ form_obj.non_field_errors }}   ——》 __all__的错误
 
 
 

展示数据

 1. 普通字段
  obj.字段名   ——>  数据库的数据
     
 2. choices参数
  obj.字段名   ——>  数据库的数据
     obj.get_字段名_display()   ——>  要显示的数据
     
 3. 外键
  obj.外键   ——>  外键的对象   __str__
  obj.外键.字段   ——>  外键的对象某个字段的值
     
 4. 自定义的方法
  def show_class(self):
         return  ' , '.join( [  str(i) for i in self.class_list.all() ])
 
     返回HTML字符串,前端加safe 或者 python中加mark_safe
     from django.utils.safestring import mark_safe
 

删除

分页

page = 1

start = (page-1) * per_num

end= page * per_num

data[start:end]

模糊搜索

filter(Q(xxx__contains='xxxx')|Q(xxx__contains='xxxx'))

 def search(self,field_list):
     q = Q()
     q.connector = 'OR'
     
     Q(xxx__contains='xxxx')  # Q( ( 'xxx__contains', 'xxxx' ) )
     for field in field_list:
         q.children.append(Q(('{}__contains'.format(field),'xxx')))
 
     return q
     

分页保留搜索条件

 request.GET  # QueryDict   {'page':'1','query':'xxx'}
 request.GET.urlencode()  # page=1&query=xxx
 qd = request.GET.copy()  # 深拷贝 可编辑
 
 request.GET._mutable=True
 
 qd['page'] = 1
 qd.urlencode()   # ?{}

编辑后可以跳转到源页面

 1. 生成编辑的地址  地址携带 当前页面的url
 
  "{}?next={}".format(url,request.get_full_path())
  qd = QueryDict(mutable=True)
  qd['next'] = request.get_full_path()
 
  "{}?{}".format(url,qd.urlencode())
 
 
 2. 提交POST请求后跳转到next的地址
  next = requst.GET.get('next')
  retutn redirect(next)
 

模板中自定义方法 simple_tag

  1. 在app下创建一个名为templatetags的python包

  2. 在包内创建python文件 _> my_tags.py

  3. 在python文件中写固定内容

     form django import template
     register = template.Library()  # register的名字不能错
  4. 写函数 + 加装饰器

     @register.simple_tag
     def reverse_url(request,name,*args,**kwargs):
         qd = QueryDict(mutable=True
      qd['next'] = request.get_full_path()
         url = reverse(name,args=args,kwargs=kwargs)
         
      return "{}?{}".format(url,qd.urlencode())
     
  5. 在模板中使用

     {% load my_tags %}
     
     {% reverse_url request 'edit' pk %}

     

事务 + 行级锁

谁先来谁先操作数据

 with transaction.atomic():
             # 事务
     query_set = models.Customer.objects.filter(pk__in=pks, consultant=None).select_for_update()  # 加锁
 
    if len(pks) == query_set.count():
  query_set.update(consultant=self.request.user_obj)
  else:
         return HttpResponse('你的手速太慢了,需要再练练')

 

批量初始化学习记录

 models.StudyRecord.objects.bulk_create(study_record_obj_list)

展示和编辑学习记录 —— modelformset

 from django.forms import modelformset_factory
 
 ModelFormSet = modelformset_factory(models.StudyRecord, StudyRecordForm, extra=0)
 study_records = models.StudyRecord.objects.filter(course_record_id=course_record_id)
 
 formset = ModelFormSet(queryset=study_records)
 
 
 {{ formset.management_form }}
 
 {% for form in formset %}
     <tr>
        {{ form.id }}
         <td>{{ form.instance.student }}</td>
         <td>{{ form.attendance }}</td>
         <td>{{ form.score }}</td>
         <td>{{ form.homework_note }}</td>
         <td class="hidden">{{ form.course_record }}
        {{ form.student }}</td>
     </tr>
 {% endfor %}
 

权限

web开发中 url就是权限

如何实现权限控制?

RBAC

一、 表结构

 # 实现权限控制
 
 class Permission(models.Model):
     url = models.CharField(max_length=64,verbose_name='权限') # url地址的正则表达式
     title = models.CharField(max_length=32,verbose_name='标题')
     
 class Role(models.Model):
     name = models.CharField(max_length=32,verbose_name='角色名称')
     permissions = models.ManyToManyField('Permission',blank=True)
     
   
 class User(models.Model):
     username = models.CharField(max_length=32,verbose_name='用户名')
     password = models.CharField(max_length=32,verbose_name='密码')
     roles = models.ManyToManyField('Role',blank=True)
     
 # 动态生成一级菜单
 
 class Permission(models.Model):
     url = models.CharField(max_length=64,verbose_name='权限')
     title = models.CharField(max_length=32,verbose_name='标题')
     is_menu = models.BooleanField(default=False,verbose_name='是否是菜单')
     icon = models.CharField(max_length=32,null=True,blank=True,verbose_name='图标')
     
 class Role(models.Model):
     name = models.CharField(max_length=32,verbose_name='角色名称')
     permissions = models.ManyToManyField('Permission',blank=True)
     
 class User(models.Model):
     username = models.CharField(max_length=32,verbose_name='用户名')
     password = models.CharField(max_length=32,verbose_name='密码')
     roles = models.ManyToManyField('Role',blank=True)
     
     
     
 # 动态生成二级菜单
 class Menu(models.Model):
     title = models.CharField(max_length=32,verbose_name='标题')
     icon = models.CharField(max_length=32,verbose_name='图标')
     weight = models.IntergerField(default=0)
 
 
 class Permission(models.Model):
     """
    menu_id 有menu_id 二级菜单 没有menu_id 普通权限
    """
     url = models.CharField(max_length=64,verbose_name='权限')
     title = models.CharField(max_length=32,verbose_name='标题')
     menu = models.ForeignKey('Menu',null=True,blank=True)
   
     
 class Role(models.Model):
     name = models.CharField(max_length=32,verbose_name='角色名称')
     permissions = models.ManyToManyField('Permission',blank=True)
     
 class User(models.Model):
     username = models.CharField(max_length=32,verbose_name='用户名')
     password = models.CharField(max_length=32,verbose_name='密码')
     roles = models.ManyToManyField('Role',blank=True)

二、 登录成功后 查询当前用户的权限信息 并保存到session中

 #  简单的控制
 
 permissions = obj.roles.exclude(permissions__url=None).values('permissions__url').distinct()
 # QuerySet<[ {'permissions__url':'xxxx'} ]>
 
 request.session['permissions'] = list(permissions)
 
 
 # 动态生成一级菜单
 
 permissions = obj.roles.exclude(permissions__url=None).values(
     'permissions__url',
     'permissions__is_menu',
     'permissions__title',
     'permissions__icon',
 ).distinct()
 # QuerySet<[ {'permissions__url':'xxxx'} ]>
 
 # 权限列表
 permission_list = [ { 'url': i['permissions__url'] } ]
 # 菜单的列表
 menu_list = [
    { 'url'  'title'  'icon'}
 ]
 
 
 request.session['permissions'] = permission_list
 request.session['menus'] = menu_list
 
 
 # 动态生成二级菜单
 
 permissions = obj.roles.exclude(permissions__url=None).values(
     'permissions__url',
     'permissions__title',
     'permissions__menu__title',
     'permissions__menu__icon',
     'permissions__menu__weight',
     'permissions__menu_id',
 ).distinct()
 # QuerySet<[ {'permissions__url':'xxxx'} ]>
 
 # 权限列表
 permission_list = [ { 'url': i['permissions__url'] } ]
 
 # 菜单的列表
 menu_dict = {
     
     一级菜单的id : {
         'title':'一级菜单的标题'
         'icon':'一级菜单的图标'
         'weight':'一级菜单的权重'
         'children': [
            { 'url'   'title' }
             
        ]
    }
     
 }
 
 
 request.session['permissions'] = permission_list
 request.session['menus'] = menu_dict
 
 

三、 每次请求 都做权限的校验

中间件 process_request

 from djang.conf import settings
 
 # 获取了当前的url地址
 url = request.path_info
 
 # 白名单
 for i in  settings.WHITE_LIST :
     if re.match(i,url):
         return
 
 # 登录状态的校验
 is_login = request.session.get('is_login')
 if not is_login:
     return redirect('login')
 
 
 # 免认证地址的校验
 for i in  settings.NO_PERMISSION_LIST :
     if re.match(i,url):
         return
 
 # 获取权限信息
 permissions = request.session.get('permissions')
 for i in permissions :  # {'permissions__url':'xxxx'}
     if re.match(r"^{}$".format(i['url']),url):
         return
 
     
 return HttpResponse('没有权限')

动态生成一级菜单

 #  menu.html
 
 <div class="static-menu">
 
    {% for menu in menu_list %}
         <a href="{{ menu.url }}"  class="{{ menu.class }}" >
             <span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.title }}</a>
    {% endfor %}
 
 </div>
 
 

使用inclusion_tag 实现菜单

 @register.inclusion_tag('rbac/menu.html')
 def menu(request):
     url = request.path_info
 
     menu_list = request.session.get(settings.MENU_SESSION_KEY)
     # [ {'url' class } {'url' }{'url' }]
     for i in menu_list:
         if re.match(r'^{}$'.format(i['url']), url):
             i['class'] = 'active'
             break
 
     return {'menu_list': menu_list}

在模板中使用:

 
 {% load rbac %}
 {% menu request %}
 

动态生成二级菜单

 #  menu2.html
 
 <div class="multi-menu">
 
 
    {% for menu in menu_list %}
 
         <div class="item">
             <div class="title"><i class="fa {{ menu.icon }}"></i> {{ menu.title }}</div>
             <div class="body {{ menu.class }}">
 
                {% for child in menu.children %}
                     <a class="{{ child.class }}" href="{{ child.url }}">{{ child.title }}</a>
                {% endfor %}
 
             </div>
 
         </div>
 
    {% endfor %}
 
 </div>
 
 

使用inclusion_tag 实现菜单

 @register.inclusion_tag('rbac/menu.html')
 def menu(request):
     url = request.path_info
 
     menu_dict = request.session.get(settings.MENU_SESSION_KEY)
 
     menu_list = []
     
     for i in sorted(menu_dict,key=lambda x:menu_dict[x]['weight'],reverse=True):
    menu_list.append(menu_dict[i])
         
     for item in menu_list:
 
         item['class'] = 'hidden'
 
         for i in item['children']:
             if re.match(r'^{}$'.format(i['url']), url):
                 i['class'] = 'active'
                 item['class'] = ''
                 break
 
     return {'menu_list':menu_list }

在模板中使用:

 {% load  rbac %}
 {% menu request %}
 

非菜单的权限的归属的问题

id url title menu_id parent_id

1 list 客户列表 1 null 2 add 添加客户 null 1 3 edit 编辑客户 null 1 4 del 删除客户 null 1

自定义filter

猜你喜欢

转载自www.cnblogs.com/zhang-da/p/12195292.html