第十一章, 学员报名流程开发
11.1. 面包屑的制作
Boorstrap 路径导航条 https://v3.bootcss.com/components/#navbar
(1)table_obj_list.html 页面面包屑
def table_obj_list
返回数据改成 locals()
table_obj_list.html
- kingadmin_tags.py
- @register.simple_tag
- def get_model_verbose_name(admin_class):
- return admin_class.model._meta.verbose_name
(2)change 页面的面包屑
table_obj_change.html
- <ol class="breadcrumb">
- <li><a href="/kingadmin/">Home</a></li>
- <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li>
- <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}/">{% get_model_verbose_name admin_class %}</a></li>
- <li class="active">{{ form_obj.instance }}</li>
- </ol>
- <h4 class="page-header"> 修改 {{ form_obj.instance }}</h4>
(3)add 页面的面包屑
因为 add 和 change 共用 tags 和 html. 所以要添加判断是 add 还是 change
table__obj_change_component.html
kingadmin_tags.py
table_obj_add.html
- <ol class="breadcrumb">
- <li><a href="/kingadmin/">Home</a></li>
- <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li>
- <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}/">{% get_model_verbose_name admin_class %}</a></li>
- <li class="active">ADD {{ model_name }}</li>
- </ol>
- <h2 class="page-header">{% get_model_name admin_class %}</h2>
- <h4 class="page-header"> 添加 {% get_model_name admin_class %}</h4>
11.2. 报名流程和 models 设计
(1) 后台修改左侧 "客户库" 的 url
(2) 左侧 menu 菜单添加 "active" 样式
kingadmin/index.html
如果当前的 url 跟 menu 的 url_name 就添加 "active"
- <ul class="nav nav-sidebar">
- {% for role in request.user.userprofile.role.select_related %}
- {% for menu in role.menus.select_related %}
- {% if request.path == menu.url_name %}
- <li class="active"><a href="{% if menu.url_type == 0 %}{{ menu.url_name }}{% else %}{% url menu.url_name %}{% endif %}">{{ menu.name }}</a></li>
- {% else %}
- <li><a href="{% if menu.url_type == 0 %}{{ menu.url_name }}{% else %}{% url menu.url_name %}{% endif %}">{{ menu.name }}</a></li>
- {% endif %}
- {% endfor %}
- {% endfor %}
- </ul>
报名流程
销售 发起报名流程, 选择班级, 发报名链接给学员
学员 填写在线报名表, 提交 gerenxinxi, 上传证件信息, 同意培训协议
销售 审核报名表, 审核通过后, 创建一条缴费记录, 自动把学员添加到相应的班级, 报名成功
models 设计
添加三张表 crm/models.py
- 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)
- consultant_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
班级关联合同表
修改 student 跟 customer 为一对一的关系
11.3. 报名页面
流程
销售填写客户跟班级, 点 "下一步" 提交
后台获取到客户 id 和班级 id, 在数据库中创建记录, 并生成一个报名链接, 返回到前端
前端显示报名链接, 然后销售把报名链接发给用户
- (1)crm/urls.py
- # crm/urls.py
- from django.conf.urls import url,include
- from crm import views
- urlpatterns = [
- url(r'^$', views.dashboard,name='sales_dashboard'),
- #学员报名
- url(r'^stu_enrollment/$', views.stu_enrollment,name='stu_enrollment'),
- ]
- (2)crm/views.py
- @login_required
- def stu_enrollment(request):
- customers = models.CustomerInfo.objects.all()
- class_lists = models.ClassList.objects.all()
- if request.method == 'POST':
- #获取提交的客户 id 和班级 id, 然后生成报名链接
- customer_id = request.POST.get('customer_id')
- class_grade_id = request.POST.get('class_grade_id')
- enrollment_obj = models.StudentEnrollment.objects.create(
- customer_id = customer_id,
- class_grade_id = class_grade_id,
- consultant_id = request.user.userprofile.id
- )
- #生成链接返回到前端
- enrollment_link = "http://localhost:8000/crm/enrollment/%s"% enrollment_obj.id
- return render(request,'crm/stu_enrollment.html',locals())
(3) 新建 templates/crm/stu_enrollment.html
crm/index.html
- crm/stu_enrollment.html
- {#templates/crm/stu_enrollment.html#}
- {% extends 'index.html' %}
- {% block right-content-container %}
- <h3 > 学员报名页 </h3>
- <form class="form-horizontal" method="post">
- {% csrf_token %}
- <div class="form-group">
- <label for="inputEmail3" class="col-sm-2 control-label"> 客户 </label>
- <div class="col-sm-10">
- <select name="customer_id" class="form-control">
- {% for customer in customers %}
- <option value="{{ customer.id }}">{{ customer }}</option>
- {% endfor %}
- </select>
- </div>
- </div>
- <div class="form-group">
- <label for="inputEmail3" class="col-sm-2 control-label"> 报名班级 </label>
- <div class="col-sm-10">
- <select name="class_grade_id" class="form-control">
- {% for class_grade in class_lists %}
- <option value="{{ class_grade.id }}">{{ class_grade }}</option>
- {% endfor %}
- </select>
- </div>
- </div>
- <input type="submit" class="btn btn-success pull-right" value="下一步">
- </form>
- {% if enrollment_link %}
- <p > 请将此报名链接复制并发送给学员填写 {{ enrollment_link }}</p>
- {% endif %}
- {% endblock %}
11.4. 学员填写报名信息
添加学员注册 url
添加 CustomerInfo 字段, 身份证信息, 紧急联络人, 性别
有些字段是只读的, 填写信息的时候不能修改, 因为如果设置了只读 (添加属性 disabled=true), 提交的时候会报这些字段为空, 导致提交错误
所以在前段添加了 js 代码, BeforeFormSubmit 在提交前去掉 disable=true(因为数据库中有默认值, 提交的时候就不会报错)
防止用户通过前端改 html 代码的方式改只读字段的信息, 所以在 form.py 里面添加了一个自定义的验证方法 (clean), 如果只读字段提交的时候信息跟数据库中默认的不一样, 就报错
- (1)crm/urls.py
- # crm/urls.py
- from django.conf.urls import url,include
- from crm import views
- urlpatterns = [
- url(r'^$', views.dashboard,name='sales_dashboard'),
- #学员报名
- url(r'^stu_enrollment/$', views.stu_enrollment,name='stu_enrollment'),
- #学员注册
- url(r'^enrollment/(\d+)/$', views.enrollment,name='enrollment'),
- ]
- (2)crm/models.py
CustomerInfo 表 添加字段
- (3)crm/form.py
- # crm/form.py
- from django.forms import ModelForm
- from crm import models
- from django import forms
- class CustomerForm(ModelForm):
- class Meta:
- model = models.CustomerInfo
- fields = "__all__"
- #不显示的字段
- exclude = ['consult_content','status','consult_courses']
- #只读的字段
- readonly_fields = ['contact_type','contact','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({'class':'form-control'})
- if field_name in cls.Meta.readonly_fields:
- field_obj.widget.attrs.update({'disabled':'true'})
- return ModelForm.__new__(cls)
- #只读字段不让用户通过浏览器改 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}))
- (4)crm/views.py
- def enrollment(request,enrollment_id):
- '''学员在线报名表地址'''
- enrollment_obj = models.StudentEnrollment.objects.get(id=enrollment_id)
- if request.method == 'POST':
- customer_form = form.CustomerForm(instance=enrollment_obj.customer,data=request.POST)
- if customer_form.is_valid():
- customer_form.save()
- return HttpResponse("你已成功提交报名信息, 请等待审核, 欢迎加入仙剑奇侠传")
- else:
- customer_form = form.CustomerForm(instance=enrollment_obj.customer)
- return render(request,'crm/enrollment.html',locals())
- (4)crm/enrollment.html
- {#templates/crm/enrollment.html#}
- {% extends 'index.html' %}
- {% block body %}
- <div class="container">
- <h3 > 仙剑奇侠传 | 学员报名 </h3>
- <div class="panel panel-primary">
- <div class="panel-heading">
- <h3 class="panel-title"> 学员在线报名 </h3>
- </div>
- <div class="panel-body">
- <form class="form" method="post" onsubmit="return BeforeFormSubmit(this)">
- {% csrf_token %}
- {% for field in customer_form %}
- <div class="form-group col-lg-6">
- <label class="col-sm-2 control-label">{{ field.label }}</label>
- <div class="col-sm-10">
- {{ field }}
- <span style="color: red;">{{ field.errors.0 }}</span>
- </div>
- </div>
- {% endfor %}
- <div class="form-group col-lg-6">
- <label class="col-sm-2 control-label"> 报名班级 </label>
- <div class="col-sm-10">
- {{ enrollment_obj.class_grade }}
- </div>
- </div>
- <div class="form-group col-lg-6">
- <label class="col-sm-2 control-label"> 学费 </label>
- <div class="col-sm-10">
- {{ enrollment_obj.class_grade.course.price }}
- </div>
- </div>
- <div class="col-sm-offset-11 col-sm-2">
- <input type="submit" class="btn btn-success" value="提交">
- </div>
- </form>
- </div>
- <div class="panel-footer"><a href="http://www.cnblogs.com/derek1184405959/">zhang_derek</a></div>
- </div>
- </div>
- <script>
- function BeforeFormSubmit(ele) {
- $(":disabled").removeAttr("disabled");
- }
- </script>
- {% endblock %}
在线报名填表页面
修改只读字段会报错
来源: https://www.cnblogs.com/derek1184405959/p/8985067.html