Day1:项目分析

一:需求分析

二:CRM角色功能介绍

三:业务场景分析

销售:

1.销售A 从百度推广获取了一个客户,录入了CRM系统,咨询了Python课程,但是没有报名

2.销售B 从qq群获取一个客户,成功使他报名Python班,然后给他发送了报名连接,等待用户填写完毕后,将他添加到Python具体的学习班级中

3.销售C 打电话给之前的一个客户,说服他报名Python课程,但是没有成功,更新了跟踪记录

4.销售D 获取了一个客户,录入信息时,发现此客户已经存在,不允许重复录入,随后通知相应的原负责人跟进

5.销售E 从客户库中获取了,超过一个月未跟进的客户,进行再次跟进

6.销售主管 查看了部门本月的销售报表,包括来源分析,成单率分析,班级报名数量分析,销售额环比,同比

学员:

1.客户A 填写了销售发来的报名连接,上传了个人的证件信息,提交,之后收到邮件,告知报名成功,并为他开通了学员账号,升级为学员A

2.学员A 登录学员系统,看到自己的合同,报名的班级,课程大纲

3.学员A 提交了Python课程当时课时作业

4.学员A 查看自己的Python课程成绩,排名

5.学员A 搜索问题,未找到答案,录入一条问题

6.学员A 转介绍学员,录入其信息

讲师:

1.讲师A 登录CRM系统,查看自己管理的班级列表

2.讲师A 进入Python 5期课程,创建第3节的上课记录,填写了本节课内容,作业要求

3.讲师A 在课程中点名,对点名情况进行录入,标记相关状态

4.讲师A 批量下载所有学员的课时作业,给每个学员在线批注了成绩+状态

管理员:

1.创建课程 C++,Python..

2.创建校区 上海,北京..

3.创建班级 C++35期,Python27期

4.创建账号 ABCD

5.创建了销售,讲师,学员角色

6.为账号分配到对应的角色,将ABCD分配给销售

7.创建相关权限

8.为销售角色分配了相关权限

四:表结构设计

数据库关联模型

Django表结构实现

 表结构创建

Day2:主要实现功能kingadmin为各个应用实现一个类似于Django自带的数据库管理功能

kingadmin目录

销售目录

学员目录

1.首先我们需要在项目启动后(进入Kingadmin模块中view视图后,能够自动采集所有的应用中需要我们采集的数据库信息)

(1)先设置采集方法:在每个需要我们采集的应用模块中添加上kingadmin.py文件(类似于后台admin会在应用模块的admin.py中采集信息一样)。如上面目录结构,在其中添加了kingadmin.py

 Sale模块中kingadmin

 Student模块中kingadmin

从中发现需要用到一个基类BaseKingAdmin来自于kingadmin模块:是为了防止注册事件时出现为空的现象,而且在基类中添加功能更加方便

 admin_base.py中BaseKingAdmin基类

还需要from kingadmin.sites import site,使用到site方法(类似于admin.site.register(模型,自定义模型显示类)):功能是将各个模块中的数据模型统一添加在一个数据结构中,方便调用

 sites.py中的site方法

将数据统一放入self.enabled_admins{}中,形式为self.enabled_admins[模块][表名] = 自定义模型显示类(默认BaseKingAdmin)

注意:虽然在每个模块中都导入了一次sites模块,使用一次site对象,实际上使用的是同一个site对象

可以使用id(site)查看内存,因为python机制中将一个模块导入后,会将其保存在内存中,下次导入数据的时候,会直接从内存中获取数据(所以大家使用的是一个site对象)

所以说:python模块本身就是单例模式

(2)从settings.py中获取各个模块。创建app_setup.py文件,在项目进入view时去调用该文件,并执行,获取到所有模块的信息

进入views.py自动调用app_setup.kingadmin_auto_discover()方法

 views.py进入后,顺序执行,首先去调用app_setup.kingadmin_auto_discover()方法采集信息

看如何采集各个模块信息:从配置文件中settings的INSTALLED_APPS中获取所有模块信息

 settings文件INSTALLED_APPS

views调用了app_setup中的kingadmin_auto_discover()方法自动采集信息,下面看看app_setup文件:实现方法。反向查找

from django import conf  #实现动态获取配置文件,而不是以目录形式

import importlib

def kingadmin_auto_discover():

for module in conf.settings.INSTALLED_APPS:

try:

# md = importlib.import_module('.kingadmin',module)  #这个也可以

md = __import__('%s.kingadmin'%module) #导入Kingadmin,然后回去执行该文件中的数据,去注册事件(模块导入后,会自动使用site.register方法注册事件)

except ImportError as e:

pass

(3)上面将数据采集完毕,方法内存中site对象中,使用app_index视图方法,可以实现后台管理admin首页功能

def app_index(request):return render(request,"kingadmin/app_index.html",{'site':site})

 前端主要代码

(4)实现点击表名,查看数据的功能

 table_obj_list方法根据模块和表名去获取site对象中的数据

 前端

 my_func.py设置自定义函数

