客户关系管理(customer relationship management)的定义是:企业为提高核心竞争力,利用相应的信息技术以及互联网技术协调企业与顾客间在销售、营销和服务上的交互,从而提升其管理方式,向客户提供创新式的个性化的客户交互和服务的过程。其最终目标是吸引新客户、保留老客户以及将已有客户转为忠实客户,增加市场。
1.提高市场营销效果
2.为生产研发提供决策支持
3.提供技术支持的重要手段
4.为财务金融策略提供决策支持
5.为适时调整内部管理提供依据
6.使企业的资源得到合理利用
7.优化企业业务流程
8.提高企业的快速响应和应变能力
9.改善企业服务,提高客户满意度
10.提高企业的销售收入
11.推动了企业文化的变革
12.与QQ集成,可以快速与客户沟通
销售
销售B 从 qq群聊了客户,且报名了python全栈9期课程,给用户发送了报名连接,待用户填写完毕后,把他添加到了python fullstack s9的班级里
销售C 打电话给之前的一个客户,说服他报名linux40期,但是没说服成功,更新了跟踪记录
销售主管 查看了部门 本月的销售报表, 包括来源分析,成单率分析,班级报名数量分析,销售额同比
学员
客户A 填写了销售发来的报名链接,上传了个人的证件信息,并提交,过了一会儿,发现收到一个邮件,告知他报名python9期课程成功,并帮他开通了学员账号
学员A 登录了学员系统,看到了 自己的合同,报名的班级,以及课程大纲
讲师
批量下载了所有学员的python9期第一节的作业,给每个学生在线 打成绩+批注
管理员
创建了 课程(linux,python)
登录
from django.shortcuts import render,redirect
from django.contrib.auth import authenticate,login
def acc_login(request):
if request.method == 'POST':
username = request.POST.get('username',None)
password = request.POST.get('password',None)
#user是一个对象
#验证
user = authenticate(username=username,password=password)
if user:
#登录(已生成session)
login(request,user)
#如果有next值就获取next值,没有就跳转到首页
return redirect(request.GET.get('next','/'))
return render(request,'login.html')
动态菜单生成
- 首先获取登录的用户(User)
- 通过User反向查找到UserProfile(用户信息)
- 然后通过UserProfile找到用户关联的所有角色
- 最后通过角色循环遍历出用户所有的菜单
OneToOneField和ForeignKey反向获取
- OneToOneField反向查,直接request.user.userprofile 后面跟反向的表名(小写)就可以
- 如果是FK,直接request.user.userprofile_set 后面跟反向的表名(小写)+“_set” 就可以
- request.user.userprofile.role.select_related等价于request.user.userprofile.role.all
自定义前端kingadmin全局注册时报错NoneType
是因为我们在注册model的时候,有的写了自定义的model类,有的没写,而我们都统一的赋值,导致那些没写自定义model类(空的)赋值的时候就会报NoneType错误
django自带的自定义admin类的写法继承了ModelAdmin,那注册的时候为什么有的没写自定义admin类没有报错呢?
是因为继承的admin.ModelAdmin
帮我们写了(里面其实都定义为空了),我们模仿django admin的写法,也写个父类。
分页
官方实例
报名页面流程
- 销售填写客户跟班级,点“下一步”提交
- 后台获取到客户id和班级id,在数据库中创建记录,并生成一个报名链接,返回到前端
- 前端显示报名链接,然后销售把报名链接发给用户
学员填写报名信息
- 添加学员注册url
- 添加CustomerInfo字段,身份证信息,紧急联络人,性别
- 有些字段是只读的,填写信息的时候不能修改,因为如果设置了只读(添加属性disabled=true),提交的时候会报这些字段为空,导致提交错误
- 所以在前段添加了js代码,BeforeFormSubmit 在提交前去掉disable=true(因为数据库中有默认值,提交的时候就不会报错)
- 防止用户通过前端改html代码的方式改只读字段的信息,所以在form.py里面添加了一个自定义的验证方法(clean),如果只读字段提交的时候信息跟数据库中默认的不一样,就报错
#只读字段不让用户通过浏览器改html代码的方式改
def clean(self):
# 表单级别的错误
if self.errors:
raise forms.ValidationError(("Please fix errors before re-submit."))
# means this is a change form ,should check the readonly fields
if self.instance.id is not None:
#取出只读字段,是一个字符串形式
for field in self.Meta.readonly_fields:
#通过反射取出字段的值(数据库里的数据)
old_field_val = getattr(self.instance, field)
#提交过来的数据
form_val = self.cleaned_data.get(field)
#如果两个数据不匹配
if old_field_val != form_val:
#就提示只读字段不能修改
#add_error是字段级别的错误
self.add_error(field, "Readonly Field: field should be '{value}' ,not '{new_value}' ".format(**{'value': old_field_val, 'new_value': form_val}))
学员报名合同及身份信息上传
- 必须勾选报名合同协议
- 必须上传个人证件信息
- 最多只能上传三个文件
- 文件大小2M以内
- 列出已上传文件
@csrf_exempt
def enrollment_fileupload(request,enrollment_id):
'''学员报名文件上传'''
enrollment_upload_dir = os.path.join(conf.settings.CRM_FILE_UOLOAD_DIR,enrollment_id)
#第一次上传图片就创建目录,学员上传第二章图片的时候,会判断目录是否已经存在
#因为如果目录存在还mkdir就会报错,所以这里要做判断
if not os.path.isdir(enrollment_upload_dir):
os.mkdir(enrollment_upload_dir)
#获取上传文件的对象
file_obj = request.FILES.get('file')
#最多只允许上传3个文件
if len(os.listdir(enrollment_upload_dir)) <= 3:
#把图片名字拼接起来(file.name:上传的文件名字)
with open(os.path.join(enrollment_upload_dir,file_obj.name),'wb') as f:
for chunks in file_obj.chunks():
f.write(chunks)
else:
return HttpResponse(json.dumps({'status':False,'err_msg':'最多只能上传三个文件'}))
return HttpResponse(json.dumps({'status':True,}),)
# 列出学员已上传的文件
upload_files = []
enrollment_upload_dir = os.path.join(conf.settings.CRM_FILE_UOLOAD_DIR, enrollment_id)
if os.path.isdir(enrollment_upload_dir):
upload_files = os.listdir(enrollment_upload_dir)
合同审核
- 学员提交报名信息后,应该是等待审核状态
- 管理员改审核状态为ture, 并保存提交时间
所有权限
perm_dic = {
# 'crm_table_index': ['table_index', 'GET', [], {}, ], # 可以查看CRM APP里所有数据库表
# 可以查看每张表里所有的数据
'crm_table_list': ['table_obj_list', 'GET', [], {}],
# 'crm_table_list': ['table_obj_list', 'GET', [], {'source':0,'status':0}], # 添加参数:只能访问来源是qq和未报名的客户
# 可以访问表里每条数据的修改页
'crm_table_list_view': ['table_obj_change', 'GET', [], {}],
# 可以对表里的每条数据进行修改
'crm_table_list_change': ['table_obj_change', 'POST', [], {}],
# 可以访问数据增加页
'crm_table_list_add_view': ['table_obj_add ', 'GET', [], {}],
# 可以添加表数据
'crm_table_list_add': ['table_obj_add ', 'POST', [], {}],
}
*per_name和split用法
> a,*b,c=[1,2,3,4,5,6]
>a
1
>c
6
>b
[2,3,4,5]
>>>a='crm_table_list'
>>>a.split('_')
['crm','table','list']
>>>
>>>app_name,*per_name=a.split('_')
>>>
>>>app_name
'crm'
>>>per_name
# 权限
['table','list']