饮冰三年-人工智能-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
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()
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)
同样的添加一个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/")
{% 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 %}
此时效果是:
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)
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
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中配置相应的文件路径 并且再相应的目录下创建文件夹