一、本节目标
前几节我们开发了数据展示,增删改查,本节开始开发业务流程,报名流程。
二、需求分析
1、报名流程如以下描述:
销售 发起报名流程,选择班级,发报名链接给学员
学员 填写在线报名表,提交个人信息,上传证件信息,同意培训协议
销售 审核报名表,审核通过后,创建一条缴费记录,自动把学员添加到相应的班级,报名成功
三、功能开发
1、表设计
因为需要存储学生报名,缴费信息,因此需要设计表结构,同时合同信息也需要表来存储,因此添加model
class ContractTemplate(models.Model):
'''存储合同模板'''
name = models.CharField(max_length=64)
content = models.TextField()
date = models.DateField(auto_now_add=True)
class StudentEnrollment(models.Model):
'''学员报名表'''
customer = models.ForeignKey('CustomerInfo',on_delete=models.CASCADE)
class_grade = models.ForeignKey('ClassList',on_delete=models.CASCADE)
consultant = models.ForeignKey('UserProfile',on_delete=models.CASCADE)
contract_agreed = models.BooleanField(default=False)
contract_signed_date = models.DateTimeField(blank=True,null=True)
contract_approved = models.BooleanField(default=False)
contract_approved_date = models.DateTimeField('合同审核时间',blank=True,null=True)
class Meta:
unique_together = ('customer','class_grade')
def __str__(self):
return '%s'%self.customer
class PaymentRecord(models.Model):
'''存储学员缴费记录'''
enrollment = models.ForeignKey('StudentEnrollment',on_delete=models.CASCADE)
payment_type_choices = ((0,'报名费'),(1,'学费'),(2,'退费'))
payment_type = models.SmallIntegerField(choices=payment_type_choices,default=0)
amount = models.IntegerField('费用',default=500)
consultant = models.ForeignKey('UserProfile',on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s'%self.enrollment
2、按照流程,开发报名流程页面:
销售人员根据学员id,以及班级名称,生成一个报名链接:
在crm应用下,添加url:
编写视图函数:
创建一个名为:stu_enrollment的前端页面:
学员报名页
{% csrf_token %}
客户
{% for customer in customers %}
{{ customer.name }}
{% endfor %}
报名班级
{% for class_grade in class_lists %}
{{ class_grade }}
{% endfor %}
{% if enrollment_link %}
请将此报名链接复制并发送给学员填写 {{ enrollment_link }}
{% endif %}
效果如图:
3、开发学员填写注册信息页面:
学员填写注册信息,需要有一个页面,因此,还是在crm应用下,首先添加url:
customerinfo添加如下字段:
在crm创建from.py文件,生成customerinfo的form
防止用户通过前端改html代码的方式改只读字段的信息,所以在form.py里面添加了一个自定义的验证方法(clean),如果只读字段提交的时候信息跟数据库中默认的不一样,就报错
from django import forms
from django.forms import ModelForm
from crm import models
class CustomerForm(ModelForm):
class Meta:
model = models.CustomerInfo
fields = "__all__"
#不显示的字段
exclude = ['consult_content','status','consult_courses']
#只读的字段
readonly_fields = ['contact_type','contract','consultant','referral_from','source']
#Django是通过“__new__”方法,找到ModelForm里的每个字段的,然后循环处每个字段添加自定义样式
def __new__(cls, *args, **kwargs):
#cls.base_fields是一个元组,里面的数据结构是 [(字段名,字段的对象),(),()]
for field_name in cls.base_fields:
field_obj = cls.base_fields[field_name]
#添加属性
field_obj.widget.attrs.update({'disable':'true'})
return ModelForm.__new__(cls)
#只读字段不让用户通过浏览器改HTML代码的方式修改
def clean(self):
#表单级别的错误
if self.errors:
raise forms.ValidationError(("please fix errors before re-submit"))
#如果有instance id,表名这是一个修改的表单,应该检查只读字段
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}))
编写视图函数:
前端页面:
在线报名信息填写
学员在线报名
{% csrf_token %}
{% for field in customer_form %}
{{ field.label }}
{{ field }}
{{ field.errors.0 }}
{% endfor %}
报名班级
{{ enrollment_obj.class_grade }}
学费
{{ enrollment_obj.class_grade.course.price }}
function BeforeFormSubmit(ele) {
$(":disabled").removeAttr("disabled");
}
页面效果如图:
4、如果修改了只读字段,会提示错误:
5、开发学生签署合同信息以及上传证件照片功能
首先,添加一个上传文件的url:
建一个存储上传文件的文件夹:
在settings里添加上传文件路径:
CRM_FILE_UPLOAD_DIR = os.path.join(BASE_DIR,'crm/upload_files/enrollment_data')
编写视图函数:
@csrf_exempt
def enrollment_fileupload(request,enrollment_id):
'''学员报名文件上传'''
enrollment_upload_dir = os.path.join(conf.settings.CRM_FILE_UPLOAD_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}),)
前端页面完善修改:
这里使用到了dropzone: https://www.dropzonejs.com/#installation,下载将css和js文件放到static里:
9、点击后提示报名成功:
10、接下来该审核合同了
首先,添加审核的url:
编写视图函数:
def contract_audit(request,enrollment_id):
enrollment_obj = models.StudentEnrollment.objects.get(id=enrollment_id)
if request.method == 'POST':
enrollment_form = form.EnrollmentForm(instance=enrollment_obj, data=request.POST)
if enrollment_form.is_valid():
enrollment_form.save()
stu_obj = models.Student.objects.get_or_create(customer=enrollment_obj.customer)[0]
# m2m, 添加班级
stu_obj.class_grades.add(enrollment_obj.class_grade_id)
stu_obj.save()
# 改变报名
enrollment_obj.customer.status = 1
enrollment_obj.save()
return redirect("/backadmin/crm/customerinfo/%s/change" % enrollment_obj.customer.id)
else:
# 拿到客户信息的表单
customer_form = form.CustomerForm(instance=enrollment_obj.customer)
enrollment_form = form.EnrollmentForm(instance=enrollment_obj)
return render(request, 'contract_audit.html', locals())
添加enrollment的form
class EnrollmentForm(ModelForm):
class Meta:
model = models.StudentEnrollment
fields = '__all__'
def __new__(cls, *args, **kwargs):
# cls.base_fields是一个元组,里面是 所有的 【(字段名,字段的对象),(),()】
for field_name in cls.base_fields:
# 获取每个字段的对象
field_obj = cls.base_fields[field_name]
# 添加属性
field_obj.widget.attrs.update({'class': 'form-control'})
return ModelForm.__new__(cls)
设计html页面:
学员报名|合同审核
{% csrf_token %}{{ customer_form }}
{{ enrollment_form }}
function BeforeFormSubmit(ele) {
$(":disabled").removeAttr("disabled");
}
效果如图: