ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CRM项目_权限系统_crm业务_ 其他

2022-06-17 16:32:11  阅读:226  来源: 互联网

标签:__ CRM models menu request django import 权限 crm


CRM项目

1.权限系统

1.1问题

  • 问:为什么程序需要权限控制?

    答:生活中的权限限制,① 看灾难片电影《2012》中富人和权贵有权登上诺亚方舟,穷苦老百姓只有等着灾难的来临;② 屌丝们,有没有想过为什么那些长得漂亮身材好的姑娘在你身边不存在呢?因为有钱人和漂亮姑娘都是珍贵稀有的,稀有的人在一起玩耍和解锁各种姿势。而你,无权拥有他们,只能自己玩自己了。 程序开发时的权限控制,对于不同用户使用系统时候就应该有不同的功能,如:

    • 普通员工、部门主管、总监、总裁

    所以,只要有不同角色的人员来使用系统,那么就肯定需要权限系统。

  • 问:为什么要开发权限组件?

    答:假设你今年25岁,从今天开始写代码到80岁,每年写5个项目,那么你的一生就会写275个项目,保守估计其中应该有150+个都需要用到权限控制,为了以后不再重复的写代码,所以就开发一个权限组件以便之后55年的岁月中使用。 亲,不要太较真哦,你觉得程序员能到80岁么,哈哈哈哈哈哈哈 偷偷告诉你:老程序员开发速度快,其中一个原因是经验丰富,另外一个就是他自己保留了很多组件,新系统开发时,只需把组件拼凑起来基本就可以完成。

  • 问:web开发中权限指的是什么?

    答:web程序是通过 url 的切换来查看不同的页面(功能),所以权限指的其实就是URL,对url控制就是对权限的控制。

    • 结论:一个人有多少个权限就取决于他有多少个URL的访问权限。

  1. 表的划分

    • 用户表: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问题总结

  1. 权限有几张表?

  2. 简述权限流程?

  3. 为什么要把权限放入session?

  4. 静态文件和模块文件

  5. 相关技点。

  6. 二级菜单时,如何构造的数据结构?

  7. 非菜单的权限归属?

  8. 层级导航?

  9. 粒度控制到按钮?

  10. 如何实现的权限系统?

  11. 为什么要在中间件中做校验?

  12. 写出流程(思维导图)

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有