今日内容 1. 沟通记录 1. 展示沟通记录 2. 添加和编辑沟通记录 1. form表单实例化 1. 如何修改字段的choices选项 1. 重写父类的__init__方法中修改 2. 如何设置默认值 1. Form(initial={'字段名': '默认值'}) 3. ModelForm在实例化的时候可以传instance参数 1. 生成form表单的时候,所有输入框的默认值就是instance实例里的对应属性 2. 表单提交的时候,调用save()会用POST数据去更新指定的instance实例 4. 业务逻辑 1. 点击左侧菜单的沟通记录 --> 查看的是当前销售的所有客户的沟通记录 2. 点击客户列表页面每个客户的查看按钮 --> 查看的是当前客户的所有沟通记录 3. URL反向解析传参数 当URL路由中设置的是 分组匹配 ->位置参数 分组命令匹配 ->关键字参数 1. 模板中 --> {% url '别名' 参数 %} 2. 视图中 --> reverse('别名', args=(参数,)) | reverse('别名', kwargs={k:v}) 2. 报名表 1. 查看 1. 点击左侧查看报名表,显示的是我这个销售的所有客户的报名表 2. 添加报名表和编辑报名表 1. 添加报名表一定是给某个具体的客户添加报名表 1. 添加完报名表之后要更新客户的状态 2. 编辑报名表 1. 给一个具体的报名表做编辑 3. 在客户列表页面展示一个查看当前客户所有报名表的链接 3. 公户-->私户 1. 如何限制同一客户只能被同一个招生老师 获取 1. 开始事务 from django.db import transaction with transaction.atomic(): 2. 设置行级锁 Customer.objects.filter(id__in=cid, consultant__isnull=True).select_for_update() 3. MySQL: 开启事务:begin; 设置行级锁:select * from student for update; 关闭事务:commit; 2. 私户上限 1. 销售已经有的客户数 + 要转为私户的客户数 不能超过私户限制
一. 沟通记录增改查
进入跟进记录页面点添加,弹出一页面让我填---用到modelform,但此处的modelform和之前写的modelform有区别,此modelform有两限定条件:只能给自己的客户添加沟通记录-->如何给form表单的字段设置指定的choice选项;跟进人默认是自己-->如何给form表单字段设置默认值--在form实例化时里可以给它实例化传一个initial={'字段':'默认值'}参数--->衍生出当我在做编辑时modelform在实例化时可传instance参数,我借助这个instance表示我要编辑的实例对象是谁,好处是传过来的instance参数直接生成form表单时:(点编辑时)-->生成form表单时,就是instance实例里的属性设置成所有输入框的默认值,(点提交时)--->直接调用save()方法用post数据去更新指定的instance实例。
(1)views.py中:
查客户沟通记录有两个:指定客户的沟通记录和所有客户的沟通记录,cid不传值则是查所有,cid传值则是查指定的。
#展示沟通记录 @login_required def consult_record_list(request,cid=0):#0表示此参数可以不传默认0 if int(cid) == 0: query_set = ConsultRecord.objects.filter(consultant=request.user, delete_status=False)#当前用户的所有客户 else:#从数据库查询指定客户的没有删除的沟通记录--只拿到当前选中的客户 query_set = ConsultRecord.objects.filter(customer_id=cid,delete_status=False) return render(request, 'consult_record_list.html', {'consult_record': query_set}) # 添加和编辑沟通记录 def consult_record(request, edit_id=None): record_obj = ConsultRecord.objects.filter(id=edit_id).first() # 根据edit_id去找 if not record_obj: record_obj = ConsultRecord(consultant=request.user) # 生成一个销售是我的ConsultRecord对象,此时在form表单中通过instance就能拿到此对象 form_obj = ConsultRecordForm(instance=record_obj, initial={'consultant': request.user})#initial告诉form表单字段设默认值 if request.method == 'POST': form_obj = ConsultRecordForm(request.POST,instance=record_obj)#把提交的数据放进来去更新数据库里的record_obj这个对象,且不需传初始值 if form_obj.is_valid():#如果数据没问题 form_obj.save()#这个save做了两事:更新实例和更新多对多关联的数据 return redirect(reverse('consult_record',kwargs={'cid':0}))#给此url传一关键字参数 return render(request, 'consult_record.html', {'form_obj': form_obj, 'edit_id': edit_id})
(2)forms.py中:
跟进人只能是当前登录用户,怎么实现?---把choice选项值设成当前登录用户即可。
# 沟通记录的form class ConsultRecordForm(BootstrapBaseForm): class Meta: model = ConsultRecord exclude = ['delete_status', ]#排除此字段 def __init__(self, *args, **kwargs):#重写init方法 super().__init__(*args, **kwargs)#继承父类方法,给每一字段加class=formcontranl #如何给form表单的字段设置指定的choice选项:方法1把customer字段的choice设置成我的客户 # self.fields['customer'].choices = Customer.objects.filter(consultant=self.instance.consultant).values_list('id','name') # 方法2将form表的字段直接修改 self.fields['customer'] = forms.models.ModelChoiceField(queryset=Customer.objects.filter(consultant=self.instance.consultant)) self.fields['customer'].widget.attrs.update({'class': 'form-control'}) #跟进人只能是自己 self.fields['consultant'].choices = [(self.instance.consultant.id,self.instance.consultant.name),]
(3)consult_record_list.html中:
把跟进状态改成获取一具体值。<td>{{ record.get_status_display }}</td>
(4)customer_list.html中:
在客户列表页面,给每一客户增加一列(查看沟通记录)。
点击查看时是只能查看指定客户的沟通记录
<th style="width: 120px!important;">沟通记录</th> ..... <td><a href="{% url 'consult_record' cid=customer.id %}">查看</a></td>
(5)crm_urls.py中:
给查看沟通记录的url添加分组命名--那此时我views.py中的cid可以不传值。注意你的customer_list.html中也传cid参数。
给编辑沟通记录的url添\d+
url(r'^consult_record/(?P<cid>\d+)/$', views.consult_record_list, name='consult_record'), # 查看沟通记录
url(r'^add_consult_record/$', views.consult_record, name='add_record'), # 添加沟通记录
url(r'^edit_consult_record/(\d+)$', views.consult_record, name='edit_record'), # 编辑沟通记录
(6)base.html中:
<li><a href="{% url 'consult_record' 0%}">跟进记录</a>{# url反转映射 #}
这样就实现沟通记录的增改查了。如下图:

二.报名表增改查
添加报名表只能给自己的客户添加--给form字段(客户名称和所报班级--只能从他自己的课程中选 )做限定。
如下图中,所报班级,填报名表时,这个班应该是之前所报的班级,但是这个存不进去的,因为models中报名设计的是unique_together=('enrolment_clall','customer')即报名班级和客户只能报一次(一个人一个班只能报一次)。所以这个不能用form表单了只能用ajax和js做--->选 了school校区框,所报班级框检测到校区框值发生变化就把它自己的值换成数据库中当前这个校区下的班级。

(1)base.html:母版中
<li><a href="{% url 'enrollment_list' 0 %}">查看报名表</a></li>
(2)crm_urls.py中:
from django.conf.urls import url from crm import views, ajax_views urlpatterns = [ .... url(r'^enrollment_list/(?P<customer_id>\d+)/$', views.EnrollmentListView.as_view(), name='enrollment_list'), # 查看报名记录 url(r'^add_enrollment/(?P<customer_id>\d+)/$', views.enrollment, name='add_enrollment'), url(r'^edit_enrollment/(?P<enrollment_id>\d+)/$', views.enrollment, name='edit_enrollment'), # AJAX url(r'^ajax_class/$', ajax_views.ajax_class), ]
(3)views.py中:
返回一个页面让用户去填,所以用到modelform.
from crm.forms import RegForm, CustomerForm, ConsultRecordForm, EnrollmentForm from crm.models import UserProfile, Customer, ConsultRecord, Enrollment class EnrollmentListView(views.View):#报名表 def get(self, request, customer_id=0):#当客户id不传时默认查所有的 if int(customer_id) == 0:# 查询当前这个销售所有客户的报名表 跨到客户表查 query_set = Enrollment.objects.filter(customer__consultant=request.user)#用customer表的consultant字段做检索用双下划线 else: query_set = Enrollment.objects.filter(customer_id=customer_id) return render(request, 'enrollment_list.html', {'enrollment_list': query_set}) # 添加/编辑报名记录 def enrollment(request, customer_id=None, enrollment_id=None): # 先根据报名表id去查询 enrollment_obj = Enrollment.objects.filter(id=enrollment_id).first()#拿到报名中当前的客户 if not enrollment_obj:# 查询不到报名表id说明是新增报名表操作,又因为新增报名表必须指定客户 enrollment_obj = Enrollment(customer=Customer.objects.filter(id=customer_id).first()) form_obj = EnrollmentForm(instance=enrollment_obj)#传instance是为在form表单中拿到报名表对象 if request.method == 'POST': form_obj = EnrollmentForm(request.POST, instance=enrollment_obj) if form_obj.is_valid(): new_obj = form_obj.save() #生成一新的报名表对象 new_obj.customer.status = 'signed'# 报名成功,更改客户当前的状态 new_obj.customer.save() # 改的是哪张表的字段就保存哪个对象 return redirect(reverse('enrollment_list')) return render(request, 'enrollment.html', {'form_obj': form_obj})
(4)ajax_views.py中:
单独写一个ajax的views.
from django.http import JsonResponse from crm import models def ajax_class(request): res = {'code': 0, 'data': []} sid = request.GET.get('sid') # 根据前端发送的校区id找出该校区下面所有的班级 query_set = models.ClassList.objects.filter(campuses_id=sid)#拿到该校区的一个个班级对象 for c in query_set:#拿到一个个班级对象并和显示的内容和学期拼接成一起放到字典中 res['data'].append({'id': c.id, 'name': '{}-{}'.format(c.get_course_display(), c.semester)}) return JsonResponse(res)
(5)enrollment_list.html中:和跟进记录页面一样
{% extends 'base.html' %}
{% block page-main %}
<h2 class="sub-header">报名表</h2>
<div class="col-md-12">
<div class="col-md-4 pull-right">
<form action="" method="get" enctype="application/x-www-form-urlencoded">
<div class="input-group">
<input type="text" name="query" class="form-control" placeholder="Search for...">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">搜呀</button>
</span>
</div><!-- /input-group -->
</form>
</div>
</div>
<form action="" method="post">
{% csrf_token %}
<div class="col-md-3" style="margin: 5px 0">
<div class="input-group">
<select class="form-control" name="action">
<option value="">---------</option>
<option value="to_public">变为公户</option>
<option value="to_private">变为私户</option>
<option value="delete">删除</option>
</select>
<div class="input-group-btn">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</div>
<div class="col-md-12">
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th style="width: 20px">选择</th>
<th style="width: 20px">#</th>
<th style="width: 80px">客户</th>
<th style="width: 80px">课程</th>
<th style="width: 80px">校区</th>
<th style="width: 80px">报名原因</th>
<th style="width: 80px">期望</th>
<th style="width: 80px">是否同意协议</th>
<th style="width: 60px">报名时间</th>
<th style="width: 80px">备注</th>
<th style="width: 20px!important;">操作</th>
</tr>
</thead>
<tbody>
{% for enrollment in enrollment_list %}
<tr>
<td><input type="checkbox" name="cid" value="{{ enrollment.id }}"></td>
<td>{{ forloop.counter }}</td>
<td>{{ enrollment.customer.name }}</td>
<td>{{ enrollment.enrolment_class }}</td>
<td>{{ enrollment.school }}</td>
<td>{{ enrollment.why_us }}</td>
<td>{{ enrollment.your_expectation }}</td>
<td>{{ enrollment.contract_agreed }}</td>
<td>{{ enrollment.enrolled_date }}</td>
<td>{{ enrollment.memo }}</td>
<td><a href="{% url 'edit_enrollment' enrollment.id %}"><i class="fa fa-edit"
aria-hidden="true"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div>
{{ page_html|safe }}
</div>
</div>
</div>
</form>
{% endblock %}
(6)forms.py中:
from crm.models import UserProfile, Customer, ConsultRecord,Enrollment, ClassList class CustomerForm(BootstrapBaseForm): class Meta: model = Customer fields = '__all__' widgets = { 'course': forms.widgets.SelectMultiple, 'birthday': forms.widgets.DateInput(attrs={'type': 'date'}), } # 沟通记录的form class ConsultRecordForm(BootstrapBaseForm): class Meta: model = ConsultRecord exclude = ['delete_status', ]#排除此字段 def __init__(self, *args, **kwargs):#重写init方法 super().__init__(*args, **kwargs)#继承父类方法,给每一字段加class=formcontranl #如何给form表单的字段设置指定的choice选项:方法1把customer字段的choice设置成我的客户 # self.fields['customer'].choices = Customer.objects.filter(consultant=self.instance.consultant).values_list('id','name') # 方法2将form表的字段直接修改 self.fields['customer'] = forms.models.ModelChoiceField(queryset=Customer.objects.filter(consultant=self.instance.consultant)) self.fields['customer'].widget.attrs.update({'class': 'form-control'}) #跟进人只能是自己 self.fields['consultant'].choices = [(self.instance.consultant.id,self.instance.consultant.name),] # 报名表 class EnrollmentForm(BootstrapBaseForm): class Meta: model = Enrollment exclude = ['contract_approved', 'delete_status'] def __init__(self, *args, **kwargs):#重写 super().__init__(*args, **kwargs) # 给限制添加报名表的时候只能选自己私户 # self.instance是拿到报名表对象--限定客户框只能拿它自己的id和name self.fields['customer'].choices = [(self.instance.customer.id, self.instance.customer.name)]
(7)enrollment.html中:和添加/编辑跟进记录一样
在下面配置自己的js
{% extends 'base.html' %}
{% block page-main %}
<div>
<h2 class="text-center">{% if edit_id %}编辑报名表{% else %}添加报名表{% endif %}</h2>
<form class="form-horizontal" action="" method="post" novalidate>
{% csrf_token %}
{% for field in form_obj %}
<div class="form-group {% if field.errors.0 %}has-error{% endif %}">
<label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">提交</button>
</div>
</div>
</form>
</div>
{% endblock %}
{% block page-js %}
{% load static %}
<script src="{% static 'jquery.js' %}"></script>
<script>//写自己的脚本(document.ready)是文档加载完后给校区绑定change事件)
$(document).ready(function () {
$('#id_school').on('change', function () {
var sId = $(this).val(); //当它的值发生变化时取到它的---->// 获取到选中的校区id
// 发AJAX到后端取出当前校区下面所有的班级课程
$.ajax({
url: '/crm/ajax_class/',//往哪里发
type: 'get', //get请求
data: {'sid': sId}, //校区id
success: function (res) { //当成功后
console.log(res); //打印
var $classSelect = $('#id_enrolment_class');//找到id后保存在classseledt变量中
$classSelect.text('');//先把select标签内部文本清空
$classSelect.append('<option value="">-------</option>');//给它的option标签设空值
$.each(res.data, function (k, v) {//each是循环
var opEle = document.createElement('option');//创建新option选项
$(opEle).text(v.name).attr('value', v.id);//给option选项获取文本内容和设置value值
$classSelect.append(opEle);//select标签追加option选项
})
}
})
})
})
</script>
{% endblock %}



三.开启事务和行级锁
考虑到多个销售抢一个客户的情况,公户列表中怎样保证一个用户某客户时,别的用户不能对此客户改--给此要更新的数据加个锁--别人再来操作时就卡住了。
且私户有上限--写在settings.py中。
(1)settings.py中:
#销售的私户限制数CUSTOMER_NUM_LIMIT= 3
(2)views.py中:
做判断。
四.班级列表增改查页面--班主任的操作
创建一班主任的视图,所有班主相关的视图.
一点添加和编辑按钮就是form表单--二合一视图。
(1)mei_views.py:
# 班主任相关的视图都放在这里 from django import views from django.shortcuts import render, redirect from crm.models import ClassList from crm.forms import ClassListForm from django.urls import reverse class ClassListView(views.View): def get(self, request): query_set = ClassList.objects.all() return render(request, 'class_list.html', {'class_list': query_set}) # 新增和编辑班级 def op_class(request, edit_id=None): edit_obj = ClassList.objects.filter(id=edit_id).first() form_obj = ClassListForm(instance=edit_obj) if request.method == 'POST': form_obj = ClassListForm(request.POST, instance=edit_obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('class_list')) return render(request, 'op_class.html', {'form_obj': form_obj, 'edit_id': edit_id})
(2)crm_urls.py中:
from django.conf.urls import url from crm import views, ajax_views, mei_views 。。。。 班级管理 url(r'^class_list/$', mei_views.ClassListView.as_view(), name='class_list'), url(r'^add_class/$', mei_views.op_class, name='add_class'), url(r'^edit_class/(\d+)/$', mei_views.op_class, name='edit_class'),
(3)class_list.html中:展示
{% extends 'base.html' %}
{% block page-main %}
<h2 class="sub-header">班级列表</h2>
<div class="col-md-12">
<a href="{% url 'add_class' %}" class="btn btn-success btn-sm">添加</a>
<div class="col-md-4 pull-right">
<form action="" method="get" enctype="application/x-www-form-urlencoded">
<div class="input-group">
<input type="text" name="query" class="form-control" placeholder="Search for...">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">搜呀</button>
</span>
</div><!-- /input-group -->
</form>
</div>
</div>
<form action="" method="post">
{% csrf_token %}
<div class="col-md-3" style="margin: 5px 0">
<div class="input-group">
<select class="form-control" name="action">
<option value="">---------</option>
<option value="to_public">变为公户</option>
<option value="to_private">变为私户</option>
<option value="delete">删除</option>
</select>
<div class="input-group-btn">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</div>
<div class="col-md-12">
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th style="width: 20px">选择</th>
<th style="width: 20px">#</th>
<th style="width: 80px">课程名称</th>
<th style="width: 80px">学期</th>
<th style="width: 60px">校区</th>
<th style="width: 80px">学费</th>
<th style="width: 90px">说明</th>
<th style="width: 120px">开班日期</th>
<th style="width: 60px">结业日期</th>
<th style="width: 60px">老师</th>
<th style="width: 60px">班级类型</th>
<th style="width: 20px!important;">操作</th>
</tr>
</thead>
<tbody>
{% for class in class_list %}
<tr>
<td><input type="checkbox" name="cid" value="{{ class.id }}"></td>
<td>{{ forloop.counter }}</td>
<td>{{ class.course }}</td>
<td>{{ class.semester }}</td>
<td>{{ class.campuses }}</td>
<td>{{ class.price }}</td>
<td>{{ class.memo }}</td>
<td>{{ class.start_date }}</td>
<td>{{ class.graduate_date }}</td>
<td>{{ class.teachers }}</td>
<td>{{ class.get_class_type_display }}</td>
<td><a href="{% url 'edit_class' class.id %}"><i class="fa fa-edit" aria-hidden="true"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div>
{{ page_html|safe }}
</div>
</div>
</div>
</form>
{% endblock %}
(4)base.html中:
<li><a href="{% url 'class_list' %}">班级列表</a></li>
(5)op_class.html:编辑
{% extends 'base.html' %}
{% block page-main %}
<div>
<h2 class="text-center">{% if edit_id %}编辑班级信息{% else %}添加班级信息{% endif %}</h2>
<form class="form-horizontal" action="" method="post" novalidate>
{% csrf_token %}
{% for field in form_obj %}
<div class="form-group {% if field.errors.0 %}has-error{% endif %}">
<label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{{ field }}
<span class="help-block">{{ field.errors.0 }}</span>
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">提交</button>
</div>
</div>
</form>
</div>
{% endblock %}
浙公网安备 33010602011771号