CRM客户关系管理系统(十一) 第十一章、学员报名流程开发

第十一章、学员报名流程开发

11.1.面包屑的制作

Boorstrap路径导航条

 (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 %}

在线报名填表页面

 修改只读字段会报错

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏康怀帅的专栏

Ubuntu 常用软件

本文列举了 Ubuntu 常用软件。 娱乐 网易云音乐 http://music.163.com/#/download mpv 播放器 https://mpv....

4895
来自专栏漏斗社区

黑客游戏| Owasp juice shop (一)

0x01 前言 最近看到一篇关于owasp juice shop的文章,觉的很有意思,斗哥就自己撸了个环境,上手后深深觉的这是一个很棒的漏洞靶场,所以就把该...

4988
来自专栏difcareer的技术笔记

ELF文件格式修复

在IDA动态调试-没啥卵用的静态加固中,我构造了一个畸形的ELF文件,虽然能够糊弄一下IDA的静态分析,但是动态分析无效。

1806
来自专栏Python Web学习记录及整理

Django开发在线教育平台--学习整理(一)

首先,打开cmd,cd到存放django项目的文件夹,创建一个新工程(也可以用虚拟环境virtualenv):

8678
来自专栏杨建荣的学习笔记

system表空间不足的问题分析(r6笔记第66天)

很多事情见多了也就有了麻木的感觉,报警短信就是如此,每天总能收到不少的报警短信,可能很多时候就扫一眼,如果没有严重的问题自己是不会情愿打开电脑处理的。 对于此,...

2754
来自专栏JavaEdge

Druid入门应用场景存储系统选择Druid介绍

设计一个系统来预估未来一年的广告流量,不是总流量,是任意时间段任何定向(Targeting)条件约束情况下的流量。定向条件有近百种(内容类别,设备平台,用户地域...

4364
来自专栏androidBlog

360面试总结(Android)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

871
来自专栏coding

听说,撸代码,ide与vim更配哦vim折腾记vim常用命令

在选择编辑器上面,我是一个纠结的人,曾经年少的我执着地追求一款万能的编辑器,可以支持所有编辑语言,灵活可定制,可纯粹用键盘操作。符合这种条件的编辑器,非vim莫...

1012
来自专栏程序猿DD

极大提高工作效率的几十种神兵利器

来源:https://www.liutf.com/posts/3720794851.html

2193
来自专栏数据和云

Oracle的SQL多版本控制 - VERSION_COUNT

在上一期的“恩墨讲堂”微信课中,我提到了一个控制SQL多版本的参数,有很多朋友讨论,这个参数是:_CURSOR_OBSOLETE_THRESHOLD 。 为什么...

3096

扫码关注云+社区

领取腾讯云代金券