zoukankan      html  css  js  c++  java
  • 饮冰三年-人工智能-Python-34CRM项目实战

     Customer Relationship Management。客户管理系统。

    源码位置:https://github.com/1692134188/AaronCRM.git

     创建项目、实体类,设置登录页面这些等不再一一介绍。着重介绍一下内容

    一、模仿Django Admin创建的KingAdmin

     

     我们这次以QA的模式整理思路

    Q1:我们要达到的效果是什么样的?

      A1:

     

     Q2:整体实现思路?

      A2:1:每个模块下建立相应kingadmin.py文件。ps:其作用是将需要管理的表注册进来,同时可以配置一些自定制设置(展示、过滤、查询)等

         2:程序启动==>自动将所有模块下kingadmin中的信息存放到一个全局变量中,其全局变量的格式如下:

        enabled_admin={'模块名1':{'表名1':相应配置,'表名2':相应配置},'模块名2':{'表名1':相应配置}}

        enabled_admin={'crm':{'Customer':CustomerAdmin,'role':RoleAdmin},...}

    Q3:对于A2中的自动注册如何实现?

      A3:1:在KingAdmin模块下创建app_setup.py文件。    

    # Q1:该文件的作用是什么?
    #     A1:查找当前项目中setting文件中配置的模板。
    #         获取每个模块下的kingadmin中的注册信息
    #         将注册信息写入到全局变量中
    
    # Q2:如何动态获取配置文件中的内容
    #     A2:导入conf文件,conf.settings.INSTALLED_APPS
    
    # Q3:如何将每个模块下的信息注册到全局变量中
    #     A3:通过sites.py 方法
    
    from django import conf
    
    def kingadmin_auto_discover():
        mods=conf.settings.INSTALLED_APPS
        for app_name in mods:
            try:
                mod = __import__('%s.kingadmin' % app_name) #动态加载类和函数
            except ImportError:
                pass
    app_setup.py

         2:创建site文件并调用

    # Q1:该文件的作用是什么?
    #   A1:将每个模块下的信息注册到全局变量中
    
    class AdminSite(object):
        def __init__(self):
            self.enabled_admins={}
    
        def register(self,model_class,admin_class=None):
            app_name=model_class._meta.app_label #获取模块名称
            model_name = model_class._meta.model_name #获取表名称
            if app_name not in self.enabled_admins:
                self.enabled_admins[app_name]={}
            self.enabled_admins[app_name][model_name]=admin_class
    
    site=AdminSite()
    site.py

        3:每个模块下创建相应的kingadmin.py文件

    from KingAdmin import sites
    from KingAdmin.sites import site
    from CRM import models
    
    # Register your models here.
    print('crm kingadmin ............')
    class CustomerAdmin(sites.AdminSite):
        list_display = ['name','source','contact_type','contact','consultant','consult_content','status','date']
        list_filter = ['source','consultant','status','date']
        search_fields = ['contact','consultant__name']
    
    site.register(models.CustomerInfo,CustomerAdmin)
    site.register(models.CustomerFollowUp)
    site.register(models.ClassList)
    site.register(models.Course)
    site.register(models.Role)
    site.register(models.Menus)
    site.register(models.CourseRecord)
    site.register(models.StudyRecord)
    site.register(models.Student)
    site.register(models.UserProfile)
    CRM模块下的kingadmin.py

        同样的添加一个Student模块,其中有Test类

        4:KingAdmin中在View中进行的调用

    from django.shortcuts import render,redirect
    from django.contrib.auth import login,authenticate,logout
    from KingAdmin import app_setup
    from KingAdmin.sites import site
    app_setup.kingadmin_auto_discover()
    
    def app_index(request):
        return render(request, 'kingadmin/app_index.html', {'site': site})
    # Create your views here.
    def acc_login(request):
        error_msg=""
        if request.method=="POST":
            username=request.POST.get('username')
            password= request.POST.get('password')
    
            user=authenticate(username=username,password=password)
            if user:
                login(request,user)
                return redirect(request.GET.get('next', '/kingadmin/'))
            else:
                error_msg = "Wrong username or password!"
        return render(request,"kingadmin/login.html", {'error_msg':error_msg})
    
    def acc_logout(request):
        logout(request)
        return redirect("/kingadmin/login/")
    views
    {% extends 'kingadmin/index.html' %}
    {% block  right-content-container %}
        <h2 class="page-header">app</h2>
    
        <div>
            {% for app_name,app_tables  in site.enabled_admins.items %}
                <table class="table table-striped">
                    <thead>
                    <tr>
                        <th>{{ app_name }}</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for model_name in app_tables %}
                        <tr>
                            <td>{{ model_name }}</td>
                            <td>ADD</td>
                            <td>Change</td>
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            {% endfor %}
    
        </div>
    
    {% endblock %}
    app_index.html

    此时效果是:

     Q4:如何为表名添加url链接?

      A4:在sites文件中注册方法register的时候,把model_class赋值给admin_class属性。前端页面通过该属性值获取到对应的url。

        ps:需要注意的是,通过定义一个基类BaseKingAdmin,来解决某些注册类中没有admin_class属性的问题   

          需要注意的是,为了避免多个model共享同一个BaseKingAdmin内存对象,需要将其实例化

    Q5:如何展示页面?

      A5:拿到要该的类,通过自定义模板反射出其要展示的内容。 ps:需要注意枚举类型要转换成相应的汉字

    from django.template import Library
    from django.utils.safestring import mark_safe 
    register = Library() 
    
    @register.simple_tag
    def build_table_row(obj, admin_class):
        # Q1:该方法的作用是什么?
        # A1:通过模板,将表中的记录生成相应的html元素
        ele = ""
        for column_name in admin_class.list_display:
            column_obj = admin_class.model._meta.get_field(column_name)
            if column_obj.choices:
                # 如果是枚举类型,需要将其转换成相应的汉字
                column_date = getattr(obj,'get_%s_display' %column_name)()
            else:
                column_date = getattr(obj, column_name)
            td_ele = "<td>%s</td>" % column_date
            ele += td_ele
        return mark_safe(ele)
    自定义模板页面

     Q6:搜索功能

      A6:通过request.GET方法,从url拿到查询条件,过滤查询即可。ps:需要注意日期类型。  

    from django.template import Library
    from django.utils.safestring import mark_safe
    import datetime,time
    register = Library()
    
    
    @register.simple_tag
    def build_filter_ele(filter_column,admin_class):
    
        column_obj = admin_class.model._meta.get_field(filter_column)
        print("column obj:",column_obj)
        try:
            filter_ele = "<select name='%s'>" % filter_column
            for choice in column_obj.get_choices():
                selected = ''
                if filter_column in admin_class.filter_condtions:#当前字段被过滤了
                    # print("filter_column", choice,
                    #       type(admin_class.filter_condtions.get(filter_column)),
                    #       admin_class.filter_condtions.get(filter_column))
                    if str(choice[0]) == admin_class.filter_condtions.get(filter_column):#当前值被选中了
                        selected = 'selected'
                        print('selected......')
    
                option = "<option value='%s' %s>%s</option>" % (choice[0],selected,choice[1])
                filter_ele += option
        except AttributeError as e:
            print("err",e)
            filter_ele = "<select name='%s__gte'>" % filter_column
            if column_obj.get_internal_type() in ('DateField','DateTimeField'):
                time_obj = datetime.datetime.now()
                time_list = [
                    ['','------'],
                    [time_obj,'Today'],
                    [time_obj - datetime.timedelta(7),'七天内'],
                    [time_obj.replace(day=1),'本月'],
                    [time_obj - datetime.timedelta(90),'三个月内'],
                    [time_obj.replace(month=1,day=1),'YearToDay(YTD)'],
                    ['','ALL'],
                ]
    
                for i in time_list:
                    selected = ''
                    time_to_str = ''if not i[0] else  "%s-%s-%s"%(i[0].year,i[0].month,i[0].day)
                    if  "%s__gte"% filter_column in admin_class.filter_condtions:  # 当前字段被过滤了
                        print('-------------gte')
                        if time_to_str == admin_class.filter_condtions.get("%s__gte"% filter_column):  # 当前值被选中了
                            selected = 'selected'
                    option = "<option value='%s' %s>%s</option>" % 
                             (time_to_str ,selected,i[1])
                    filter_ele += option
    
        filter_ele += "</select>"
        return mark_safe(filter_ele)
    
    
    @register.simple_tag
    def build_table_row(obj, admin_class):
        # Q1:该方法的作用是什么?
        # A1:通过模板,将表中的记录生成相应的html元素
        ele = ""
        for column_name in admin_class.list_display:
            column_obj = admin_class.model._meta.get_field(column_name)
            if column_obj.choices:
                # 如果是枚举类型,需要将其转换成相应的汉字
                column_date = getattr(obj, 'get_%s_display' % column_name)()
            else:
                column_date = getattr(obj, column_name)
            td_ele = "<td>%s</td>" % column_date
            ele += td_ele
        return mark_safe(ele)
    kingadmin_tags.py

    Q7:分页功能

      A7:可以参考https://docs.djangoproject.com/en/2.2/topics/pagination/。文档中相应的功能介绍

    Q8:如何实现排序

      A8:给相应的标题上添加链接,通过地址栏传值到后台。后台取到相应的值,进行判断

        ps:需要注意细节点:1:升序降序问题。2:排序上下箭头展示  3:排序和查询的问题(隐藏域)   4:排序和分页的问题 

    Q9:如何实现关键字段搜索功能

      A9:添加检索文本域字段,注意搜索关键字段和过滤条件的状态保持

    Q10:如何实现任意表的增删改查?

      A10:首先要实现对任意表的操作,name需要通过动态生成的方式动态生成modelform。

        1:在KingAdmin创建form_handle,以动态生成modelform    

    from django.forms import ModelForm
    
    
    def create_dynamic_model_form(admin_class,form_add=False):
        """动态的生成modelform
        form_add: False 默认是修改的表单,True时为添加
        """
    
        class Meta:
            model = admin_class.model
            # fields = ['name','consultant','status']
            fields = "__all__"
            if not form_add:#change
                exclude = admin_class.readonly_fields
                admin_class.form_add = False #这是因为自始至终admin_class实例都是同一个,
                # 这里修改属性为True是为了避免上一次添加调用将其改为了True
            else: #add
                admin_class.form_add = True
    
        def __new__(cls, *args, **kwargs):
            print("__new__",cls,args,kwargs)
            for field_name in cls.base_fields:
                filed_obj = cls.base_fields[field_name]
                filed_obj.widget.attrs.update({'class':'form-control'})
                # if field_name in admin_class.readonly_fields:
                #     filed_obj.widget.attrs.update({'disabled': 'true'})
                #     print("--new meta:",cls.Meta)
    
            #print(cls.Meta.exclude)
            return  ModelForm.__new__(cls)
    
        dynamic_form = type("DynamicModelForm" ,(ModelForm,) ,{'Meta' :Meta,'__new__':__new__})
    
        print(dynamic_form)
        return dynamic_form
    form_hander

     Q11:如何实现表的修改操作?

      A11:展示页面的时候将数据显示出来,添加保存按钮,通过post方法后台保存即可

    Q12:如何完成新增操作?

      A12:新增操作同样不是很复杂,

        ps:1和编辑可以共用模板页面。2:某些只读字段 例如状态等,一旦输入不可随意修改。通过在kingadmin.py中配置readonly_fields属性

    Q13:对many2many的多选字段进行完善。

      A13:可实现多选,模糊查询等功能

     Q14:删除功能的实现。

      A14:删除时给出提示所关联的表提示

    Q15:如何实现action

      A15:通过在kingadmin.py中配置action属性并定义配套的方法,View中反射实现自定义方法,进行操作

    Q16:如何实现默认的自定义删除action

      A16:注意首先model类中相应的属性需要设置成可以级联删除 如: referral_from = models.ForeignKey("self", blank=True, null=True, verbose_name="转介绍",on_delete=models.CASCADE)

      然后在admin_base.py和view.py文件中做判断

    Q17:面包屑

      A17:在需要添加的页面修改即可

     二、CRM项目学员报名流程

    Q1:学员报名的大致步骤有哪些?

      A1:选择班级(销售人员)==> 学生上传个人信息  ==> 缴费 ==>

    Q2:需要哪些准备工作?

      A2:1:基础数据的维护(创建Menu链接、为角色分配菜单)

        2:维护模型:

              a:客户信息表(CustomerInfo)添加一些身份证号、性别、紧急联系人等个人信息

              b:新增 合同模板表 (ContractTemplate),该表和班级表相关联

              c:新增 学员报名表(StudentEnrollment)

              d:新增 学员缴费记录表(PaymentRecord)

    Q3:销售人员为学院分配班级?

      A3:班级分配完成后,会生成相应的链接,学生拿到链接地址后,学生上传个人信息。

    Q4:学生填写个人信息有哪些亮点?

      A4:1:利用了djangoform验证,并且设置可可读属性也不能修改。

         2:附件上传DropZone控件

    Q5:DropZone附件上传具体的步骤有哪些?

      A5:1:引入相应的js和css

         2:html页面中添加相应的<form>表单,并设置action提交路径

        `3:后台代码处理,setting中配置相应的文件路径 并且再相应的目录下创建文件夹

  • 相关阅读:
    git 比较两个分支日志和文件的差异
    Interspeech 2020调研:文本前端
    centOS 7 修改分辨率、图形与命令行界面切换
    pdf表格提取camelot安装教程
    CFS任务的负载均衡——2
    CFS任务的负载均衡(框架篇)——(1)
    ftrace笔记一
    红黑树rbtree学习笔记
    一. scheduler相关结构体简介
    cpu_capacity、task_util、cpu_util计算方法
  • 原文地址:https://www.cnblogs.com/YK2012/p/11773010.html
Copyright © 2011-2022 走看看