Django - CRM
表结构设计
注册
from django import forms from crm import models from django.core.exceptions import ValidationError # 注册form class RegForm(forms.ModelForm): password = forms.CharField( label='密码', widget=forms.widgets.PasswordInput(), min_length=6, error_messages={'min_length': '最小长度为6'} ) re_password = forms.CharField( label='确认密码', widget=forms.widgets.PasswordInput() ) class Meta: model = models.UserProfile # fields = '__all__' # 所有字段 fields = ['username', 'password', 're_password', 'name', 'department'] # 指定字段 # exclude = [''] widgets = { 'username': forms.widgets.EmailInput(attrs={'class': 'form-control'}), 'password': forms.widgets.PasswordInput, } labels = { 'username': '用户名', 'password': '密码', 'name': '姓名', 'department': '部门', } error_messages = { 'password': { 'required': '密码不能为空', } } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for filed in self.fields.values(): filed.widget.attrs.update({'class': 'form-control'}) def clean(self): pwd = self.cleaned_data.get('password') re_pwd = self.cleaned_data.get('re_password') if pwd == re_pwd: return self.cleaned_data self.add_error('re_password', '两次密码不一致') raise ValidationError('两次密码不一致')
# 注册 def reg(request): form_obj = RegForm() if request.method == 'POST': form_obj = RegForm(request.POST) if form_obj.is_valid(): # 创建新用户 # 方法一 # form_obj.cleaned_data.pop('re_password') # models.UserProfile.objects.create_user(**form_obj.cleaned_data) # 方法二 obj = form_obj.save() obj.set_password(obj.password) obj.save() return redirect('/login/') return render(request, 'reg.html', {'form_obj': form_obj})
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-Type" charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.3.7/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'css/reg.css' %}"> </head> <body> <div class="container"> <div class="row"> <div class="col-sm-6 col-sm-offset-3"> <form class="form-horizontal" novalidate action="" method="post"> <h2>注册</h2> {% csrf_token %} <div class="form-group {% if form_obj.username.errors %}has-error{% endif %} "> <label for="{{ form_obj.username.id_for_label }}" class="col-sm-2 control-label"> {{ form_obj.username.label }}</label> <div class="col-sm-10"> {{ form_obj.username }} <span class="help-block"> {{ form_obj.username.errors.0 }} </span> </div> </div> <div class="form-group {% if form_obj.password.errors %}has-error{% endif %}"> <label for="{{ form_obj.password.id_for_label }}" class="col-sm-2 control-label"> {{ form_obj.password.label }}</label> <div class="col-sm-10"> {{ form_obj.password }} <span class="help-block"> {{ form_obj.password.errors.0 }} </span> </div> </div> <div class="form-group {% if form_obj.re_password.errors %}has-error{% endif %}"> <label for="{{ form_obj.re_password.id_for_label }}" class="col-sm-2 control-label"> {{ form_obj.re_password.label }}</label> <div class="col-sm-10"> {{ form_obj.re_password }} <span class="help-block"> {{ form_obj.re_password.errors.0 }} </span> </div> </div> <div class="form-group {% if form_obj.name.errors %}has-error{% endif %}"> <label for="{{ form_obj.name.id_for_label }}" class="col-sm-2 control-label"> {{ form_obj.name.label }}</label> <div class="col-sm-10"> {{ form_obj.name }} <span class="help-block"> {{ form_obj.name.errors.0 }} </span> </div> </div> <div class="form-group {% if form_obj.department.errors %}has-error{% endif %}"> <label for="{{ form_obj.department.id_for_label }}" class="col-sm-2 control-label"> {{ form_obj.department.label }}</label> <div class="col-sm-10"> {{ form_obj.department }} <span class="help-block"> {{ form_obj.department.errors.0 }} </span> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default">Sign in</button> </div> </div> </form> </div> </div> </div> <!-- /container --> <script src="{% static 'js/jQuery.js'%}"></script> <script> $('input').focus(function () { $(this).next().text('').parent().parent().removeClass('has-error') }) </script> </body> </html>
登录
def login(request): err_msg = '' if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') obj = auth.authenticate(request, username=username, password=password) if obj: auth.login(request, obj) return redirect(reverse('my_customer')) err_msg = '用户名或密码错误' return render(request, 'login.html', {'err_msg': err_msg})
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-Type" charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Title</title> <link rel="stylesheet" href="{% static 'css/reset.css' %}"> <link rel="stylesheet" href="{% static 'css/style.css' %}"> </head> <body> <div id="particles-js"> <div class="login"> <form action="" method="post"> {% csrf_token %} <div class="login-top"> 登录 </div> <div class="login-center clearfix"> <div class="login-center-img"><img src="{% static 'imgs/name.png' %}"></div> <div class="login-center-input"> <input type="text" name="username" value="admin" placeholder="请输入您的用户名" onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的用户名'"> <div class="login-center-input-text">用户名</div> </div> </div> <div class="login-center clearfix"> <div class="login-center-img"><img src="{% static 'imgs/password.png' %}"></div> <div class="login-center-input"> <input type="password" name="password" value="" placeholder="请输入您的密码" onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的密码'"> <div class="login-center-input-text">密码</div> </div> </div> <p style="color: red;text-align: center">{{ err_msg }}</p> <div style="text-align: center"> <button class="login-button">登录</button> </div> </form> </div> <div class="sk-rotating-plane"></div> <canvas class="particles-js-canvas-el" width="1343" height="202" style="width: 100%; height: 100%;"></canvas> </div> <script src="{% static 'js/particles.min.js' %}"></script> <script src="{% static 'js/app.js' %}"></script> <script type="text/javascript"> function hasClass(elem, cls) { cls = cls || ''; if (cls.replace(/\s/g, '').length == 0) return false; //当cls没有参数时,返回false return new RegExp(' ' + cls + ' ').test(' ' + elem.className + ' '); } function addClass(ele, cls) { if (!hasClass(ele, cls)) { ele.className = ele.className == '' ? cls : ele.className + ' ' + cls; } } function removeClass(ele, cls) { if (hasClass(ele, cls)) { var newClass = ' ' + ele.className.replace(/[\t\r\n]/g, '') + ' '; while (newClass.indexOf(' ' + cls + ' ') >= 0) { newClass = newClass.replace(' ' + cls + ' ', ' '); } ele.className = newClass.replace(/^\s+|\s+$/g, ''); } } document.querySelector(".login-button").onclick = function () { addClass(document.querySelector(".login"), "active") setTimeout(function () { addClass(document.querySelector(".sk-rotating-plane"), "active") document.querySelector(".login").style.display = "none" }, 800) setTimeout(function () { removeClass(document.querySelector(".login"), "active") removeClass(document.querySelector(".sk-rotating-plane"), "active") document.querySelector(".login").style.display = "block" alert("登录成功") }, 5000) } </script> </body> </html>
客户信息添加和展示
重点在models和html
from django import forms from crm import models from django.core.exceptions import ValidationError # 客户form class CustomerForm(forms.ModelForm): class Meta: model = models.Customer fields = '__all__' widgets = { 'course':forms.widgets.SelectMultiple } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for filed in self.fields.values(): filed.widget.attrs.update({'class': 'form-control'})
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-Type" charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" href="{% static 'imgs/layout/luffy-logo.png' %}"> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.3.7/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %}"> <link rel="stylesheet" href="{% static 'css/layout.css' %}"> <title>CRM管理系统</title> {% block css %} {% endblock %} </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <img src="{% static 'imgs/layout/logo.svg' %}" alt="" style="float: left;margin-top: 5px;margin-right: 5px"> <a class="navbar-brand" href="#">CRM管理系统</a> </div> <div id="navbar" class="navbar-collapse collapse"> <div class="navbar-right"> <img class="img-circle" height="45px" style="margin-top: 2.5px;margin-right: 5px" src="{% static 'imgs/layout/default.png' %}" alt="" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <ul class="dropdown-menu"> <li><a href="#">个人中心</a></li> <li><a href="#">修改密码</a></li> <li role="separator" class="divider"></li> <li><a href="#">注销</a></li> </ul> </div> <ul class="nav navbar-nav navbar-right"> <li><a href="#">任务 <i class="fa fa-bell-o" aria-hidden="true"> </i><span class="badge">4</span></a> </li> <li><a href="#">通知 <i class="fa fa-envelope-o" aria-hidden="true"> </i><span class="badge">2</span></a> </li> <li><a href="#">消息 <i class="fa fa-commenting-o" aria-hidden="true"> </i><span class="badge">6</span></a></li> </ul> </div> </div> </nav> <div class="container-fluid"> <div class="row"> <div class="col-sm-3 col-md-2 sidebar"> <ul class="nav nav-sidebar"> <li class="active"><a href="{% url 'customer' %}">客户列表</a></li> <li><a href="#">Reports</a></li> <li><a href="#">Analytics</a></li> <li><a href="#">Export</a></li> </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> {% block content %} {% endblock %} </div> </div> </div> <script src="{% static 'js/jQuery.js' %}"></script> <script src="{% static 'plugins/bootstrap-3.3.7/js/bootstrap.js' %}"></script> {% block js %} {% endblock %} </body> </html>
from django.contrib import admin from django.urls import path,include from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('crm/',include('app01.urls')), ]
def cutomer_list(request): cutomer_obj = Customer.objects.all() return render(request, 'my_crm/customer_list.html', {'cutomer_obj': cutomer_obj})
from django.db import models from django.contrib import auth from django.core.exceptions import PermissionDenied from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, User from django.utils.translation import ugettext_lazy as _ from multiselectfield import MultiSelectField from django.utils.safestring import mark_safe course_choices = (('LinuxL', 'Linux中高级'), ('PythonFullStack', 'Python高级全栈开发'),) class_type_choices = (('fulltime', '脱产班',), ('online', '网络班'), ('weekend', '周末班',),) source_type = (('qq', "qq群"), ('referral', "内部转介绍"), ('website', "官方网站"), ('baidu_ads', "百度推广"), ('office_direct', "直接上门"), ('WoM', "口碑"), ('public_class', "公开课"), ('website_luffy', "路飞官网"), ('others', "其它"),) enroll_status_choices = (('signed', "已报名"), ('unregistered', "未报名"), ('studying', '学习中'), ('paid_in_full', "学费已交齐")) seek_status_choices = (('A', '近期无报名计划'), ('B', '1个月内报名'), ('C', '2周内报名'), ('D', '1周内报名'), ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '无效'),) pay_type_choices = (('deposit', "订金/报名费"), ('tuition', "学费"), ('transfer', "转班"), ('dropout', "退学"), ('refund', "退款"),) attendance_choices = (('checked', "已签到"), ('vacate', "请假"), ('late', "迟到"), ('absence', "缺勤"), ('leave_early', "早退"),) score_choices = ((100, 'A+'), (90, 'A'), (85, 'B+'), (80, 'B'), (70, 'B-'), (60, 'C+'), (50, 'C'), (40, 'C-'), (0, ' D'), (-1, 'N/A'), (-100, 'COPY'), (-1000, 'FAIL'),) class Customer(models.Model): """ 客户表 """ qq = models.CharField('QQ', max_length=64, unique=True, help_text='QQ号必须唯一') qq_name = models.CharField('QQ昵称', max_length=64, blank=True, null=True) name = models.CharField('姓名', max_length=32, blank=True, null=True, help_text='学员报名后,请改为真实姓名') sex_type = (('male', '男'), ('female', '女')) sex = models.CharField("性别", choices=sex_type, max_length=16, default='male', blank=True, null=True) birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True) phone = models.BigIntegerField('手机号', blank=True, null=True) source = models.CharField('客户来源', max_length=64, choices=source_type, default='qq') introduce_from = models.ForeignKey('self', verbose_name="转介绍自学员", blank=True, null=True) course = MultiSelectField("咨询课程", choices=course_choices) class_type = models.CharField("班级类型", max_length=64, choices=class_type_choices, default='fulltime') customer_note = models.TextField("客户备注", blank=True, null=True, ) status = models.CharField("状态", choices=enroll_status_choices, max_length=64, default="unregistered", help_text="选择客户此时的状态") network_consult_note = models.TextField(blank=True, null=True, verbose_name='网络咨询师咨询内容') date = models.DateTimeField("咨询日期", auto_now_add=True) last_consult_date = models.DateField("最后跟进日期", auto_now_add=True) next_date = models.DateField("预计再次跟进时间", blank=True, null=True) network_consultant = models.ForeignKey('UserProfile', blank=True, null=True, verbose_name='咨询师', related_name='network_consultant') consultant = models.ForeignKey('UserProfile', verbose_name="销售", related_name='customers', blank=True, null=True, ) class_list = models.ManyToManyField('ClassList', verbose_name="已报班级", ) def show_status(self): color_dict = { "signed": 'green', "unregistered": 'red', "studying": 'pink', "paid_in_full": 'blue', } return mark_safe('<span style="background-color: {};color: white;padding: 4px">{}</span>'.format(color_dict[self.status],self.get_status_display())) def show_classes(self): return ' | '.join([str(i) for i in self.class_list.all()]) class Meta: verbose_name = '客户列表' verbose_name_plural = '客户列表' class Campuses(models.Model): """ 校区表 """ name = models.CharField(verbose_name='校区', max_length=64) address = models.CharField(verbose_name='详细地址', max_length=512, blank=True, null=True) def __str__(self): return self.name class ContractTemplate(models.Model): """ 合同模板表 """ name = models.CharField("合同名称", max_length=128, unique=True) content = models.TextField("合同内容") date = models.DateField(auto_now=True) class ClassList(models.Model): """ 班级表 """ course = models.CharField("课程名称", max_length=64, choices=course_choices) semester = models.IntegerField("学期") campuses = models.ForeignKey('Campuses', verbose_name="校区") price = models.IntegerField("学费", default=10000) memo = models.CharField('说明', blank=True, null=True, max_length=100) start_date = models.DateField("开班日期") graduate_date = models.DateField("结业日期", blank=True, null=True) contract = models.ForeignKey('ContractTemplate', verbose_name="选择合同模版", blank=True, null=True) teachers = models.ManyToManyField('UserProfile', verbose_name="老师") class_type = models.CharField(choices=class_type_choices, max_length=64, verbose_name='班额及类型', blank=True, null=True) class Meta: unique_together = ("course", "semester", 'campuses') def __str__(self): return "{}{}({})".format(self.get_course_display(), self.semester, self.campuses)
{% extends 'layout.html' %} {% block css %} <style> th, tr { text-align: center; } </style> {% endblock %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">Panel heading</div> <table class="table table-condensed table-hover table-bordered"> <thead> <tr> <th>序号</th> <th>QQ</th> <th>QQ昵称</th> <th>姓名</th> <th>性别</th> <th>手机号</th> <th>客户来源</th> <th>咨询课程</th> <th>班级类型</th> <th>状态</th> <th>咨询日期</th> <th>最后跟进日期</th> <th>销售</th> <th>已报班级</th> </tr> </thead> <tbody> {% for customer in all_customer %} <tr> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> <td>{{ customer.qq_name|default:'暂无' }}</td> <td>{{ customer.name|default:'暂无' }}</td> <td>{{ customer.get_sex_display }}</td> <td>{{ customer.phone|default:'暂无' }}</td> <td>{{ customer.get_source_display }}</td> <td>{{ customer.course }}</td> <td>{{ customer.get_class_type_display }}</td> <td> {{ customer.show_status }} </td> <td>{{ customer.date }}</td> <td>{{ customer.last_consult_date }}</td> <td>{{ customer.consultant }}</td> <td>{{ customer.show_classes }}</td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %}
总结:
客户信息展示 1. 母版和继承 {% extends 'layout'%} {% load static%} {% static '文件路径' %} block css js content 2. 内容显示 一 普通字段 {{ customer.qq }} choices字段 {{ customer.get_class_type_display }} 二 多对多 自定义方法,返回字符串 def show_classes(self): return ' | '.join([str(i) for i in self.class_list.all()]) 三 显示状态 form django.utils.safestring import mark_safe mark_safe
分页第一版
tests = [{'name': "xushuo{}".format(i), 'pwd': 'changjian{}'.format(i)} for i in range(1, 302)] def user_test(request): try: current_page = int(request.GET.get('page', 1)) if current_page <= 0: current_page = 1 except Exception as e: current_page = 1 # 总数据量 all_count = len(tests) # 每页的显示数据 per_num = 10 # 总页码数 total_num, more = divmod(all_count, per_num) if more: total_num += 1 # 切片 start = (current_page - 1) * per_num end = current_page * per_num return render(request, 'user_test.html', {'data': tests[start:end], 'total_num': range(1, total_num + 1)}) Views
{% extends 'my_crm/layout.html' %} {% block content %} <table class="table-bordered table-hover"> <thead> <tr> <th>序号</th> <th>账号</th> <th>密码</th> </tr> </thead> <tbody> {% for i in data %} <tr> <td>{{ forloop.counter }}</td> <td>{{ i.name }}</td> <td>{{ i.pwd }}</td> </tr> {% endfor %} </tbody> </table> <nav aria-label="Page navigation"> <ul class="pagination"> <li> <a href="#" aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> {% for num in total_num %} <li><a href="/user_test/?page={{ num }}">{{ num }}</a></li> {% endfor %} <li> <a href="#" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </nav> {% endblock %} html
分页第二版展示指定个数的页码
users = [{'name': 'alex{}'.format(i), 'pwd': 'alexdsb{}'.format(i)} for i in range(1, 302)] # def user_list(request): # # 当前页码 # try: # current_page = int(request.GET.get('page', 1)) # if current_page <= 0: # current_page = 1 # except Exception as e: # current_page = 1 # # 最多显示的页码数 # max_show = 11 # half_show = max_show // 2 # # # 每页显示的数据条数 # per_num = 10 # # 总数据量 # all_count = len(users) # # # 总页码数 # total_num, more = divmod(all_count, per_num) # if more: # total_num += 1 # # # 总页码数小于最大显示数:显示总页码数 # if total_num <= max_show: # page_start = 1 # page_end = total_num # else: # # 总页码数大于最大显示数:最多显示11个 # if current_page <= half_show: # page_start = 1 # page_end = max_show # elif current_page + half_show >= total_num: # page_end = total_num # page_start = total_num - max_show + 1 # else: # page_start = current_page - half_show # page_end = current_page + half_show """ # 1 0 10 # 2 10 20 # """ # # 切片的起始值 # start = (current_page - 1) * per_num # # 切片的终止值 # end = current_page * per_num # # return render(request, 'user_list.html', # { # "data": users[start:end], # # 'total_num': range(page_start, page_end + 1) # # })
分页第三版选中当前页,增加首页,尾页
from django.utils.safestring import mark_safe users = [{'name': 'alex{}'.format(i), 'pwd': 'alexdsb{}'.format(i)} for i in range(1, 302)] # def user_list(request): # # 当前页码 # try: # current_page = int(request.GET.get('page', 1)) # if current_page <= 0: # current_page = 1 # except Exception as e: # current_page = 1 # # 最多显示的页码数 # max_show = 11 # half_show = max_show // 2 # # # 每页显示的数据条数 # per_num = 10 # # 总数据量 # all_count = len(users) # # # 总页码数 # total_num, more = divmod(all_count, per_num) # if more: # total_num += 1 # # # 总页码数小于最大显示数:显示总页码数 # if total_num <= max_show: # page_start = 1 # page_end = total_num # else: # # 总页码数大于最大显示数:最多显示11个 # if current_page <= half_show: # page_start = 1 # page_end = max_show # elif current_page + half_show >= total_num: # page_end = total_num # page_start = total_num - max_show + 1 # else: # page_start = current_page - half_show # page_end = current_page + half_show # # 存放li标签的列表 # html_list = [] # # first_li = '<li><a href="/user_list/?page=1">首页</a></li>' # html_list.append(first_li) # # if current_page == 1: # prev_li = '<li class="disabled"><a><<</a></li>' # else: # prev_li = '<li><a href="/user_list/?page={0}"><<</a></li>'.format(current_page - 1) # html_list.append(prev_li) # # for num in range(page_start, page_end + 1): # if current_page == num: # li_html = '<li class="active"><a href="/user_list/?page={0}">{0}</a></li>'.format(num) # else: # li_html = '<li><a href="/user_list/?page={0}">{0}</a></li>'.format(num) # html_list.append(li_html) # # if current_page == total_num: # next_li = '<li class="disabled"><a>>></a></li>' # else: # next_li = '<li><a href="/user_list/?page={0}">>></a></li>'.format(current_page + 1) # # html_list.append(next_li) # # last_li = '<li><a href="/user_list/?page={}">尾页</a></li>'.format(total_num) # html_list.append(last_li) # # html_str = mark_safe(''.join(html_list)) # # """ # 1 0 10 # 2 10 20 # """ # # 切片的起始值 # start = (current_page - 1) * per_num # # 切片的终止值 # end = current_page * per_num # # return render(request, 'user_list.html', # { # "data": users[start:end], # # 'total_num': range(page_start, page_end + 1) # 'html_str': html_str # }) Views
{% extends 'layout.html' %} {% block content %} <table class="table table-bordered"> <thead> <tr> <th>序号</th> <th>用户名</th> <th>密码</th> </tr> </thead> <tbody> {% for user in data %} <tr> <td>{{ forloop.counter }}</td> <td>{{ user.name }}</td> <td>{{ user.pwd }}</td> </tr> {% endfor %} </tbody> </table> <nav aria-label="Page navigation"> <ul class="pagination"> {{ html_str }} </ul> </nav> {% endblock %} html
分页封装成类
""" 分页器 """ from django.utils.safestring import mark_safe class Pagination: def __init__(self, request, all_count, per_num=10, max_show=11): # 基本的URL self.base_url = request.path_info # 当前页码 try: self.current_page = int(request.GET.get('page', 1)) if self.current_page <= 0: self.current_page = 1 except Exception as e: self.current_page = 1 # 最多显示的页码数 self.max_show = max_show half_show = max_show // 2 # 每页显示的数据条数 self.per_num = per_num # 总数据量 self.all_count = all_count # 总页码数 self.total_num, more = divmod(all_count, per_num) if more: self.total_num += 1 # 总页码数小于最大显示数:显示总页码数 if self.total_num <= max_show: self.page_start = 1 self.page_end = self.total_num else: # 总页码数大于最大显示数:最多显示11个 if self.current_page <= half_show: self.page_start = 1 self.page_end = max_show elif self.current_page + half_show >= self.total_num: self.page_end = self.total_num self.page_start = self.total_num - max_show + 1 else: self.page_start = self.current_page - half_show self.page_end = self.current_page + half_show @property def start(self): return (self.current_page - 1) * self.per_num @property def end(self): return self.current_page * self.per_num @property def show_li(self): # 存放li标签的列表 html_list = [] first_li = '<li><a href="{}?page=1">首页</a></li>'.format(self.base_url) html_list.append(first_li) if self.current_page == 1: prev_li = '<li class="disabled"><a><<</a></li>' else: prev_li = '<li><a href="{1}?page={0}"><<</a></li>'.format(self.current_page - 1, self.base_url) html_list.append(prev_li) for num in range(self.page_start, self.page_end + 1): if self.current_page == num: li_html = '<li class="active"><a href="{1}?page={0}">{0}</a></li>'.format(num, self.base_url) else: li_html = '<li><a href="{1}?page={0}">{0}</a></li>'.format(num, self.base_url) html_list.append(li_html) if self.current_page == self.total_num: next_li = '<li class="disabled"><a>>></a></li>' else: next_li = '<li><a href="{1}?page={0}">>></a></li>'.format(self.current_page + 1, self.base_url) html_list.append(next_li) last_li = '<li><a href="{1}?page={0}">尾页</a></li>'.format(self.total_num, self.base_url) html_list.append(last_li) return mark_safe(''.join(html_list)) pagination.py
<div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> html
from utils.pagination import Pagination # 导入自定义的类 # 展示客户列表 def customer_list(request): all_customer = models.Customer.objects.all() page = Pagination(request, all_customer.count()) return render(request, 'crm/customer_list.html', {"all_customer": all_customer[page.start:page.end], 'pagination':page.show_li}) Views
新增客户
# 增加客户 def add_customer(request): # 实例化一个空的form对象 form_obj = CustomerForm() if request.method == 'POST': # 实例化一个带提交数据的form对象 form_obj = CustomerForm(request.POST) # 对提交数据进行校验 if form_obj.is_valid(): # 创建对象 form_obj.save() return redirect(reverse('customer')) return render(request, 'crm/add_customer.html', {"form_obj": form_obj})
{% extends 'layout.html' %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">添加客户</div> <div class="panel-body"> <div class="col-sm-8 col-sm-offset-2 "> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group row {% if field.errors %}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-primary">提交</button> </div> </div> </form> </div> </div> </div> {% endblock %}
编辑客户
# # 编辑客户 # url(r'customer/edit/(\d+)', views.edit_customer, name='edit_customer')
# 编辑客户 def edit_customer(request, edit_id): # 根据ID查出所需要编辑的客户对象 obj = models.Customer.objects.filter(id=edit_id).first() form_obj = CustomerForm(instance=obj) if request.method == 'POST': # 将提交的数据和要修改的实例交给form对象 form_obj = CustomerForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('customer')) return render(request, 'crm/edit_customer.html', {"form_obj": form_obj})
{% extends 'layout.html' %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">编辑客户</div> <div class="panel-body"> <div class="col-sm-8 col-sm-offset-2 "> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group row {% if field.errors %}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-primary">提交</button> </div> </div> </form> </div> </div> </div> {% endblock %}
新增,编辑二合一
# 增加客户 url(r'customer/add/', views.customer, name='add_customer'), # 编辑客户 url(r'customer/edit/(\d+)', views.customer, name='edit_customer')
# 新增和编辑客户 def customer(request, edit_id=None): obj = models.Customer.objects.filter(id=edit_id).first() form_obj = CustomerForm(instance=obj) if request.method == 'POST': form_obj = CustomerForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('customer')) return render(request, 'crm/customer.html', {"form_obj": form_obj, "edit_id": edit_id})
{% extends 'layout.html' %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> {% if edit_id %} 编辑客户 {% else %} 新增客户 {% endif %} </div> <div class="panel-body"> <div class="col-sm-8 col-sm-offset-2 "> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group row {% if field.errors %}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-primary">提交</button> </div> </div> </form> </div> </div> </div> {% endblock %}
公户和私户
# 公户 # url(r'customer_list', views.customer_list, name='customer'), # 私户 # url(r'my_customer', views.customer_list, name='my_customer'),
def customer_list(request): print(request.POST) if request.path_info == reverse('customer'): # 公户 all_customer = models.Customer.objects.filter(consultant__isnull=True) else: # 私户 all_customer = models.Customer.objects.filter(consultant=request.user) page = Pagination(request, all_customer.count()) return render(request, 'crm/customer_list.html', {"all_customer": all_customer[page.start:page.end], 'pagination': page.show_li})
多选公私户转化(反射+CBV)
公户 url(r'customer_list/', views.CustomerList.as_view(), name='customer'), 私户 url(r'my_customer/', views.CustomerList.as_view(), name='my_customer'),
{% extends 'layout.html' %} {% block css %} <style> th, tr { text-align: center; } </style> {% endblock %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">Panel heading</div> <div class="panel-body"> <a href="{% url 'add_customer' %}" class="btn btn-primary btn-sm">添加</a> <form action="" method="post" class="form-inline"> {% csrf_token %} <select name="action" class="form-control" style="margin: 5px 0"> <option value="">请选择</option> <option value="multi_delte">删除</option> <option value="multi_apply">放入私户</option> <option value="multi_pub">放入公户</option> {# <option value=""></option>#} </select> <button class="btn btn-success btn-sm">提交</button> <table class="table table-condensed table-hover table-bordered"> <thead> <tr> <th>选择</th> <th>序号</th> <th>QQ</th> {# <th>QQ昵称</th>#} <th>姓名</th> <th>性别</th> {# <th>手机号</th>#} {# <th>客户来源</th>#} <th>咨询课程</th> <th>班级类型</th> <th>状态</th> {# <th>咨询日期</th>#} <th>最后跟进日期</th> <th>销售</th> <th>已报班级</th> <th>操作</th> </tr> </thead> <tbody> {% for customer in all_customer %} <tr> <td><input type="checkbox" name="id" value="{{ customer.id }}"></td> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> {# <td>{{ customer.qq_name|default:'暂无' }}</td>#} <td>{{ customer.name|default:'暂无' }}</td> <td>{{ customer.get_sex_display }}</td> {# <td>{{ customer.phone|default:'暂无' }}</td>#} {# <td>{{ customer.get_source_display }}</td>#} <td>{{ customer.course }}</td> <td>{{ customer.get_class_type_display }}</td> <td> {{ customer.show_status }} </td> {# <td>{{ customer.date }}</td>#} <td>{{ customer.last_consult_date }}</td> <td>{{ customer.consultant }}</td> <td>{{ customer.show_classes }}</td> <td><a href="{% url 'edit_customer' customer.id %}"><i class="fa fa-edit fa-fw"></i></a></td> </tr> {% endfor %} </tbody> </table> </form> <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> </div> </div> {% endblock %}
from django.views import View # 展示客户列表CBV class CustomerList(View): def get(self, request): q = self.get_search_contion(['qq', 'name', 'last_consult_date']) if request.path_info == reverse('customer'): all_customer = models.Customer.objects.filter(q, consultant__isnull=True) else: all_customer = models.Customer.objects.filter(q, consultant=request.user) page = Pagination(request, all_customer.count(),query_params, 2) return render(request, 'crm/customer_list.html', {"all_customer": all_customer[page.start:page.end], 'pagination': page.show_li}) def post(self, request): # 处理post提交的action的动作 print(request.POST) action = request.POST.get('action') if not hasattr(self, action): return HttpResponse('非法操作') ret = getattr(self, action)() if ret: return ret return self.get(request) def multi_apply(self): # 公户变私户 ids = self.request.POST.getlist('id') # 方法一 # models.Customer.objects.filter(id__in=ids).update(consultant=self.request.user) # 方法二 self.request.user.customers.add(*models.Customer.objects.filter(id__in=ids)) # return HttpResponse('申请成功') def multi_pub(self): # 私户变公户 ids = self.request.POST.getlist('id') # 方法一 # models.Customer.objects.filter(id__in=ids).update(consultant=None) # 方法二 self.request.user.customers.remove(*models.Customer.objects.filter(id__in=ids))
模糊查询 Q
{% extends 'my_crm/layout.html' %} {% block content %} <div class="panel panel-default"> <div class="panel-heading">Panel heading</div> <a href="{% url 'add_cutomer' %}" class="btn btn-primary btn-sm">添加用户</a> {# 搜索框开始#} <div> <form action="" class="form-inline pull-right"> {# 这里用get请求#} <input type="text" name="query" class="form-control"> <button class="btn btn-sm btn-primary">搜索 <i class="fa fa-search"></i></button> </form> </div> {# 搜索框结束#} {# 展示客户表格开始#} <form action="" method="post" class="form-inline"> {% csrf_token %} <select name="action" class="form-control" style="margin: 5px 0"> <option value="">请选择</option> <option value="multi_delte">删除</option> <option value="multi_apply">放入私户</option> <option value="multi_pub">放入公户</option> {# <option value=""></option>#} </select> <button class="btn btn-success btn-sm">提交</button> <table class="table table-bordered table-hover table-condensed" > <thead> <tr> <th>选择</th> <th>序号</th> <th>QQ</th> {# <th>昵称</th>#} <th>姓名</th> <th>性别</th> <th>手机</th> {# <th>客户来源</th>#} <th>咨询课程</th> {# <th>咨询日期</th>#} <th>状态</th> <th>最后跟进日期</th> <th>销售</th> <th>已报班级</th> <th>操作</th> </tr> </thead> <tbody> {% for cutomer in all_customer %} <tr> <td><input type="checkbox" name="id" value="{{ cutomer.id }}"></td> <td>{{ forloop.counter }}</td> <td>{{ cutomer.qq }}</td> {# <td>{{ cutomer.qq_name|default:"暂无" }}</td>#} <td>{{ cutomer.name|default:'暂无' }}</td> <td>{{ cutomer.get_sex_display }}</td> <td>{{ cutomer.phone|default:'暂无' }}</td> {# <td>{{ cutomer.get_source_display }}</td>#} <td>{{ cutomer.course }}</td> {# <td>{{ cutomer.date }}</td>#} <td>{{ cutomer.show_status }}</td> <td>{{ cutomer.last_consult_date }}</td> <td>{{ cutomer.consultant }}</td> <td>{{ cutomer.show_classes }}</td> <td><a href="{% url 'edit_cutomer' cutomer.id %}" class="fa fa-edit"></a></td> </tr> {% endfor %} </tbody> </table> {# 展示客户表格结束#} </form> {# 分页器#} <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> {# 分页器结束#} </div> {% endblock %}
class CutomerList(View): def get(self, request): query = request.GET.get('query','') if request.path_info == reverse('customer'): # 写法一 all_customer = models.Customer.objects.filter(Q(qq__contains=query) | Q(qq_name__contains=query), consultant__isnull=True) # 写法二 (重点) all_customer = models.Customer.objects.filter(Q(('qq__contains',query)) | Q(('qq_name__contains',query)), consultant__isnull=True) else: # 写法一 all_customer = models.Customer.objects.filter(Q(qq__contains=query) | Q(qq_name__contains=query),consultant=request.user) # 写法二 (重点) all_customer = models.Customer.objects.filter(Q(('qq__contains',query)) | Q(('qq_name__contains',query)),consultant=request.user) page = Pagination(request, all_customer.count()) return render(request, 'my_crm/customer_list.html', {"all_customer": all_customer[page.start:page.end], 'pagination': page.show_li})
写法二(推荐)
class CutomerList(View): def get(self, request): q = self.get_search_contion(['qq','name','last_consult_date']) if request.path_info == reverse('customer'): # 写法一 all_customer = models.Customer.objects.filter(q,consultant__isnull=True) else: # 写法一 all_customer = models.Customer.objects.filter(q,consultant=request.user) page = Pagination(request, all_customer.count()) return render(request, 'my_crm/customer_list.html', {"all_customer": all_customer[page.start:page.end], 'pagination': page.show_li}) def post(self, request): atcion = request.POST.get('action') if not hasattr(self, atcion): return HttpResponse('非法操作') ret = getattr(self, atcion)() if ret: return ret return self.get(request) def multi_apply(self): # 公户变私户 ids = self.request.POST.getlist('id') models.Customer.objects.filter(id__in=ids).update(consultant=self.request.user) return HttpResponse('申请成功') def multi_pub(self): # 私户变公户 ids = self.request.POST.getlist('id') # 方法一 # models.Customer.objects.filter(id__in=ids).update(consultant=None) # 方法二 self.request.user.customers.remove(*models.Customer.objects.filter(id__in=ids)) def get_search_contion(self,query_list): query = self.request.GET.get('query', '') q = Q() q.connector = 'OR' for i in query_list: q.children.append(Q(('{}__contains'.format(i), query))) return q # Q( Q(qq__contains=query) | Q(name__contains=query) )
分页保留搜索条件
""" 分页器 """ from django.utils.safestring import mark_safe from django.http import QueryDict class Pagination: def __init__(self, request, all_count, query_params=QueryDict(), per_num=10, max_show=11): # 基本的URL self.base_url = request.path_info # 查询条件 self.query_params = query_params # _mutable改为True就可以修改 self.query_params._mutable = True # 当前页码 try: self.current_page = int(request.GET.get('page', 1)) if self.current_page <= 0: self.current_page = 1 except Exception as e: self.current_page = 1 # 最多显示的页码数 self.max_show = max_show half_show = max_show // 2 # 每页显示的数据条数 self.per_num = per_num # 总数据量 self.all_count = all_count # 总页码数 self.total_num, more = divmod(all_count, per_num) if more: self.total_num += 1 # 总页码数小于最大显示数:显示总页码数 if self.total_num <= max_show: self.page_start = 1 self.page_end = self.total_num else: # 总页码数大于最大显示数:最多显示11个 if self.current_page <= half_show: self.page_start = 1 self.page_end = max_show elif self.current_page + half_show >= self.total_num: self.page_end = self.total_num self.page_start = self.total_num - max_show + 1 else: self.page_start = self.current_page - half_show self.page_end = self.current_page + half_show @property def start(self): return (self.current_page - 1) * self.per_num @property def end(self): return self.current_page * self.per_num @property def show_li(self): # 存放li标签的列表 html_list = [] self.query_params['page'] = 1 # query=alex&page=1 first_li = '<li><a href="{}?{}">首页</a></li>'.format(self.base_url, self.query_params.urlencode()) html_list.append(first_li) if self.current_page == 1: prev_li = '<li class="disabled"><a><<</a></li>' else: self.query_params['page'] = self.current_page - 1 prev_li = '<li><a href="{0}?{1}"><<</a></li>'.format(self.base_url, self.query_params.urlencode()) html_list.append(prev_li) for num in range(self.page_start, self.page_end + 1): self.query_params['page'] = num if self.current_page == num: li_html = '<li class="active"><a href="{0}?{1}">{2}</a></li>'.format(self.base_url, self.query_params.urlencode(), num) else: li_html = '<li><a href="{0}?{1}">{2}</a></li>'.format(self.base_url, self.query_params.urlencode(), num) html_list.append(li_html) if self.current_page == self.total_num: next_li = '<li class="disabled"><a>>></a></li>' else: self.query_params['page'] =self.current_page + 1 next_li = '<li><a href="{0}?{1}">>></a></li>'.format(self.base_url,self.query_params.urlencode()) html_list.append(next_li) self.query_params['page'] = self.total_num last_li = '<li><a href="{0}?{1}">尾页</a></li>'.format(self.base_url,self.query_params.urlencode()) html_list.append(last_li) return mark_safe(''.join(html_list))
from django.shortcuts import render, redirect, reverse, HttpResponse from django.contrib import auth from crm.forms import RegForm, CustomerForm from crm import models from django.utils.safestring import mark_safe from utils.pagination import Pagination from django.views import View from django.db.models import Q from django.http import QueryDict import copy class CustomerList(View): def get(self, request): q = self.get_search_contion(['qq', 'name', 'last_consult_date']) if request.path_info == reverse('customer'): all_customer = models.Customer.objects.filter(q, consultant__isnull=True) else: all_customer = models.Customer.objects.filter(q, consultant=request.user) # 下面对request.GET值进行修改,所以这里要做深拷贝 # query_params = copy.deepcopy(request.GET) # <QueryDict: {'query': ['alex']}> query_params = request.GET.copy() # <QueryDict: {'query': ['alex']}> # query=alex # print(request.GET.urlencode()) # query_params['page'] = 1 # <QueryDict: {'query': ['alex'],'page': ['1']}> # print(request.GET.urlencode()) # query=alex&page=1 page = Pagination(request, all_customer.count(),query_params, 2) return render(request, 'crm/customer_list.html', {"all_customer": all_customer[page.start:page.end], 'pagination': page.show_li}) def post(self, request): # 处理post提交的action的动作 print(request.POST) action = request.POST.get('action') if not hasattr(self, action): return HttpResponse('非法操作') ret = getattr(self, action)() if ret: return ret return self.get(request) # 处理完在走一遍get()方法 def multi_apply(self): # 公户变私户 ids = self.request.POST.getlist('id') # 方法一 # models.Customer.objects.filter(id__in=ids).update(consultant=self.request.user) # 方法二 self.request.user.customers.add(*models.Customer.objects.filter(id__in=ids)) # return HttpResponse('申请成功') def multi_pub(self): # 私户变公户 ids = self.request.POST.getlist('id') # 方法一 # models.Customer.objects.filter(id__in=ids).update(consultant=None) # 方法二 self.request.user.customers.remove(*models.Customer.objects.filter(id__in=ids)) def get_search_contion(self, query_list): query = self.request.GET.get('query', '') q = Q() q.connector = 'OR' for i in query_list: q.children.append(Q(('{}__contains'.format(i), query))) return q # Q( Q(qq__contains=query) | Q(name__contains=query) )
总结:
1. 增加客户 2. 编辑客户 form_obj = CustomerForm(instance=obj) form_obj带着原有的数据,根据数据生成input的值 form_obj = CustomerForm(request.POST,instance=obj) 将提交的数据和要修改的实例交给form对象 form_obj.save() 对要修改的实例进行修改 3. 公户变私户 CBV self.request 反射 orm操作: models.Customer.objects.filter(id__in=ids).update(consultant=self.request.user) self.request.user.customers.add(*models.Customer.objects.filter(id__in=ids)) 4. 私户变公户 orm操作: models.Customer.objects.filter(id__in=ids).update(consultant=None) self.request.user.customers.remove(*models.Customer.objects.filter(id__in=ids)) 5. 模糊查询 all_customer = models.Customer.objects.filter(Q(qq__contains=query) | Q(name__contains=query), consultant__isnull=True) all_customer = models.Customer.objects.filter(Q(('qq__contains',query)) | Q(('name__contains',query)), consultant__isnull=True) def get_search_contion(self,query_list): query = self.request.GET.get('query', '') q = Q() q.connector = 'OR' for i in query_list: q.children.append(Q(('{}__contains'.format(i), query))) return q 6. 保留搜索条件 from django.http import QueryDict print('query',request.GET) # <QueryDict: {'query': ['alex']}> print(request.GET.urlencode()) query=alex
新增和编辑后跳转原页面
# 新增编辑二合一 def cutomer(request, edit_id=None): obj = models.Customer.objects.filter(id=edit_id).first() form_obj = CustomerForm(instance=obj) if request.method == 'POST': form_obj = CustomerForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() # 获取next next = request.GET.get('next') if next: return redirect(next) return redirect(reverse('customer')) return render(request, 'my_crm/cutomer.html', {"form_obj": form_obj, 'edit_id': edit_id}) class CutomerList(View): def get(self, request): q = self.get_search_contion(['qq', 'name', 'last_consult_date']) if request.path_info == reverse('customer'): # 写法一 all_customer = models.Customer.objects.filter(q, consultant__isnull=True) else: # 写法一 all_customer = models.Customer.objects.filter(q, consultant=request.user) # print('query', request.GET) # print(request.GET.urlencode()) query_params = copy.deepcopy(request.GET) query_params._mutable = True query_params['page'] = 1 print(request.GET.urlencode()) # 生成添加按钮 add_btn,query_params= self.get_add_btn() page = Pagination(request, all_customer.count(), query_params, 2) return render(request, 'my_crm/customer_list.html', {"all_customer": all_customer[page.start:page.end], 'pagination': page.show_li, 'add_btn': add_btn,'query_params':query_params}) def post(self, request): atcion = request.POST.get('action') if not hasattr(self, atcion): return HttpResponse('非法操作') ret = getattr(self, atcion)() if ret: return ret return self.get(request) def multi_apply(self): # 公户变私户 ids = self.request.POST.getlist('id') models.Customer.objects.filter(id__in=ids).update(consultant=self.request.user) return HttpResponse('申请成功') def multi_pub(self): # 私户变公户 ids = self.request.POST.getlist('id') # 方法一 # models.Customer.objects.filter(id__in=ids).update(consultant=None) # 方法二 self.request.user.customers.remove(*models.Customer.objects.filter(id__in=ids)) def get_search_contion(self, query_list): query = self.request.GET.get('query', '') q = Q() q.connector = 'OR' for i in query_list: q.children.append(Q(('{}__contains'.format(i), query))) return q # Q( Q(qq__contains=query) | Q(name__contains=query) ) def get_add_btn(self): # 获取添加按钮 url = self.request.get_full_path() qd = QueryDict() qd._mutable = True qd['next'] = url query_params = qd.urlencode() # add_btn = '<a href="{}?next={}" class="btn btn-primary btn-sm">添加用户</a>'.format(reverse('add_cutomer'), url) add_btn = '<a href="{}?{}" class="btn btn-primary btn-sm">添加用户</a>'.format(reverse('add_cutomer'),query_params) return mark_safe(add_btn),query_params
{% extends 'my_crm/layout.html' %} {% block content %} <div class="panel panel-default"> <div class="panel-heading">Panel heading</div> <a href="{% url 'add_cutomer' %}?{{ query_params }}" class="btn btn-primary btn-sm">添加用户</a> {# {{ add_btn }}#} {# 搜索框开始#} <div> <form action="" class="form-inline pull-right"> {# 这里用get请求#} <input type="text" name="query" class="form-control"> <button class="btn btn-sm btn-primary">搜索 <i class="fa fa-search"></i></button> </form> </div> {# 搜索框结束#} {# 展示客户表格开始#} <form action="" method="post" class="form-inline"> {% csrf_token %} <select name="action" class="form-control" style="margin: 5px 0"> <option value="">请选择</option> <option value="multi_delte">删除</option> <option value="multi_apply">放入私户</option> <option value="multi_pub">放入公户</option> {# <option value=""></option>#} </select> <button class="btn btn-success btn-sm">提交</button> <table class="table table-bordered table-hover table-condensed" > <thead> <tr> <th>选择</th> <th>序号</th> <th>QQ</th> {# <th>昵称</th>#} <th>姓名</th> <th>性别</th> <th>手机</th> {# <th>客户来源</th>#} <th>咨询课程</th> {# <th>咨询日期</th>#} <th>状态</th> <th>最后跟进日期</th> <th>销售</th> <th>已报班级</th> <th>操作</th> </tr> </thead> <tbody> {% for cutomer in all_customer %} <tr> <td><input type="checkbox" name="id" value="{{ cutomer.id }}"></td> <td>{{ forloop.counter }}</td> <td>{{ cutomer.qq }}</td> {# <td>{{ cutomer.qq_name|default:"暂无" }}</td>#} <td>{{ cutomer.name|default:'暂无' }}</td> <td>{{ cutomer.get_sex_display }}</td> <td>{{ cutomer.phone|default:'暂无' }}</td> {# <td>{{ cutomer.get_source_display }}</td>#} <td>{{ cutomer.course }}</td> {# <td>{{ cutomer.date }}</td>#} <td>{{ cutomer.show_status }}</td> <td>{{ cutomer.last_consult_date }}</td> <td>{{ cutomer.consultant }}</td> <td>{{ cutomer.show_classes }}</td> <td><a href="{% url 'edit_cutomer' cutomer.id %}?{{ query_params }}" class="fa fa-edit"></a></td> </tr> {% endfor %} </tbody> </table> {# 展示客户表格结束#} </form> {# 分页器#} <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> {# 分页器结束#} </div> {% endblock %}
跟进记录的展示,新增和编辑
# 展示跟进记录 url(r'consult_record_list/(\d+)', views.ConsultRecord.as_view(), name='consult_record'), # 添加跟进记录 url(r'consult_record/add/', views.consult_record, name='add_consult_record'), # 编辑跟进记录 url(r'consult_record/edit/(\d+)/', views.consult_record, name='edit_consult_record'),
# 跟进记录form class ConsultRecordForm(BaseForm): class Meta: model = models.ConsultRecord # fields = '__all__' exclude = ['delete_status'] # widgets = { # 'customer': forms.widgets.Select(choices=((1, 'xxxx'),)) # } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) customer_choice =[(i.id, i) for i in self.instance.consultant.customers.all()] customer_choice.insert(0,('','--------')) # 限制客户是当前销售的私户 self.fields['customer'].widget.choices = customer_choice # 限制跟进人是当前的用户(销售) self.fields['consultant'].widget.choices = [(self.instance.consultant.id,self.instance.consultant),]
{% extends 'layout.html' %} {% block css %} <style> th, tr { text-align: center; } </style> {% endblock %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">Panel heading</div> <div class="panel-body"> <a href="{% url 'add_consult_record' %}" class="btn btn-primary btn-sm">添加</a> <div> <form action="" class="form-inline pull-right"> <input type="text" name="query" class="form-control"> <button class="btn btn-sm btn-primary">搜索 <i class="fa fa-search"></i></button> </form> </div> <form action="" method="post" class="form-inline"> {% csrf_token %} <select name="action" class="form-control" style="margin: 5px 0"> <option value="">请选择</option> <option value="multi_delte">删除</option> </select> <button class="btn btn-success btn-sm">提交</button> <table class="table table-condensed table-hover table-bordered"> <thead> <tr> <th>选择</th> <th>序号</th> <th>客户</th> <th>跟进内容</th> <th>跟进状态</th> <th>跟进日期</th> <th>跟进人</th> <th>操作</th> </tr> </thead> <tbody> {% for record in all_consult_record %} <tr> <td><input type="checkbox" name="id" value="{{ record.id }}"></td> <td>{{ forloop.counter }}</td> <td>{{ record.customer }}</td> <td>{{ record.note }}</td> <td>{{ record.get_status_display }}</td> <td>{{ record.date }}</td> <td>{{ record.consultant }}</td> <td><a href="{% url 'edit_consult_record' record.id %}"><i class="fa fa-edit fa-fw"></i></a> </td> </tr> {% endfor %} </tbody> </table> </form> <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> </div> </div> {% endblock %}
{% extends 'layout.html' %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">添加跟进记录</div> <div class="panel-body"> <div class="col-sm-8 col-sm-offset-2 "> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group row {% if field.errors %}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-primary">提交</button> </div> </div> </form> </div> </div> </div> {% endblock %}
{% extends 'layout.html' %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">编辑跟进记录</div> <div class="panel-body"> <div class="col-sm-8 col-sm-offset-2 "> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group row {% if field.errors %}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-primary">提交</button> </div> </div> </form> </div> </div> </div> {% endblock %}
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-Type" charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" href="{% static 'imgs/layout/luffy-logo.png' %}"> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.3.7/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %}"> <link rel="stylesheet" href="{% static 'css/layout.css' %}"> <title>CRM管理系统</title> {% block css %} {% endblock %} </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <img src="{% static 'imgs/layout/logo.svg' %}" alt="" style="float: left;margin-top: 5px;margin-right: 5px"> <a class="navbar-brand" href="#">CRM管理系统</a> </div> <div id="navbar" class="navbar-collapse collapse"> <div class="navbar-right"> <img class="img-circle" height="45px" style="margin-top: 2.5px;margin-right: 5px" src="{% static 'imgs/layout/default.png' %}" alt="" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <ul class="dropdown-menu"> <li><a href="#">个人中心</a></li> <li><a href="#">修改密码</a></li> <li role="separator" class="divider"></li> <li><a href="#">注销</a></li> </ul> </div> <ul class="nav navbar-nav navbar-right"> <li><a href="#">任务 <i class="fa fa-bell-o" aria-hidden="true"> </i><span class="badge">4</span></a> </li> <li><a href="#">通知 <i class="fa fa-envelope-o" aria-hidden="true"> </i><span class="badge">2</span></a> </li> <li><a href="#">消息 <i class="fa fa-commenting-o" aria-hidden="true"> </i><span class="badge">6</span></a></li> </ul> </div> </div> </nav> <div class="container-fluid"> <div class="row"> <div class="col-sm-3 col-md-2 sidebar"> <ul class="nav nav-sidebar"> <li><a href="{% url 'customer' %}">客户列表</a></li> <li><a href="{% url 'my_customer' %}">我的客户</a></li> <li><a href="{% url 'consult_record' 0 %}">我的跟进记录</a></li> <li><a href="#">Reports</a></li> <li><a href="#">Analytics</a></li> <li><a href="#">Export</a></li> </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> {% block content %} {% endblock %} </div> </div> </div> <script src="{% static 'js/jQuery.js' %}"></script> <script src="{% static 'plugins/bootstrap-3.3.7/js/bootstrap.js' %}"></script> {% block js %} {% endblock %} </body> </html>
# 展示跟进记录 class ConsultRecord(View): def get(self, request, customer_id): if customer_id == '0': all_consult_record = models.ConsultRecord.objects.filter(delete_status=False, consultant=request.user) else: all_consult_record = models.ConsultRecord.objects.filter(customer_id=customer_id, delete_status=False) return render(request, 'crm/consult_record_list.html', { 'all_consult_record': all_consult_record }) # 添加跟进记录 def add_consult_record(request): obj = models.ConsultRecord(consultant=request.user) form_obj = ConsultRecordForm(instance=obj) if request.method == 'POST': form_obj = ConsultRecordForm(request.POST) if form_obj.is_valid(): form_obj.save() return redirect(reverse('consult_record')) return render(request, 'crm/add_consult_record.html', {'form_obj': form_obj}) # 编辑跟进记录 def edit_consult_record(request, edit_id): obj = models.ConsultRecord.objects.filter(id=edit_id).first() form_obj = ConsultRecordForm(instance=obj) if request.method == 'POST': form_obj = ConsultRecordForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('consult_record')) return render(request, 'crm/edit_consult_record.html', {'form_obj': form_obj}) # 新增和编辑跟进记录 def consult_record(request, edit_id=None): obj = models.ConsultRecord.objects.filter(id=edit_id).first() or models.ConsultRecord(consultant=request.user) form_obj = ConsultRecordForm(instance=obj) if request.method == 'POST': form_obj = ConsultRecordForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() return redirect(reverse('consult_record', args=(0,))) return render(request, 'crm/edit_consult_record.html', {'form_obj': form_obj})
报名表展示,新增,编辑,以及报名表显示链接策略
# 展示报名记录 class EnrollmentList(View): def get(self, request, customer_id): if customer_id == '0': all_record = models.Enrollment.objects.filter(delete_status=False, customer__consultant=request.user) else: all_record = models.Enrollment.objects.filter(customer_id=customer_id, delete_status=False) # 获取搜索条件 query_params = self.get_query_params() return render(request, 'crm/enrollment_list.html', { 'all_record': all_record, 'query_params': query_params }) def get_query_params(self): # 获取添加按钮 url = self.request.get_full_path() qd = QueryDict() qd._mutable = True qd['next'] = url query_params = qd.urlencode() return query_params # 添加报名记录 def enrollment(request, customer_id=None, edit_id=None): obj = models.Enrollment.objects.filter(id=edit_id).first() or models.Enrollment(customer_id=customer_id) form_obj = EnrollmentForm(instance=obj) if request.method == 'POST': form_obj = EnrollmentForm(request.POST, instance=obj) if form_obj.is_valid(): enrollment_obj = form_obj.save() # 修改客户的状态 enrollment_obj.customer.status = 'signed' enrollment_obj.customer.save() next = request.GET.get('next') if next: return redirect(next) else: return redirect(reverse('my_customer')) return render(request, 'crm/enrollment.html', {"form_obj": form_obj})
re_path(r'customer_list/', views.CustomerList.as_view(), name='customer'), # 展示报名记录 re_path(r'enrollment_list/(?P<customer_id>\d+)', views.EnrollmentList.as_view(), name='enrollment'), # 添加报名记录 re_path(r'enrollment/add/(?P<customer_id>\d+)', views.enrollment, name='add_enrollment'), # 编辑报名记录 re_path(r'enrollment/edit/(?P<edit_id>\d+)', views.enrollment, name='edit_enrollment'),
# 报名Form class EnrollmentForm(BaseForm): class Meta: model = models.Enrollment exclude = ['delete_status','contract_approved'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 限制当前的客户只能是传的id对应的客户 self.fields['customer'].widget.choices = [(self.instance.customer_id, self.instance.customer), ] # 限制当前可报名的班级是当前客户的意向班级 self.fields['enrolment_class'].widget.choices = [(i.id, i) for i in self.instance.customer.class_list.all()]
{% extends 'layout.html' %} {% block css %} <style> th, tr { text-align: center; } </style> {% endblock %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">Panel heading</div> <div class="panel-body"> <div> <form action="" class="form-inline pull-right"> <input type="text" name="query" class="form-control"> <button class="btn btn-sm btn-primary">搜索 <i class="fa fa-search"></i></button> </form> </div> <form action="" method="post" class="form-inline"> {% csrf_token %} <select name="action" class="form-control" style="margin: 5px 0"> <option value="">请选择</option> <option value="multi_delte">删除</option> </select> <button class="btn btn-success btn-sm">提交</button> <table class="table table-condensed table-hover table-bordered"> <thead> <tr> <th>选择</th> <th>序号</th> <th>客户</th> <th>报名原因</th> <th>期望</th> <th>协议</th> <th>审核</th> <th>报名日期</th> <th>备注</th> <th>校区</th> <th>所报班级</th> <th>操作</th> </tr> </thead> <tbody> {% for record in all_record %} <tr> <td><input type="checkbox" name="id" value="{{ record.id }}"></td> <td>{{ forloop.counter }}</td> <td>{{ record.customer }}</td> <td>{{ record.why_us }}</td> <td>{{ record.your_expectation }}</td> <td>{{ record.contract_agreed }}</td> <td>{{ record.contract_approved }}</td> <td>{{ record.enrolled_date }}</td> <td>{{ record.memo }}</td> <td>{{ record.school }}</td> <td>{{ record.enrolment_class }}</td> <td><a href="{% url 'edit_enrollment' record.id %}?{{ query_params }}"><i class="fa fa-edit fa-fw"></i></a> </td> </tr> {% endfor %} </tbody> </table> </form> <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> </div> </div> {% endblock %}
{% extends 'layout.html' %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> 添加报名记录 </div> <div class="panel-body"> <div class="col-sm-8 col-sm-offset-2 "> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group row {% if field.errors %}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-primary">提交</button> </div> </div> </form> </div> </div> </div> {% endblock %}
{% extends 'layout.html' %} {% block css %} <style> th, tr { text-align: center; } </style> {% endblock %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">Panel heading</div> <div class="panel-body"> <a href="{% url 'add_customer' %}?{{ query_params }}" class="btn btn-primary btn-sm">添加</a> {# {{ add_btn }}#} <div> <form action="" class="form-inline pull-right"> <input type="text" name="query" class="form-control"> <button class="btn btn-sm btn-primary">搜索 <i class="fa fa-search"></i></button> </form> </div> <form action="" method="post" class="form-inline"> {% csrf_token %} <select name="action" class="form-control" style="margin: 5px 0"> <option value="">请选择</option> <option value="multi_delte">删除</option> <option value="multi_apply">放入私户</option> <option value="multi_pub">放入公户</option> {# <option value=""></option>#} </select> <button class="btn btn-success btn-sm">提交</button> <table class="table table-condensed table-hover table-bordered"> <thead> <tr> <th>选择</th> <th>序号</th> <th>QQ</th> {# <th>QQ昵称</th>#} <th>姓名</th> <th>性别</th> {# <th>手机号</th>#} {# <th>客户来源</th>#} <th>咨询课程</th> <th>班级类型</th> <th>状态</th> {# <th>咨询日期</th>#} <th>最后跟进日期</th> <th>销售</th> <th>已报班级</th> {% if request.path_info == '/crm/my_customer/' %} <th>跟进记录</th> <th>报名记录</th> {% endif %} <th>操作</th> </tr> </thead> <tbody> {% for customer in all_customer %} <tr> <td><input type="checkbox" name="id" value="{{ customer.id }}"></td> <td>{{ forloop.counter }}</td> <td>{{ customer.qq }}</td> {# <td>{{ customer.qq_name|default:'暂无' }}</td>#} <td>{{ customer.name|default:'暂无' }}</td> <td>{{ customer.get_sex_display }}</td> {# <td>{{ customer.phone|default:'暂无' }}</td>#} {# <td>{{ customer.get_source_display }}</td>#} <td>{{ customer.course }}</td> <td>{{ customer.get_class_type_display }}</td> <td> {{ customer.show_status }} </td> {# <td>{{ customer.date }}</td>#} <td>{{ customer.last_consult_date }}</td> <td>{{ customer.consultant }}</td> <td>{{ customer.show_classes }}</td> {% if request.path_info == '/crm/my_customer/' %} <td><a href="{% url 'consult_record' customer.id %}">查看跟进</a></td> {# <td><a href="{% url 'add_enrollment' customer.id %}?{{ query_params }}">添加报名表</a></td>#} <td>{{ customer.enroll_link }}</td> {% endif %} <td><a href="{% url 'edit_customer' customer.id %}?{{ query_params }}"><i class="fa fa-edit fa-fw"></i></a> </td> </tr> {% endfor %} </tbody> </table> </form> <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> </div> </div> {% endblock %}
{% load static %} <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="content-Type" charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" href="{% static 'imgs/layout/luffy-logo.png' %}"> <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.3.7/css/bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %}"> <link rel="stylesheet" href="{% static 'css/layout.css' %}"> <title>CRM管理系统</title> {% block css %} {% endblock %} </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <img src="{% static 'imgs/layout/logo.svg' %}" alt="" style="float: left;margin-top: 5px;margin-right: 5px"> <a class="navbar-brand" href="#">CRM管理系统</a> </div> <div id="navbar" class="navbar-collapse collapse"> <div class="navbar-right"> <img class="img-circle" height="45px" style="margin-top: 2.5px;margin-right: 5px" src="{% static 'imgs/layout/default.png' %}" alt="" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <ul class="dropdown-menu"> <li><a href="#">个人中心</a></li> <li><a href="#">修改密码</a></li> <li role="separator" class="divider"></li> <li><a href="#">注销</a></li> </ul> </div> <ul class="nav navbar-nav navbar-right"> <li><a href="#">任务 <i class="fa fa-bell-o" aria-hidden="true"> </i><span class="badge">4</span></a> </li> <li><a href="#">通知 <i class="fa fa-envelope-o" aria-hidden="true"> </i><span class="badge">2</span></a> </li> <li><a href="#">消息 <i class="fa fa-commenting-o" aria-hidden="true"> </i><span class="badge">6</span></a></li> </ul> </div> </div> </nav> <div class="container-fluid"> <div class="row"> <div class="col-sm-3 col-md-2 sidebar"> <ul class="nav nav-sidebar"> <li><a href="{% url 'customer' %}">客户列表</a></li> <li><a href="{% url 'my_customer' %}">我的客户</a></li> <li><a href="{% url 'consult_record' 0 %}">我的跟进记录</a></li> <li><a href="{% url 'enrollment' 0 %}">报名记录</a></li> <li><a href="#">Reports</a></li> <li><a href="#">Analytics</a></li> <li><a href="#">Export</a></li> </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> {% block content %} {% endblock %} </div> </div> </div> <script src="{% static 'js/jQuery.js' %}"></script> <script src="{% static 'plugins/bootstrap-3.3.7/js/bootstrap.js' %}"></script> {% block js %} {% endblock %} </body> </html>
from django.utils.safestring import mark_safe from django.urls import reverse class Customer(models.Model): """ 客户表 """ qq = models.CharField('QQ', max_length=64, unique=True, help_text='QQ号必须唯一') qq_name = models.CharField('QQ昵称', max_length=64, blank=True, null=True) name = models.CharField('姓名', max_length=32, blank=True, null=True, help_text='学员报名后,请改为真实姓名') sex_type = (('male', '男'), ('female', '女')) sex = models.CharField("性别", choices=sex_type, max_length=16, default='male', blank=True, null=True) birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True) phone = models.BigIntegerField('手机号', blank=True, null=True) source = models.CharField('客户来源', max_length=64, choices=source_type, default='qq') introduce_from = models.ForeignKey('self', verbose_name="转介绍自学员", blank=True, null=True) course = MultiSelectField("咨询课程", choices=course_choices) class_type = models.CharField("班级类型", max_length=64, choices=class_type_choices, default='fulltime') customer_note = models.TextField("客户备注", blank=True, null=True, ) status = models.CharField("状态", choices=enroll_status_choices, max_length=64, default="unregistered", help_text="选择客户此时的状态") network_consult_note = models.TextField(blank=True, null=True, verbose_name='网络咨询师咨询内容') date = models.DateTimeField("咨询日期", auto_now_add=True) last_consult_date = models.DateField("最后跟进日期", auto_now_add=True) next_date = models.DateField("预计再次跟进时间", blank=True, null=True) network_consultant = models.ForeignKey('UserProfile', blank=True, null=True, verbose_name='咨询师', related_name='network_consultant') consultant = models.ForeignKey('UserProfile', verbose_name="销售", related_name='customers', blank=True, null=True, ) class_list = models.ManyToManyField('ClassList', verbose_name="已报班级", blank=True) def show_status(self): color_dict = { "signed": 'green', "unregistered": 'red', "studying": 'pink', "paid_in_full": 'blue', } return mark_safe( '<span style="background-color: {};color: white;padding: 4px">{}</span>'.format(color_dict[self.status], self.get_status_display())) def show_classes(self): return ' | '.join([str(i) for i in self.class_list.all()]) def enroll_link(self): # 报名表显示链接 if not self.enrollment_set.exists(): return mark_safe('<a href="{}">添加报名表</a>'.format(reverse('add_enrollment',args=(self.id,)))) else: return mark_safe('<a href="{}">添加</a> | <a href="{}">查看</a>'.format(reverse('add_enrollment',args=(self.id,)),reverse('enrollment',args=(self.id,)))) def __str__(self): return "{}<{}>".format(self.name, self.qq) class Meta: verbose_name = '客户列表' verbose_name_plural = '客户列表' class Enrollment(models.Model): """ 报名表 """ why_us = models.TextField("为什么报名", max_length=1024, default=None, blank=True, null=True) your_expectation = models.TextField("学完想达到的具体期望", max_length=1024, blank=True, null=True) contract_agreed = models.BooleanField("我已认真阅读完培训协议并同意全部协议内容", default=False) contract_approved = models.BooleanField("审批通过", help_text="在审阅完学员的资料无误后勾选此项,合同即生效", default=False) enrolled_date = models.DateTimeField(auto_now_add=True, verbose_name="报名日期") memo = models.TextField('备注', blank=True, null=True) delete_status = models.BooleanField(verbose_name='删除状态', default=False) customer = models.ForeignKey('Customer', verbose_name='客户名称') school = models.ForeignKey('Campuses', verbose_name='校区') enrolment_class = models.ForeignKey("ClassList", verbose_name="所报班级") class Meta: unique_together = ('enrolment_class', 'customer')
公户变私户,事务+行级锁
- 多个销售同时申请一个客户 谁先申请就是谁的 mysql数据中加行级锁 开始事务:begin; 加锁 for update : select * from student where id=1 for update; 结束事务:commit;
from django.db import transaction
def multi_apply(self):
# 公户变私户
ids = self.request.POST.getlist('id')
apply_num = len(ids)
# 事务+ 行级锁
with transaction.atomic():
obj_list = models.Customer.objects.filter(id__in=ids, consultant__isnull=True).select_for_update()
if apply_num == len(obj_list): # 第一次来
obj_list.update(consultant=self.request.user)
else:
return HttpResponse('你手速太慢啦,客户被抢走了')
添加私户不超过上限
from django.conf import settings def multi_apply(self): # 公户变私户 ids = self.request.POST.getlist('id') apply_num = len(ids) # 用户总数不能超过设置值 if self.request.user.customers.count() + apply_num > settings.CUSTOMER_MAX_NUM: return HttpResponse('私户已达上限,不要太贪心啦')
# 用户总数不能超过设置值 CUSTOMER_MAX_NUM = 5
课程记录,学习记录的展示,新增,编辑
学习记录的管理 - 批量添加 student_list = [] for student in all_students: student_list.append(models.StudyRecord(course_record=course_obj, student=student)) models.StudyRecord.objects.bulk_create(student_list) - 展示编辑学习记录 from django.forms import modelformset_factory FormSet = modelformset_factory(models.StudyRecord, StudyRecordForm, extra=0) queryset = models.StudyRecord.objects.filter(course_record_id=course_id) form_set = FormSet(queryset=queryset) {% for form in form_set %} {{ form.instance.student.name }} form.instance ——》 学习记录 {{ form.attendance }} ————》对应的input {{ form.score }} {{ form.homework_note }} 提交编辑 {{ form_set.management_form }} {{ form.id }} {{ form.student }} form_set = FormSet(request.POST) if form_set.is_valid(): form_set.save()
# 展示班级列表 url(r'class_list/', teacher.ClassList.as_view(), name='class_list'), # 添加班级 url(r'class/add/', teacher.classes, name='add_class'), # 编辑班级 url(r'class/edit/(\d+)/', teacher.classes, name='edit_class'), # 展示某个班级的课程记录 url(r'course_list/(?P<class_id>\d+)/', teacher.CourseList.as_view(), name='course_list'), # 添加课程记录 url(r'course/add/(?P<class_id>\d+)/', teacher.course, name='add_course'), # 编辑课程记录 url(r'course/edit/(?P<edit_id>\d+)/', teacher.course, name='edit_course'), # 展示学习记录 url(r'study_record_list/(?P<course_id>\d+)/', teacher.study_record, name='study_record_list'),
from django.shortcuts import render, redirect, reverse, HttpResponse from django.contrib import auth from crm.forms import * from crm import models from django.utils.safestring import mark_safe from utils.pagination import Pagination from django.views import View from django.db.models import Q from django.http import QueryDict import copy from django.db import transaction import time from django.conf import settings from django.views import View # 班级列表展示 class ClassList(View): def get(self, request): # 模糊搜索 q = self.get_search_contion(['course', 'semester']) all_class = models.ClassList.objects.filter(q) # 获取路径 query_params = self.get_query_params() # 分页的应用 page = Pagination(request, len(all_class), request.GET.copy()) return render(request, 'crm/teacher/class_list.html', {"all_class": all_class[page.start:page.end], 'pagination': page.show_li, 'query_params': query_params}) def get_search_contion(self, fields_list): query = self.request.GET.get('query', '') q = Q() q.connector = 'OR' for i in fields_list: q.children.append(Q(('{}__contains'.format(i), query))) return q def get_query_params(self): url = self.request.get_full_path() qd = QueryDict() qd._mutable = True qd['next'] = url query_params = qd.urlencode() return query_params # 添加、编辑班级 def classes(request, edit_id=None): obj = models.ClassList.objects.filter(id=edit_id).first() form_obj = ClassForm(instance=obj) title = '编辑班级' if obj else '新增班级' if request.method == 'POST': form_obj = ClassForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() next = request.GET.get('next') if next: return redirect(next) return redirect(reverse('class_list')) return render(request, 'crm/form.html', {'title': title, 'form_obj': form_obj}) class CourseList(View): def get(self, request, class_id): # 模糊搜索 q = self.get_search_contion([]) all_course = models.CourseRecord.objects.filter(q, re_class_id=class_id) # 获取路径 query_params = self.get_query_params() # 分页的应用 page = Pagination(request, len(all_course), request.GET.copy()) return render(request, 'crm/teacher/course_list.html', {"all_course": all_course[page.start:page.end], 'pagination': page.show_li, 'query_params': query_params, 'class_id': class_id }) def post(self, request, class_id): action = request.POST.get('action') if not hasattr(self, action): return HttpResponse('非法操作') ret = getattr(self, action)() if ret: return ret return self.get(request, class_id) def get_search_contion(self, fields_list): query = self.request.GET.get('query', '') q = Q() q.connector = 'OR' for i in fields_list: q.children.append(Q(('{}__contains'.format(i), query))) return q def get_query_params(self): url = self.request.get_full_path() qd = QueryDict() qd._mutable = True qd['next'] = url query_params = qd.urlencode() return query_params def multi_init(self): # 根据当前提交的课程记录Id批量初识化学生的学习记录 course_ids = self.request.POST.getlist('id') course_obj_list = models.CourseRecord.objects.filter(id__in=course_ids) for course_obj in course_obj_list: # 查询当前课程记录代表的班级的学生 all_students = course_obj.re_class.customer_set.filter(status='studying') student_list = [] for student in all_students: # models.StudyRecord.objects.create(course_record=course_obj, student=student) # obj = models.StudyRecord(course_record=course_obj, student=student) # obj.save() student_list.append(models.StudyRecord(course_record=course_obj, student=student)) models.StudyRecord.objects.bulk_create(student_list) def course(request, class_id=None, edit_id=None): obj = models.CourseRecord.objects.filter(id=edit_id).first() or models.CourseRecord(re_class_id=class_id, teacher=request.user) form_obj = CourseForm(instance=obj) title = '编辑课程' if edit_id else '新增课程' if request.method == 'POST': form_obj = CourseForm(request.POST, instance=obj) if form_obj.is_valid(): form_obj.save() next = request.GET.get('next') if next: return redirect(next) return redirect(reverse('course_list', args=(class_id,))) return render(request, 'crm/form.html', {'title': title, 'form_obj': form_obj, }) # 展示学习记录 from django.forms import modelformset_factory def study_record(request, course_id): FormSet = modelformset_factory(models.StudyRecord, StudyRecordForm, extra=0) queryset = models.StudyRecord.objects.filter(course_record_id=course_id) form_set = FormSet(queryset=queryset) if request.method == 'POST': form_set = FormSet(request.POST) if form_set.is_valid(): form_set.save() return render(request, 'crm/teacher/study_record_list.html', {"form_set": form_set})
from django.db import models from django.contrib import auth from django.core.exceptions import PermissionDenied from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, User from django.utils.translation import ugettext_lazy as _ from multiselectfield import MultiSelectField from django.utils.safestring import mark_safe from django.urls import reverse course_choices = (('LinuxL', 'Linux中高级'), ('PythonFullStack', 'Python高级全栈开发'),) class_type_choices = (('fulltime', '脱产班',), ('online', '网络班'), ('weekend', '周末班',),) source_type = (('qq', "qq群"), ('referral', "内部转介绍"), ('website', "官方网站"), ('baidu_ads', "百度推广"), ('office_direct', "直接上门"), ('WoM', "口碑"), ('public_class', "公开课"), ('website_luffy', "路飞官网"), ('others', "其它"),) enroll_status_choices = (('signed', "已报名"), ('unregistered', "未报名"), ('studying', '学习中'), ('paid_in_full', "学费已交齐")) seek_status_choices = (('A', '近期无报名计划'), ('B', '1个月内报名'), ('C', '2周内报名'), ('D', '1周内报名'), ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '无效'),) pay_type_choices = (('deposit', "订金/报名费"), ('tuition', "学费"), ('transfer', "转班"), ('dropout', "退学"), ('refund', "退款"),) attendance_choices = (('checked', "已签到"), ('vacate', "请假"), ('late', "迟到"), ('absence', "缺勤"), ('leave_early', "早退"),) score_choices = ((100, 'A+'), (90, 'A'), (85, 'B+'), (80, 'B'), (70, 'B-'), (60, 'C+'), (50, 'C'), (40, 'C-'), (0, ' D'), (-1, 'N/A'), (-100, 'COPY'), (-1000, 'FAIL'),) class Customer(models.Model): """ 客户表 """ qq = models.CharField('QQ', max_length=64, unique=True, help_text='QQ号必须唯一') qq_name = models.CharField('QQ昵称', max_length=64, blank=True, null=True) name = models.CharField('姓名', max_length=32, blank=True, null=True, help_text='学员报名后,请改为真实姓名') sex_type = (('male', '男'), ('female', '女')) sex = models.CharField("性别", choices=sex_type, max_length=16, default='male', blank=True, null=True) birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True) phone = models.BigIntegerField('手机号', blank=True, null=True) source = models.CharField('客户来源', max_length=64, choices=source_type, default='qq') introduce_from = models.ForeignKey('self', verbose_name="转介绍自学员", blank=True, null=True) course = MultiSelectField("咨询课程", choices=course_choices) class_type = models.CharField("班级类型", max_length=64, choices=class_type_choices, default='fulltime') customer_note = models.TextField("客户备注", blank=True, null=True, ) status = models.CharField("状态", choices=enroll_status_choices, max_length=64, default="unregistered", help_text="选择客户此时的状态") network_consult_note = models.TextField(blank=True, null=True, verbose_name='网络咨询师咨询内容') date = models.DateTimeField("咨询日期", auto_now_add=True) last_consult_date = models.DateField("最后跟进日期", auto_now_add=True) next_date = models.DateField("预计再次跟进时间", blank=True, null=True) network_consultant = models.ForeignKey('UserProfile', blank=True, null=True, verbose_name='咨询师', related_name='network_consultant') consultant = models.ForeignKey('UserProfile', verbose_name="销售", related_name='customers', blank=True, null=True, ) class_list = models.ManyToManyField('ClassList', verbose_name="已报班级", blank=True) def show_status(self): color_dict = { "signed": 'green', "unregistered": 'red', "studying": 'pink', "paid_in_full": 'blue', } return mark_safe( '<span style="background-color: {};color: white;padding: 4px">{}</span>'.format(color_dict[self.status], self.get_status_display())) def show_classes(self): return ' | '.join([str(i) for i in self.class_list.all()]) def enroll_link(self): if not self.enrollment_set.exists(): return mark_safe('<a href="{}">添加报名表</a>'.format(reverse('add_enrollment', args=(self.id,)))) else: return mark_safe( '<a href="{}">添加</a> | <a href="{}">查看</a>'.format(reverse('add_enrollment', args=(self.id,)), reverse('enrollment', args=(self.id,)))) def __str__(self): return "{}<{}>".format(self.name, self.qq) class Meta: verbose_name = '客户列表' verbose_name_plural = '客户列表' class Campuses(models.Model): """ 校区表 """ name = models.CharField(verbose_name='校区', max_length=64) address = models.CharField(verbose_name='详细地址', max_length=512, blank=True, null=True) def __str__(self): return self.name class ContractTemplate(models.Model): """ 合同模板表 """ name = models.CharField("合同名称", max_length=128, unique=True) content = models.TextField("合同内容") date = models.DateField(auto_now=True) class ClassList(models.Model): """ 班级表 """ course = models.CharField("课程名称", max_length=64, choices=course_choices) semester = models.IntegerField("学期") campuses = models.ForeignKey('Campuses', verbose_name="校区") price = models.IntegerField("学费", default=10000) memo = models.CharField('说明', blank=True, null=True, max_length=100) start_date = models.DateField("开班日期") graduate_date = models.DateField("结业日期", blank=True, null=True) contract = models.ForeignKey('ContractTemplate', verbose_name="选择合同模版", blank=True, null=True) teachers = models.ManyToManyField('UserProfile', verbose_name="老师") class_type = models.CharField(choices=class_type_choices, max_length=64, verbose_name='班额及类型', blank=True, null=True) class Meta: unique_together = ("course", "semester", 'campuses') def __str__(self): return "{}{}({})".format(self.get_course_display(), self.semester, self.campuses) def show_teachers(self): return "|".join([ str(i) for i in self.teachers.all() ]) class ConsultRecord(models.Model): """ 跟进记录表 """ customer = models.ForeignKey('Customer', verbose_name="所咨询客户") note = models.TextField(verbose_name="跟进内容...") status = models.CharField("跟进状态", max_length=8, choices=seek_status_choices, help_text="选择客户此时的状态") consultant = models.ForeignKey("UserProfile", verbose_name="跟进人", related_name='records') date = models.DateTimeField("跟进日期", auto_now_add=True) delete_status = models.BooleanField(verbose_name='删除状态', default=False) class Enrollment(models.Model): """ 报名表 """ why_us = models.TextField("为什么报名", max_length=1024, default=None, blank=True, null=True) your_expectation = models.TextField("学完想达到的具体期望", max_length=1024, blank=True, null=True) contract_agreed = models.BooleanField("我已认真阅读完培训协议并同意全部协议内容", default=False) contract_approved = models.BooleanField("审批通过", help_text="在审阅完学员的资料无误后勾选此项,合同即生效", default=False) enrolled_date = models.DateTimeField(auto_now_add=True, verbose_name="报名日期") memo = models.TextField('备注', blank=True, null=True) delete_status = models.BooleanField(verbose_name='删除状态', default=False) customer = models.ForeignKey('Customer', verbose_name='客户名称') school = models.ForeignKey('Campuses', verbose_name='校区') enrolment_class = models.ForeignKey("ClassList", verbose_name="所报班级") class Meta: unique_together = ('enrolment_class', 'customer') class PaymentRecord(models.Model): """ 缴费记录表 """ pay_type = models.CharField("费用类型", choices=pay_type_choices, max_length=64, default="deposit") paid_fee = models.IntegerField("费用数额", default=0) note = models.TextField("备注", blank=True, null=True) date = models.DateTimeField("交款日期", auto_now_add=True) course = models.CharField("课程名", choices=course_choices, max_length=64, blank=True, null=True, default='N/A') class_type = models.CharField("班级类型", choices=class_type_choices, max_length=64, blank=True, null=True, default='N/A') enrolment_class = models.ForeignKey('ClassList', verbose_name='所报班级', blank=True, null=True) customer = models.ForeignKey('Customer', verbose_name="客户") consultant = models.ForeignKey('UserProfile', verbose_name="销售") delete_status = models.BooleanField(verbose_name='删除状态', default=False) status_choices = ( (1, '未审核'), (2, '已审核'), ) status = models.IntegerField(verbose_name='审核', default=1, choices=status_choices) confirm_date = models.DateTimeField(verbose_name="确认日期", null=True, blank=True) confirm_user = models.ForeignKey(verbose_name="确认人", to='UserProfile', related_name='confirms', null=True, blank=True) class CourseRecord(models.Model): """课程记录表""" day_num = models.IntegerField("节次", help_text="此处填写第几节课或第几天课程...,必须为数字") date = models.DateField(auto_now_add=True, verbose_name="上课日期") course_title = models.CharField('本节课程标题', max_length=64, blank=True, null=True) course_memo = models.TextField('本节课程内容', max_length=300, blank=True, null=True) has_homework = models.BooleanField(default=True, verbose_name="本节有作业") homework_title = models.CharField('本节作业标题', max_length=64, blank=True, null=True) homework_memo = models.TextField('作业描述', max_length=500, blank=True, null=True) scoring_point = models.TextField('得分点', max_length=300, blank=True, null=True) re_class = models.ForeignKey('ClassList', verbose_name="班级") teacher = models.ForeignKey('UserProfile', verbose_name="班主任") class Meta: unique_together = ('re_class', 'day_num') def __str__(self): return "{}({})".format(self.re_class,self.day_num) class StudyRecord(models.Model): """ 学习记录 """ attendance = models.CharField("考勤", choices=attendance_choices, default="checked", max_length=64) score = models.IntegerField("本节成绩", choices=score_choices, default=-1) homework_note = models.CharField(max_length=255, verbose_name='作业批语', blank=True, null=True) date = models.DateTimeField(auto_now_add=True) note = models.CharField("备注", max_length=255, blank=True, null=True) homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None) course_record = models.ForeignKey('CourseRecord', verbose_name="某节课程",) student = models.ForeignKey('Customer', verbose_name="学员") class Meta: unique_together = ('course_record', 'student') class UserManager(BaseUserManager): use_in_migrations = True def _create_user(self, username, password, **extra_fields): """ Creates and saves a User with the given username, email and password. """ if not username: raise ValueError('The given username must be set') username = self.normalize_email(username) username = self.model.normalize_username(username) user = self.model(username=username, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_user(self, username, password=None, **extra_fields): extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) return self._create_user(username, password, **extra_fields) def create_superuser(self, username, password, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) if extra_fields.get('is_staff') is not True: raise ValueError('Superuser must have is_staff=True.') if extra_fields.get('is_superuser') is not True: raise ValueError('Superuser must have is_superuser=True.') return self._create_user(username, password, **extra_fields) # A few helper functions for common logic between User and AnonymousUser. def _user_get_all_permissions(user, obj): permissions = set() for backend in auth.get_backends(): if hasattr(backend, "get_all_permissions"): permissions.update(backend.get_all_permissions(user, obj)) return permissions def _user_has_perm(user, perm, obj): """ A backend can raise `PermissionDenied` to short-circuit permission checking. """ for backend in auth.get_backends(): if not hasattr(backend, 'has_perm'): continue try: if backend.has_perm(user, perm, obj): return True except PermissionDenied: return False return False def _user_has_module_perms(user, app_label): """ A backend can raise `PermissionDenied` to short-circuit permission checking. """ for backend in auth.get_backends(): if not hasattr(backend, 'has_module_perms'): continue try: if backend.has_module_perms(user, app_label): return True except PermissionDenied: return False return False class Department(models.Model): name = models.CharField(max_length=32, verbose_name="部门名称") count = models.IntegerField(verbose_name="人数", default=0) def __str__(self): return self.name class UserProfile(AbstractBaseUser, PermissionsMixin): username = models.EmailField( max_length=255, unique=True, ) is_staff = models.BooleanField( _('staff status'), default=False, help_text=_('Designates whether the user can log into this admin site.'), ) is_admin = models.BooleanField(default=False) name = models.CharField('名字', max_length=32) department = models.ForeignKey('Department', default=None, blank=True, null=True) mobile = models.CharField('手机', max_length=32, default=None, blank=True, null=True) memo = models.TextField('备注', blank=True, null=True, default=None) date_joined = models.DateTimeField(auto_now_add=True) USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['name'] class Meta: verbose_name = '账户信息' verbose_name_plural = "账户信息" def get_full_name(self): # The user is identified by their email address return self.name def get_short_name(self): # The user is identified by their email address return self.username def __str__(self): # __unicode__ on Python 2 return self.username def has_perm(self, perm, obj=None): # "Does the user have a specific permission?" # Simplest possible answer: Yes, always if self.is_active and self.is_superuser: return True return _user_has_perm(self, perm, obj) def has_perms(self, perm_list, obj=None): # "Does the user have a specific permission?" # Simplest possible answer: Yes, always for perm in perm_list: if not self.has_perm(perm, obj): return False return True def has_module_perms(self, app_label): # "Does the user have permissions to view the app `app_label`?" # Simplest possible answer: Yes, always if self.is_active and self.is_superuser: return True return _user_has_module_perms(self, app_label) objects = UserManager()
from django import forms from crm import models from django.core.exceptions import ValidationError class BaseForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for filed in self.fields.values(): filed.widget.attrs.update({'class': 'form-control'}) # 注册form class RegForm(BaseForm): password = forms.CharField( label='密码', widget=forms.widgets.PasswordInput(), min_length=6, error_messages={'min_length': '最小长度为6'} ) re_password = forms.CharField( label='确认密码', widget=forms.widgets.PasswordInput() ) class Meta: model = models.UserProfile # fields = '__all__' # 所有字段 fields = ['username', 'password', 're_password', 'name', 'department'] # 指定字段 # exclude = [''] widgets = { 'username': forms.widgets.EmailInput(attrs={'class': 'form-control'}), 'password': forms.widgets.PasswordInput, } labels = { 'username': '用户名', 'password': '密码', 'name': '姓名', 'department': '部门', } error_messages = { 'password': { 'required': '密码不能为空', } } def clean(self): pwd = self.cleaned_data.get('password') re_pwd = self.cleaned_data.get('re_password') if pwd == re_pwd: return self.cleaned_data self.add_error('re_password', '两次密码不一致') raise ValidationError('两次密码不一致') # 客户form class CustomerForm(BaseForm): class Meta: model = models.Customer fields = '__all__' widgets = { 'course': forms.widgets.SelectMultiple } # 跟进记录form class ConsultRecordForm(BaseForm): class Meta: model = models.ConsultRecord # fields = '__all__' exclude = ['delete_status'] # widgets = { # 'customer': forms.widgets.Select(choices=((1, 'xxxx'),)) # } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) customer_choice = [(i.id, i) for i in self.instance.consultant.customers.all()] customer_choice.insert(0, ('', '--------')) # 限制客户是当前销售的私户 self.fields['customer'].widget.choices = customer_choice # 限制跟进人是当前的用户(销售) self.fields['consultant'].widget.choices = [(self.instance.consultant_id, self.instance.consultant), ] # 报名表Form class EnrollmentForm(BaseForm): class Meta: model = models.Enrollment exclude = ['delete_status', 'contract_approved'] labels = {} def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 限制当前的客户只能是传的id对应的客户 self.fields['customer'].widget.choices = [(self.instance.customer_id, self.instance.customer), ] # 限制当前可报名的班级是当前客户的意向班级 self.fields['enrolment_class'].widget.choices = [(i.id, i) for i in self.instance.customer.class_list.all()] # 班级Form class ClassForm(BaseForm): class Meta: model = models.ClassList fields = '__all__' # 课程记录Form class CourseForm(BaseForm): class Meta: model = models.CourseRecord fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 限制当前的班级是传过来id的班级 self.fields['re_class'].widget.choices = [(self.instance.re_class_id, self.instance.re_class)] # 限制当前的班主任是当前用户 self.fields['teacher'].widget.choices = [(self.instance.teacher_id, self.instance.teacher)] # 学习记录Form class StudyRecordForm(BaseForm): class Meta: model = models.StudyRecord fields = ['attendance', 'score', 'homework_note', 'student']
{% extends 'layout.html' %} {% block css %} <style> th, tr { text-align: center; } </style> {% endblock %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">班级列表</div> <div class="panel-body"> <a href="{% url 'add_class' %}?{{ query_params }}" class="btn btn-primary btn-sm">添加</a> <div> <form action="" class="form-inline pull-right"> <input type="text" name="query" class="form-control"> <button class="btn btn-sm btn-primary">搜索 <i class="fa fa-search"></i></button> </form> </div> <form action="" method="post" class="form-inline"> {% csrf_token %} <select name="action" class="form-control" style="margin: 5px 0"> <option value="">请选择</option> <option value="multi_delte">删除</option> </select> <button class="btn btn-success btn-sm">提交</button> <table class="table table-condensed table-hover table-bordered"> <thead> <tr> <th>选择</th> <th>序号</th> <th>班级名称</th> <th>学费</th> <th>开班日期</th> <th>班级类型</th> <th>老师</th> <th>操作</th> </tr> </thead> <tbody> {% for class in all_class %} <tr> <td><input type="checkbox" name="id" value="{{ class.id }}"></td> <td>{{ forloop.counter }}</td> <td><a href="{% url 'course_list' class.id %}">{{ class }}</a></td> <td>{{ class.price }}</td> <td>{{ class.start_date }}</td> <td>{{ class.get_class_type_display }}</td> <td>{{ class.show_teachers }}</td> <td><a href="{% url 'edit_class' class.id %}?{{ query_params }}"><i class="fa fa-edit fa-fw"></i></a> </td> </tr> {% endfor %} </tbody> </table> </form> <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> </div> </div> {% endblock %}
{% extends 'layout.html' %} {% block css %} <style> th, tr { text-align: center; } </style> {% endblock %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">课程列表</div> <div class="panel-body"> <a href="{% url 'add_course' class_id %}?{{ query_params }}" class="btn btn-primary btn-sm">添加</a> <div> <form action="" class="form-inline pull-right"> <input type="text" name="query" class="form-control"> <button class="btn btn-sm btn-primary">搜索 <i class="fa fa-search"></i></button> </form> </div> <form action="" method="post" class="form-inline"> {% csrf_token %} <select name="action" class="form-control" style="margin: 5px 0"> <option value="">请选择</option> <option value="multi_delete">删除</option> <option value="multi_init">初识化学习记录</option> </select> <button class="btn btn-success btn-sm">提交</button> <table class="table table-condensed table-hover table-bordered"> <thead> <tr> <th>选择</th> <th>序号</th> <th>课程名称</th> <th>课程标题</th> <th>是否有作业</th> <th>上课日期</th> <th>班主任</th> <th>操作</th> </tr> </thead> <tbody> {% for course in all_course %} <tr> <td><input type="checkbox" name="id" value="{{ course.id }}"></td> <td>{{ forloop.counter }}</td> <td><a href="{% url 'study_record_list' course.id %}">{{ course }}</a></td> <td>{{ course.course_title }}</td> <td>{{ course.has_homework }}</td> <td>{{ course.date }}</td> <td>{{ course.teacher }}</td> <td><a href="{% url 'edit_course' course.id %}?{{ query_params }}"><i class="fa fa-edit fa-fw"></i></a> </td> </tr> {% endfor %} </tbody> </table> </form> <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> </div> </div> {% endblock %}
{% extends 'layout.html' %} {% block css %} <style> th, tr { text-align: center; } </style> {% endblock %} {% block content %} <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading">学习记录</div> <div class="panel-body"> <div> <form action="" class="form-inline pull-right"> <input type="text" name="query" class="form-control"> <button class="btn btn-sm btn-primary">搜索 <i class="fa fa-search"></i></button> </form> </div> <form action="" method="post"> {% csrf_token %} {{ form_set.management_form }} <table class="table table-condensed table-hover table-bordered"> <thead> <tr> {# <th>选择</th>#} <th>序号</th> <th>学生姓名</th> <th>考勤</th> <th>成绩</th> <th>批语</th> </tr> </thead> <tbody> {% for form in form_set %} <tr> {{ form.id }} <td style="display: none">{{ form.student }}</td> {# <td><input type="checkbox" name="id" value="{{ class.id }}"></td>#} <td>{{ forloop.counter }}</td> <td>{{ form.instance.student.name }}</td> <td>{{ form.attendance }}</td> <td>{{ form.score }}</td> <td>{{ form.homework_note }}</td> </tr> {% endfor %} </tbody> </table> <button class="btn btn-success btn-sm">保存</button> </form> <div style="text-align: center"> <nav aria-label="Page navigation"> <ul class="pagination"> {{ pagination }} </ul> </nav> </div> </div> </div> {% endblock %}
幻想毫无价值,计划渺如尘埃,目标不可能达到。这一切的一切毫无意义——除非我们付诸行动。