• Django


    表结构设计

    注册

    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('两次密码不一致')
    forms
    # 注册
    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})
    views.py
    {% 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>
    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})
    views.py
    {% 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=" 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(/[	
    ]/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>
    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'})
    forms.py
    {% 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">&nbsp;</i><span class="badge">4</span></a>
                    </li>
                    <li><a href="#">通知 <i class="fa fa-envelope-o" aria-hidden="true">&nbsp;</i><span class="badge">2</span></a>
                    </li>
                    <li><a href="#">消息 <i class="fa fa-commenting-o" aria-hidden="true">&nbsp;</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>
    layout.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')),
    ]
    urls.py
    def cutomer_list(request):
        cutomer_obj = Customer.objects.all()
        return render(request, 'my_crm/customer_list.html', {'cutomer_obj': cutomer_obj})
    views.py
    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)
    models.py
    {% 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 %}
    html

    总结:

    客户信息展示
    		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
    views.py
    {% 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
    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)
    #                       
    #                   })
    views.py

     分页第三版选中当前页,增加首页,尾页

    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
    views.py
    {% 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
    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
    pagination.py
    <div style="text-align: center">
                <nav aria-label="Page navigation">
                    <ul class="pagination">
                        {{ pagination }}
                    </ul>
                </nav>
            </div>
    
    html
    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
    views.py

    新增客户

    # 增加客户
    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})
    views.py
    {% 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 %}
    html

    编辑客户

       # # 编辑客户
        # url(r'customer/edit/(d+)', views.edit_customer, name='edit_customer')
    urls.py
    # 编辑客户
    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})
    View Code
    {% 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 %}
    html

    新增,编辑二合一

        # 增加客户
        url(r'customer/add/', views.customer, name='add_customer'),
        # 编辑客户
        url(r'customer/edit/(d+)', views.customer, name='edit_customer')
    urls
    # 新增和编辑客户
    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})
    views.py
    {% 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 %}
    html

     公户和私户

        # 公户
        # url(r'customer_list', views.customer_list, name='customer'),
        # 私户
        # url(r'my_customer', views.customer_list, name='my_customer'),
    urls.py
    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})
    views.pys

    多选公私户转化(反射+CBV)

        公户 
     url(r'customer_list/', views.CustomerList.as_view(), name='customer'),
        私户
       url(r'my_customer/', views.CustomerList.as_view(), name='my_customer'),
    urls.py
    {% 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 %}
    html
    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))
    views.py

     模糊查询 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 %}
    html
    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})
    views.py

     写法二(推荐)

    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) )
    View.py

    分页保留搜索条件

    """
    分页器
    """
    
    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))
    pagination.py
    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) )
    View.py

     总结:

    	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
    views.py
    {% 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 %}
    html

     跟进记录的展示,新增和编辑

        # 展示跟进记录
        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'),
    urls.py
    # 跟进记录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),]
    forms.py
    {% 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 %}
    consult_record_list.html
    {% 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 %}
    add_consult_record.html
    {% 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 %}
    edit_consult_record.html
    {% 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">&nbsp;</i><span class="badge">4</span></a>
                    </li>
                    <li><a href="#">通知 <i class="fa fa-envelope-o" aria-hidden="true">&nbsp;</i><span class="badge">2</span></a>
                    </li>
                    <li><a href="#">消息 <i class="fa fa-commenting-o" aria-hidden="true">&nbsp;</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>
    layout.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})
    View.py

     报名表展示,新增,编辑,以及报名表显示链接策略

    # 展示报名记录
    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})
    views.py
        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'),
    urls.py
    # 报名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()]
    forms.py
    {% 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 %}
    enrollment_list.html
    {% 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 %}
    enrollment.html
    {% 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 %}
    customer_list.html
    {% 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">&nbsp;</i><span class="badge">4</span></a>
                    </li>
                    <li><a href="#">通知 <i class="fa fa-envelope-o" aria-hidden="true">&nbsp;</i><span class="badge">2</span></a>
                    </li>
                    <li><a href="#">消息 <i class="fa fa-commenting-o" aria-hidden="true">&nbsp;</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>
    layout.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')
    models.py

    公户变私户,事务+行级锁

    - 多个销售同时申请一个客户
                谁先申请就是谁的
            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('私户已达上限,不要太贪心啦')
    views.py
    # 用户总数不能超过设置值
    
    CUSTOMER_MAX_NUM = 5
    settings.py

     课程记录,学习记录的展示,新增,编辑

    学习记录的管理
                - 批量添加
                    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'),
    urls.py
    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})
    views.py
    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()
    models.py
    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']
    forms.py
    {% 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 %}
    class_list.html
    {% 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 %}
    course_list.html
    {% 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 %}
    study_record_list.html
    幻想毫无价值,计划渺如尘埃,目标不可能达到。这一切的一切毫无意义——除非我们付诸行动。
  • 相关阅读:
    百度云推送
    web请求报出 “超过了最大请求长度” 【注意:重启IIS】
    页面多个Jquery版本共存的冲突问题,解决方法!
    Web Api 中使用 PCM TO WAV 的语音操作
    Web Api 如何做上传文件的单元测试
    那些年收集的前端学习资源
    原创: 做一款属于自己风格的音乐播放器 (HTML5的Audio新特性)
    Web Api 接口文档制作
    如何在Asp.Net WebApi接口中,验证请求参数中是否携带token标识!
    JavaScript 面试题,给大家补补基础,加加油,埋埋坑!
  • 原文地址:https://www.cnblogs.com/TodayWind/p/13841715.html
Copyright © 2020-2023  润新知