Day3:对上面的功能添加分页,筛选,排序,搜索功能(功能之间的url需要重组)

 分页类代码

 分页类的使用

 使用模板函数对分页数据进行url组合

 build_page_row模板函数

二:对各个字段筛选(对kingadmin中list_filter字段进行筛选)

1:前端显示

 build_filter_row模板函数去获取数据生成标签

2.模板函数去定制标签,在form表单中加入隐藏标签(表示排序和搜索条件)

 build_filter_row模板函数对日期筛选进行自定义,外键或者choices字段使用字段对象获取数据,对于其他的字段使用model获取所有的值,组成select框进行筛选

3.在views中将url中的各个条件,放置到admin_class中,方便模板标签的使用

 注意:我在views中将各个url条件放在admin_class中,方便查询对比(也可以放在变量中分发出来)

4.在views中的url数据获取时将其他_q搜索,o排序,_p分页数据过滤,获取所有数据

 get_filter_result过滤条件,获取querysets数据

三:对各个字段进行排序(list_display)

1.前端传递排序数据,对于table中的th加上url

 使用模板函数处理

2.模板函数build_title_row 去生成标签

 build_title_row中先将过滤和搜索条件组合,再生成排序url(符号倒序,数字代表在admin_class中list_display字段中的索引顺序)

3.views中对我们获取的所有数据,根据前端传递的排序方法进行排序处理

 get_order_result获取排序结果

四:对字段进行搜索(search_fields)

1.前端生成标签时,form表单中需要一起传递其他条件的input隐藏框

 前端数据form

2.使用模板函数生成标签

 build_search_filter模板函数,生成input标签(含有各个条件)

3.后端处理搜索条件,生成querysets数据

 views处理搜索字段,注意使用OR,需要用到Q方法

Day4:动态生成任意表的CURD

1.如何在前端动态生成标签?使用form验证可以针对model生成所有的字段控件

 实验:使用固定的数据模型去生成对应的form验证类,可以用来在前端之间生成控件

2.如何针对每张表动态生成一个Form类?需要用到type方法去动态生成类

 form_handle.py中去创建方法,动态创建类

3.在修改页面中动态创建Form类(需要传递原来数据)

 table_obj_change方法去创建form类,传递到前端

 url中对于修改的匹配

4.在添加页面动态创建Form类

 table_obj_add方法

 url.py中对于添加的匹配

添加的url

5.修改和添加的HTML和公共部分

 table_obj_add.html

 table_obj_change.html

 table_obj_change_component.html公共部分

6.处理在add和change中对于readonly_fileds字段的不同

 在动态生成ModelForm修改,并且向admin_class.add_flag加入标识,前端进行判别,决定是否去显示只读字段

7.对于filter_horizontal字段我们在模板函数中进行获取所有的值,并且判断是否显示在哪一个select标签中

 前端对filter_horizontal进行判别,针对两个select都进行判别,一个放置选中一个放置未选中

 get_rel_m2m_val模板函数获取关联对象得所有值,用到字段对象的related_model属性获取关联对象

 get_rel_m2m_sel方法判断是否数据被选中,返回True选中,放在第二个select标签,放在未选中,放在第一个select标签

8.实现js双击option,在两个select之间跳转

 为两个select标签绑定同一个MoveEleToOpp方法

 MoveEleToOpp方法实现:通过判断父标签select的id,将当前option转移append到对方的select中

9.实现在点击保存时,form表单自动将右侧select中的数据全部选中。注意:加上name为select标签,name="字段名"

 为form表单绑定方法ChangeSelStatus

 ChangeSelStatus实现

10.为filter_horizontal完善功能,添加全选,全部移除

 前端HTML

 ChooseAll函数js代码

Day5:删除功能开发和action方法实现

1.删除功能开发

 前端HTML代码

 模板函数,去递归生成标签

 views后台删除代码

2.action字段功能完善

 kingadmin.py中放置action字段,包含有自定义方法

 admin_base.py中需要去设置action默认数据

(1)设置form表单布局

 form表单

(2)设置复选框完成全选功能

 HTML代码

 CheckAll方法js完成全选

(3)提交表单前先生成隐藏表单去获取数据集

 Raw_input_action方法生成一个人input标签

(4)传递到后端进行处理

 在table_obj_list方法添加上post方法即可

3.处理action中的默认行为delete批量删除

(1)提交的url不是上面的table_obj_delete,而是本页面和change_status一起作为action传递入当前url

 delete_selected_objs的action方法

(2)获取delete_selected_objs在table_obj_list方法中返回

 table_obj_list中对于post的处理

若是执行完action方法后没有返回值则是正常执行,如果有返回值,则是代表我们接下来是执行删除操作。需要返回

(3)我们还是调用的上面的table_obj_delete.html页面,但是其中的模板标签函数,是针对一个数据对象,而现在是一个数据集,我们需要再次处理

 简单改变模板函数get_del_obj,将原来单个对象也改写为可迭代

(4)我们提交数据,也不再是table_obj_delete方法,而是table_obj_list方法,所以我们需要传递一个数据代表要删除的数据id集合,同时一个一个标识

 在显示的table_obj_delete.html页面加入隐藏标签,收集所有id集合

 get_del_objs_id模板函数收集所有的数据对象的id,json序列化返回给前端

