zoukankan      html  css  js  c++  java
  • CRM系统项目总结

    一、准备:

      1、组件部分(数据增删改查实现)模仿django admin开发一个组件

        效果:

        (1)、启动服务(先在settings中添加app)

    from django.apps import AppConfig
    from django.utils.module_loading import autodiscover_modules
    
    
    class XadminConfig(AppConfig):
        name = 'xadmin'
    
        def ready(self):
            autodiscover_modules('xadmin')
    AppConfig

        (2)、注册表

    from django.db import models
    
    # Create your models here.
    class Author(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        age=models.IntegerField()
    
        # 与AuthorDetail建立一对一的关系
        authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
        def __str__(self):
            return self.name
    
    class AuthorDetail(models.Model):
    
        nid = models.AutoField(primary_key=True)
        birthday=models.DateTimeField()
        telephone=models.BigIntegerField()
        addr=models.CharField( max_length=64)
    
        def __str__(self):
            return self.addr
    
    class Publish(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        city=models.CharField( max_length=32)
        email=models.EmailField()
    
    
    
        def __str__(self):
            return self.name
    
    
    class Book(models.Model):
    
        nid = models.AutoField(primary_key=True, verbose_name=" 编号")
        title = models.CharField( max_length=32, verbose_name="书籍名称")
        publishDate=models.DateTimeField(null=True, verbose_name='日期')
        price=models.DecimalField(max_digits=5, decimal_places=2)
    
        # 与Publish建立一对多的关系,外键字段建立在多的一方
        publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
        # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
        authors=models.ManyToManyField(to='Author',)
    
    
        def __str__(self):
            return self.title
    models
    from xadmin.service.start__xadim import site, ModelAdmin
    from app01 import models
    from django import forms
    from django.utils.safestring import mark_safe
    from django.urls import reverse
    
    
    class BookConfig(ModelAdmin):
    
        # def add_tag_change(self, obj=None, is_header=False):
        #     if is_header:  # 如果是表头
        #         return '修改操作'
        #     return mark_safe('<a href="%s">修改</a>' % reverse('change_data', args=(obj.pk,)))  # 如果是表单数据
        #
        # def add_tag_delete(self, obj=None, is_header=False):
        #     if is_header:
        #         return '删除操作'
        #     return mark_safe('<a href="%s/delete">删除</a>' % reverse('delete_data', args=(obj.pk,)))
        #
        # def add_tag_checked(self, obj=None, is_header=False):
        #     if is_header:
        #         return mark_safe('<input type="checkbox" name="checked_data" class="all">')
        #     return mark_safe('<input type="checkbox" name="checked_data" value="%s" class="list_all">' % obj.pk)
        class ModelForms(forms.ModelForm):
            class Meta:
                model = models.Book
                fields = '__all__'
                widgets = {'publishDate': forms.widgets.TextInput(attrs={'type': 'date', }), }
                labels = {
                    'title': '书名',
                    'price': '价格',
                }
    
        # def action_test(self, queryset, request):
        #     print(self,type(self))
        #     for i in queryset:
        #         i.price = 100
        #         i.save()
    
        # action_test.short_description = "批量初始化"  # action函数功能实现简介
    
        list_display = ['nid', 'title', 'price', 'publish', 'publishDate', 'authors']
        list_display_links = ['title']
        model_forms_chinese_field = ModelForms
        list_search_fields = ['title', 'price']
        # list_action_func = [action_test]
        list_filter = ['title', 'publish', 'authors']
    
    class PublishConfig(ModelAdmin):
        list_display = ['nid', 'title', 'price']
    
    
    site.register(models.Book, BookConfig)
    site.register(models.Publish)
    site.register(models.Author)
    site.register(models.AuthorDetail)
    在组件之外的app中添加表和注册表

        (3)、服务(url分发,数据增删改查,展示等等)

           注意问题:

            url二级分发为什么在配置类实现?

            url地址反向解析

            自定义显示哪些字段内容,多对多字段该如何处理,修改和删除标签、哪个字段可点击进入到修改页面,该如何实现

            自定义哪些字段可以搜索,获取搜索值后用Q方法处理条件

            自定义批量处理函数,页面标签value值应该放什么(函数名,选中数据的pk值)

            自定义哪些字段可以筛选(关系字段如何获取(all方法),普通字段如何获取)

    field_obj = self.model_admin_self.model._meta.get_field(filter_field)
                if isinstance(field_obj, ManyToManyField) or isinstance(field_obj, ForeignKey):  # 过滤标签是不是多对多或者多对一关系
                    queryset_list = field_obj.remote_field.model.objects.all()  # 是的话取到所有关联表对象
    通过字符串形式的字段取得对应关系表的所有对象

            修改页面时,关系字段应该可以直接点击添加按钮添加关系字段对象(浏览器父类窗口,子类窗口的使用)

    window.opener.pop_response('{{ ret.pk }}',"{{ ret.text }}",'{{ ret.pop_res_id }}')  //执行父页面的函数pop_response,向里面传递参数,用于数据更新后的DOM操作
        function pop(url) {
            window.open(url,"","width=600,height=400,top=100,left=100")  // 为a标签“+”的点击事件函数,打开一个添加数据的子窗口
            
        }
    
    
        function pop_response(pk, text, pop_res_id) {
            console.log(pk, text, pop_res_id)
            var $option = $('<option>')  //创建一个<option>标签
            console.log($option)
            $option.val(pk)  //标签value值为这条数据的主键值
            $option.text(text) // 文本为__str__
            $option.attr("selected","selected")  //option标签为选中状态
            $('#'+pop_res_id).append($option)  // 将这条option标签插入到select标签,
    
        }
    </script>

            主页面展示的数据应该放在一个类下,比如分页数据、表头数据、表单数据、函数数据、过滤字段数据等。只需要在视图函数中创建这个类对象,把所有数据对象、request、当前models的配置类self对象作为参数传入,把创建的这个类对象传入模板语言,模板语言直接通过这个对象可以调用类下的所有方法,用于展示分页、表头、表单等数据。

            修改和增加页面使用modelform模块快速构建一个html页面,modelform在视图的使用,关系字段在增加和修改页面如何实时添加数据

        def add(self, request):
            # 获取一个modelform类,并创建一个modelform类对象,用于添加或者修改页面的渲染
            modelform = self.get_model_forms()
            forms_obj = modelform()
            # print(forms_obj)  # modelform对象
            from django.forms.boundfield import BoundField
            # from django.forms.models import ModelMultipleChoiceField
            from django.forms.models import ModelChoiceField   # ModelMultipleChoiceField继承ModelChoiceField
            for forms_field_obj in forms_obj:
                # print('-------', forms_field_obj, type(forms_field_obj))  # django.forms.boundfield.BoundField
                # print('+++++++', forms_field_obj.field, type(forms_field_obj.field))  # <class 'django.forms.models.ModelMultipleChoiceField'>  # modelform字段对象
                # print('*******', forms_field_obj.name, type(forms_field_obj.name))  # authors <class 'str'>  字段名字(字符串)
                if isinstance(forms_field_obj.field, ModelChoiceField):  # 如果这个字段对象是多对多或者一对多字段
                    forms_field_obj.is_pop = True  # 用于模板语言判断是否为关系字段
                    # print(forms_field_obj.field.queryset, type(forms_field_obj.field.queryset))  # 返回这个关系字段下所有的对象
                    # print(forms_field_obj.field.queryset.model, type(forms_field_obj.field.queryset.model))  # 返回这个关系字段的关联模型表
    
                    app_label = forms_field_obj.field.queryset.model._meta.app_label  # 获取关联字段的关系模型表所在的app
                    model_name = forms_field_obj.field.queryset.model._meta.model_name  # 获取关联字段的关系模型表的表名
    
                    url = reverse('{}_{}_add_data'.format(app_label, model_name))
                    forms_field_obj.url = url+'?pop_res_id=id_{}'.format(forms_field_obj.name)  # 用于添加页面关系字段‘+’的链接拼接 ,参数用于判断是返回当前添加页还是主添加页,以及为哪个select标签进行DOM操作
    
            if request.method == 'POST':
                forms_obj = modelform(request.POST)
                if forms_obj.is_valid():
                    add_obj = forms_obj.save()
                    pop_res_id = request.GET.get('pop_res_id')
                    if pop_res_id:
                        ret = {"pk": add_obj.pk, "text": str(add_obj), 'pop_res_id': pop_res_id}
                        return render(request, 'pop.html', {'ret': ret})
    
                    return redirect('%s' % (self.addtag.get_show_reverse_url(self.model)))
    
            return render(request, 'add_data.html', {'forms_obj': forms_obj})
    视图
    <script>
        window.opener.pop_response('{{ ret.pk }}',"{{ ret.text }}",'{{ ret.pop_res_id }}')  //执行父页面的函数pop_response,向里面传递参数,用于数据更新后的DOM操作
        {#console.log('{{ ret.pk }}',"{{ ret.text }}",'{{ ret.pop_res_id }}')#}
        window.close()
    </script>
    pop.html 用于DOM操作所需的数据传输
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3" style="padding-top: 100px">
    
                <hr>
                <form action="" method="post" novalidate>
    
                    {% csrf_token %}
                    {% for field in forms_obj %}
                        <p style="position: relative">{{ field.label }}
                            {{ field }} <span>{{ field.errors.0 }}</span>
                            {% if field.is_pop %}
                            <a onclick="pop('{{ field.url }}')" style="position: absolute;right: -30px; top: 25px; font-size: 20px; height: 50%">+</a>
                            {% endif %}
    
                        </p>
                    {% endfor %}
    
    
                    <div class="submit-btn">
                        <button class="btn btn-info ">提交</button>
                    </div>
    
                </form>
            </div>
        </div>
    </div>
    
    <script>
        function pop(url) {
            window.open(url,"","width=600,height=400,top=100,left=100")  // 为a标签“+”的点击事件函数,打开一个添加数据的子窗口
            
        }
    
    
        function pop_response(pk, text, pop_res_id) {
            console.log(pk, text, pop_res_id)
            var $option = $('<option>')  //创建一个<option>标签
            console.log($option)
            $option.val(pk)  //标签value值为这条数据的主键值
            $option.text(text) // 文本为__str__
            $option.attr("selected","selected")  //option标签为选中状态
            $('#'+pop_res_id).append($option)  // 将这条option标签插入到select标签,
    
        }
    </script>
    add.html

          url路径保存搜索条件:先深copy之前的url条件,然后再往条件添加新的条件,添加后换成url编码,最后把它拼到a标签        

    params = copy.deepcopy(self.request.GET)  #{‘name’: aike}
    params[id] = 1  #{'name': aike, 'id': 1}
    url = params.urlencode()  # # 把字典转为URL编码(?name=aike&id=1)
    link_tag = mark_safe("<a href='?%s' class='filter_field active'>%s</a>" % (url, text))

        (4)、url

    from django.contrib import admin
    from xadmin.service.start__xadim import site
    from django.urls import path, re_path
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('xadmin/', site.urls),]
    urls

        

      2、权限管理部分:RBAC权限介绍,用django制作一个简单的权限组件

        (1)、表设计:用户表、角色表、权限表、权限分组表

            用户拥有角色,角色拥有权限(一条url代表一条权限),权限拥有分组。一张表的增删改查分为一组,例如学生表,查看学生,编辑学生,删除学生,添加学生可归为学生管理。

    from django.db import models
    
    # Create your models here.
    from django.db import models
    
    # Create your models here.
    
    
    class User(models.Model):
        nid = models.AutoField(primary_key=True)
        username = models.CharField(max_length=32, )
        password = models.CharField(max_length=32,)
        roles = models.ManyToManyField(to='Roles')
    
        def __str__(self):
            return self.username
    
        class Meta:
            verbose_name = '用户表'
    
    
    class Roles(models.Model):
        nid = models.AutoField(primary_key=True)
        title = models.CharField(max_length=12, verbose_name='角色名')
        permission = models.ManyToManyField(to='Permission', verbose_name='权限')
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = '角色表'
    
    
    class Permission(models.Model):
        nid = models.AutoField(primary_key=True)
        title = models.CharField(max_length=12)
        urls = models.CharField(max_length=120)
    
        # action和group用于第二种实现权限校验方式需要添加的字段,不添加这些也能实现
        # 主要作用是在HTML页面当中用模板语言判断是否有权限时,url地址不用写很死
        action = models.CharField(max_length=32, null=True)  # 功能 增删改查
        group = models.ForeignKey(to='PermissionGroup', on_delete=models.CASCADE, null=True)  # 权限分组
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = '权限表'
    
    
    # 权限分组, 用户分组、角色分组等。用于第二种权限校验方式
    class PermissionGroup(models.Model):
        nid = models.AutoField(primary_key=True)
        title = models.CharField(max_length=32)
    
        def __str__(self):
            return self.title
    models.py

        (2)、登录成功注册session:session中应该存放哪些数据,用户主键、用户名、用户权限url列表、权限分组等。

    def reg_session(request, user):
        """
        登录后将权限和用户id存入session,存入后才能进行权限校验
        :param request:
        :param user: 登录对象
        :return: 没有返回值
        """
        request.session['user_id'] = user.pk
        request.session['username'] = user.username
        # 将用户的所有权限url以列表形式添加到session
        permission_urls_obj_list = user.roles.values('permission__urls').distinct()
        permission_urls_list = [permission_urls['permission__urls'] for permission_urls in permission_urls_obj_list]
        request.session['permission_urls_list'] = permission_urls_list
    
        # 将用户权限分组nid为键(1为用户管理,2为角色管理),action和url为值,以字典形式存入到session,
        #    {1: {
        #         'urls': ['/users/', '/users/add/', '/users/delete/(\d+)', 'users/edit/(\d+)'],
        #         'actions': ['list', 'add', 'delete', 'edit']},
    
        # 第二种校验方式,可以在HTML页面不写死url判断是否有删改的操作,即该不该显示删除、修改或者增加的按钮
        permission_dict = {}
        urls_action_nid = user.roles.values('permission__urls', 'permission__action', 'permission__group__nid').distinct()
    
        for i in urls_action_nid:
            nid = i['permission__group__nid']
            if nid not in permission_dict:
                permission_dict[nid] = {
                        'urls': [i['permission__urls']],
                        'actions': [i['permission__action']]
                        }
            else:
                permission_dict[nid]['urls'].append(i['permission__urls'])
                permission_dict[nid]['actions'].append(i['permission__action'])
    
        request.session['permission_dict'] = permission_dict
    
        # 菜单栏显示哪些权限,角色管理或者用户管理
        menu_permission = user.roles.filter(permission__action='show').values('permission__urls', 'permission__group__title')
        menu_permission_list = []
        for item in menu_permission:
            menu_permission_list.append(item)
    
        request.session['menu_permission_list'] = menu_permission_list
    注册session

        (3)、中间件权限校验:权限url以正则方式存储在数据库,校验时应该以正则方式校验。白名单处理:登录、admin等。判断是否登录。最后进行权限校验

    from django.utils.deprecation import MiddlewareMixin
    import re
    from django.shortcuts import render, redirect, HttpResponse
    
    def re_checkout(permission_urls_list, path):
        flag = False
        for permission_urls in permission_urls_list:
            permission_urls = '^%s$' % permission_urls
            ret = re.search(permission_urls, path)
            if ret:
                flag = True
                break
        return flag
    
    
    class PermissionVerify(MiddlewareMixin):
    
        def process_request(self, request):
    
            # 校验白名单
            path = request.path
            filter_urls = ['/login/', '/reg/', '/admin/.*', '/logout/']
    
            for urls in filter_urls:
                ret = re.search(urls, path)
                # print('******', ret)
                if ret:
                    return None
    
            # 校验是否登录
            user_id = request.session.get('user_id', None)
            if not user_id:
                return redirect('/login/')
    
            # 网址存在再进行权限校验
            # path_list = ['/user/edit/(d+)/', '/login/', '/user/', '/user/add/', '/roles/']
            # flag = re_checkout(path_list, path)
            # if not flag:
            #     return HttpResponse('404')
    
            # 校验权限
            # permission_urls_list = request.session['permission_urls_list']
            #
            # flag = re_checkout(permission_urls_list, path)
            # if not flag:
            #     return HttpResponse('没有权限')
            #
            # return None
    
            # 第二种校验方式,可以在HTML页面不写死url判断是否有删改的操作,即该不该显示删除、修改或者增加的按钮
            permission_dict = request.session.get('permission_dict')
            for urls_actions in permission_dict.values():
                permission_urls_list = urls_actions['urls']
                for permission_urls in permission_urls_list:
                    new_urls = '^{}$'.format(permission_urls)
                    ret = re.match(new_urls, path)
                    print(ret)
                    # 将用户权限分组:nid为键(1为用户管理,2为角色管理),action和url为值,以字典形式存入到session,
                    #    permission_urls_list={
                    #               1: {
                    #                   'urls': ['/users/', '/users/add/', '/users/delete/(\d+)', 'users/edit/(\d+)'],
                    #                   'actions': ['show', 'add', 'delete', 'edit']},
                    #               2: {
                    #                      'urls': ['/roles/'],
                    #                      'actions': ['show']}
                    #                  }
    
                    # 可以实现访问哪个权限分组(用户管理,角色管理),就能得到这个分组所有的权限url和对action,
                    # 当用户访问的是用户管理时,对应键值为1,那么通过遍历,就能取到这个分组拥有的权限
                    if ret:
                        request.permission_actions_list = urls_actions['actions']
                        print(request.permission_actions_list)
                        return None
            return HttpResponse('没有权限')
    中间件实现

    二、导入组件:

       将权限和admin组件的所有静态文件、templates等文件一并拷贝至项目即可。然后注册app,加入中间件,项目用户表与权限组件用户表一对一关系。自己写的admin组件注册表,选择实现自定义配置类

    三、功能扩展:

       需求将全部在项目app中实现,例如登录注册,自定义配置类。其中自定义配置类是在为表注册组件时实现,所有需要扩展功能时,是在自身配置类中实现。

    项目中遇到的小问题:

        self与类名调用类方法的区别,self调用会实例化方法,而类调用不会。所有传参数时,如果是实例化的方法,不需要传self,而类调用方法不会被实例化,使用时需要传self参数。

        使用ajax时,视图返回的值最好使用JsonResponse返回一个字典,非字典时需要设置safe=False

    def func(request):
        ret = {'name':aike}
        return JsonResponse(ret)
    
    
    def func1(request):
        ret = [1,2,3],
        return JsonResponse(ret,safe=False)

        删除多对多字段中的值用remove方法,删除queryset或者obj对象用delete方法。

        关系字段,通过字段对象取得关联表所有对象

    field_obj = self.model_admin_self.model._meta.get_field(filter_field)
                if isinstance(field_obj, ManyToManyField) or isinstance(field_obj, ForeignKey):  # 过滤标签是不是多对多或者多对一关系
                    queryset_list = field_obj.remote_field.model.objects.all()  # 是的话取到所有关联表对象
                else:
                    queryset_list = self.model_admin_self.model.objects.values('pk', filter_field)   # 不是的话取到对应字段值即可

        Q方法的进阶使用

    search_connection = Q()  # 创建一个Q对象
    search_connection.connector = 'or'  # Q对象查询条件为或关系
    search_connection.children.append(条件)# 将查询条件添加到children列表,children接收一个元祖,一个为字段,一个为字段值
    
    search_connection 为一个查询条件

        不清空筛选条件

    params = copy.deepcopy(self.request.GET)  #{‘name’: aike}
    params[id] = 1  #{'name': aike, 'id': 1}
    url = params.urlencode()  # # 把字典转为URL编码(?name=aike&id=1)
    link_tag = mark_safe("<a href='?%s' class='filter_field active'>%s</a>" % (url, text))
  • 相关阅读:
    Java判断一个字符是数字或字母
    java数组和字符串相互转换
    java 字符串截取的三种方法
    Templates && Algorithms
    挖坑——未完成题目列表QwQ
    作业_2018.08.25
    BZOJ1008 [HNOI2008]越狱 (快速幂,组合)
    UR #3 核聚变反应强度( gcd )
    A Super Hero
    NOIP2015 pj
  • 原文地址:https://www.cnblogs.com/aizhinong/p/12459652.html
Copyright © 2011-2022 走看看