(5)views页面根据post传递过来的隐藏标签的name,判断是不是执行删除数据操作

 获取前端input表单名delete_ids,判断是否有数据,来决定是否删除

4.实现面包屑导航

 

 add页面导航

 list页面导航

 change页面导航

 get_nva_active模板函数获取对象的中文名

 delete页面导航

 get_nav_del模板函数组合对象名

5.左侧菜单状态

 index页面在生成url时,对其进行判断。要分辨动态和绝对

Day6:学员报名流程开发

 新增3张表:学员注册表,合同表(和班级关联),缴费记录表

一:销售为想报名的学员提供链接

 Student_encroll学员注册链接获取

二:学员获取链接,进行填写信息,查阅合同,同意并上传证件信息

 enrollment学员在线报名

 erollment.html报名页面,含有Dropzone使用

 enrollment_fileupload处理Dropzone文件传输

三:销售审核学员注册信息,审核通过,为其生成账号(密码需要使用Django模块加密),发送邮件

from django.contrib.auth.hashers import make_password  #用于生成密码

 contract_audit合同审核后生成账号,发送信息

Django自带邮件发送模块

(1)settings中配置

(2)导入模块发送邮件

day7:实现讲师和学员作业发布上传功能(其中由于多使用table浏览数据,可以自定义一个类似于form的基类,去统一实现table显示)

一:讲师功能

(1)可以看出上面多是table显示信息,下面自定义table_form类似于forms

 BaseForm实现通过表数据显示table

(2)使用方法

from Teacher.table_form import BaseForm

class ClassListForm(BaseForm):

display_list = ["self","branch","class_type","start_date","graduate_date"] #self代表直接显示本条数据__str__

field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},"study_record":{"a":{"href":"/teacher/classlist/{id}/class_list.html"}},"student":{"a":{"href":"/teacher/classlist/{id}/student_list.html"}}} #在前面的标签会显示在内层,在显示的数据外面加上标签

attrs = {"class":"table table-hover"}  #为table设置属性

extra_field = [{"student":{'verbose_name':"学员数量","model_attr":"student_set","function":"count"}},{"study_record":{"verbose_name":"上课记录","value":"上课记录"}}]

#额外自定义字段,若是有值value,会直接输出,否则会去当前实例集self.querysets的每一个实例中去获取相关的数据,使用函数去执行。字符串是调用自己的内置方法,function是调用自定义方法,同时可以使用"args"传递元组,"kwargs":传递字典作为参数

def __init__(self,model,querysets):

super(ClassListForm, self).__init__(model,querysets)

 StudentForm

 CourseForm

(3)在views中调用各个tableform

 所有显示table的函数

(4)前端调用{{ forms|safe }},form是定义的tableform变量,实现简单显示页面

 class_list.html

 course_list.html

 student_list.html

(5)添加记录

 使用form类,生成控件

 views调用classRec_add,进行显示和添加数据

 前端显示course_add.html

二:学员功能,实现课程显示,作业提交

(一)根据邮件中的账号密码登录

(二)实现查看班级,查看课程记录,提交作业

(1)由于这里也是table多使用,可以继续使用tableform

from Teacher.table_form import BaseForm

class ClassListForm(BaseForm):

display_list = ["self","class_type","start_date","graduate_date",] #,"couser","semester"

field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},'score':{"a":{"href":"127.0.0.1:8080"},},'mng_homework':{"a":{"href":"/student/course.html"}}} #在前面的标签会显示在内层

attrs = {"class":"table table-hover"}

extra_field = [{"score":{"verbose_name":"成绩","value":"成绩排名"}},{"mng_homework":{'verbose_name':"作业管理","value":"作业管理"}},]

class ClassRecordForm(BaseForm):

display_list = ["self","title","teacher","content","has_homework","homework"] #,"couser","semester"

field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},'fin_homework':{"a":{"href":"/student/homework/{id}.html"}}} #在前面的标签会显示在内层

attrs = {"class":"table table-hover"}

extra_field = [{"date":{"verbose_name":"日期","model_attr":"date","function":"strftime","args":("%Y-%m-%d %H:%M:%S",)}},{"fin_homework":{'verbose_name':"我的作业","value":"提交作业"}},]

#额外自定义字段,若是有值value,会直接输出,否则会去当前实例集self.querysets的每一个实例中去获取相关的数据,使用函数去执行。字符串是调用自己的内置方法,function是调用自定义方法

def __init__(self,model,querysets):

super(ClassRecordForm, self).__init__(model,querysets)

(2)view中调用,显示班级和课程,前端也是{{forms|safe}}

 显示班级和课程

(3)使用forms表单显示数据,使用Dropzone添加作业

 CourseRecordForm

 homework作业显示和添加

(4)前端代码,使用Dropzone处理数据,以及ajax删除数据

 homework.html

(5)后台处理数据删除

 delete_files根据ajax上传数据删除文件

总结:学会偷懒,化繁为简,学会总结业务,再去动态处理,而不是一直对数据库的增删改查,和重复一个业务逻辑

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