• Django项目之客户


     关于客户的操作

    主页(被继承)

    {% load static %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
        <meta name="description" content="">
        <meta name="author" content="">
    
        <title>CRM管理系统</title>
    
        <!-- Bootstrap core CSS -->
        <link href="{% static '/plugins/bootstrap.min.css' %}" rel="stylesheet">
        <link href="{% static '/plugins/dashboard.css' %}" rel="stylesheet">
        <link href="{% static '/fonts/font-awesome.min.css' %}" rel="stylesheet">
        <link href="{% static '/fonts/fontawesome-webfont.ttf' %}" rel="stylesheet">
        <link href="{% static '/fonts/fontawesome-webfont.woff' %}" rel="stylesheet">
        <link href="{% static '/fonts/fontawesome-webfont.woff2' %}" rel="stylesheet">
        <link rel="icon" href="{% static '/layout/luffy-logo.png' %}">
        <script src="{% static 'js/jquery.min.js' %}"></script>
        <script src="{% static 'plugins/bootstrap.js' %}"></script>
        {% block css %}
    
        {% endblock %}
    </head>
    <body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
                        aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <img src="{% static '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 '/layout/default.png' %}" alt="" data-toggle="dropdown"
                            aria-haspopup="true" aria-expanded="false">
                    <ul class="dropdown-menu">
                        <li><a href="#">个人中心</a></li>
                        <li><a href="#">修改密码</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">注销</a></li>
                    </ul>
                </div>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#">任务 <i class="fa fa-bell-o" aria-hidden="true"></i> <span class="badge">4</span>
                    </a></li>
                    <li><a href="#">通知 <i class="fa fa-envelope-o" aria-hidden="true"></i> <span class="badge">2</span></a>
                    </li>
                    <li><a href="#">消息 <i class="fa fa-commenting-o" aria-hidden="true"></i> <span
                            class="badge">6</span></a></li>
                </ul>
                <form class="navbar-form navbar-right">
                    <input class="form-control" placeholder="Search..." type="text">
                </form>
            </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 'consumer' %}">客户列表</a></li>
                    <li><a href="{% url 'my_consumer' %}">我的客户</a></li>
                    <li><a href="{% url 'consult_record' 0 %}">我的跟进记录</a></li>
                    <li><a href="{% url 'enrollment' 0 %}">报名记录</a></li>
                    <li><a href="#">Overview</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>
    </body>
    </html>
    

     所有表结构

    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
    import datetime
    
    # Create your models here.
    
    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, 'FALL'),)
    
    
    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, null=True)
        last_consult_date = models.DateField('最后跟进时间', auto_now_add=True, blank=True, null=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()])
    
        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 = models.DateField('结业日期', blank=True, null=True)
        contract = models.ForeignKey('ContractTemplate', verbose_name='选择合同模板', blank=True, null=True)
    
        def __str__(self):
            return '{}{}{}'.format(self.get_course_display(), self.semester, self.campuses)
    
        class Meta:
            unique_together = ('course', 'semester', 'campuses')
    
    
    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', max_length='跟进人', 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='校区')
        enrollment_class = models.ForeignKey('ClassList', verbose_name='所报班级')
    
        class Meta:
            unique_together = ('enrollment_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')
        enrollment_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')
    
    
    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)
    
        class Meta:
            verbose_name = '部门'
            verbose_name_plural = '部门'
    
        def __str__(self):
            return self.name
    
    
    class UserProfile(AbstractBaseUser, PermissionsMixin):
        username = models.EmailField(
            unique=True,
            max_length=255
        )
        is_staff = models.BooleanField(
            _('staff status'),
            default=False,
            help_text=_('Designates whether the user can log into this admin site.')
        )
        is_admin = models.BooleanField(default=False)
        name = models.CharField('名字', max_length=32)
        department = models.ForeignKey('Department', default=None, blank=True, null=True)
        mobile = models.CharField('手机', max_length=32, default=None, blank=True, null=True)
        memo = models.TextField('备注', blank=True, null=True, default=None)
        date_joined = models.DateTimeField(auto_now_add=True)
        USERNAME_FIELD = 'username'
        REQUIRED_FIELDS = ['name']
    
        class Meta:
            verbose_name = '账户信息'
            verbose_name_plural = '账户信息'
    
        def get_full_name(self):
            # The user is identified by their email address
            return self.name
    
        def get_short_name(self):
            # The user is identified by their email address
            return self.username
    
        def __str__(self):  # __unicode__ on Python 2
            return self.username
    
        def has_perm(self, perm, obj=None):
            #     "Does the user have a specific permission?"
            # Simplest possible answer: Yes, always
    
            if self.is_active and self.is_superuser:
                return True
            return _user_has_perm(self, perm, obj)
    
        def has_perms(self, perm_list, obj=None):
            #     "Does the user have a specific permission?"
            # Simplest possible answer: Yes, always
            for perm in perm_list:
                if not self.has_perm(perm, obj):
                    return False
            return True
    
        def has_module_perms(self, app_label):
            #     "Does the user have permissions to view the app `app_label`?"
            #     Simplest possible answer: Yes, always
            if self.is_active and self.is_superuser:
                return True
    
            return _user_has_module_perms(self, app_label)
    
        objects = UserManager()
    

     分页器

    """
    分页器
    """
    from django.utils.safestring import mark_safe
    from django.http import QueryDict
    
    class Pagination:
        def __init__(self, request, all_count, query_params=QueryDict(), per_num=2, max_show=11):
            # 基本的URL
            self.base_url = request.path_info
            # 查询条件
            self.query_params = query_params
            # _mutable = True就可以修改,让urlencode()以后为# query=alex&page=1
            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.all_count = all_count
            # 每页显示的数据条数
            self.per_num = per_num
            # 总页数
            self.total_num, more = divmod(all_count, per_num)
            if more:
                self.total_num += 1
            # 最多显示的页数
            self.max_show = max_show
            self.half_page = max_show // 2
            # 总页码数小于最大显示的页码数:显示总的页码
            if self.total_num <= max_show:
                self.page_start = 1
                self.page_end = self.total_num
            # 总页数大于最大显示的页数:显示最多可显示页码数
            else:
                # 当前页码小于最多可显示页码的一半时,从1显示到最多可显示的页码
                if self.current_page <= self.half_page:
                    self.page_start = 1
                    self.page_end = max_show
                # 当前页码加上最多可显示页码的一半大于总页码时,最多可显示到总页码数
                elif self.current_page + self.half_page >= self.total_num:
                    self.page_start = self.total_num - max_show + 1
                    self.page_end = self.total_num
                # 当当前页面减去(加上)最多可显示页码的一半大于(小于总页码数时)等于1时,正常执行(按照最多可显示的页码数走)
                else:
                    self.page_start = self.current_page - self.half_page
                    self.page_end = self.current_page + self.half_page
    
        @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):
            # 标签列表
            html_list = []
    
            self.query_params['page'] = 1
            # query=alex&page=1
    
            # 首页标签
            first_li = '<li><a href="{0}?{1}">首页</a></li>'.format(self.base_url, self.query_params.urlencode())
            html_list.append(first_li)
    
            # 当前页是否为首页
            if self.current_page == 1:
                pre_li = '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">«</span></a></li>'
            else:
                self.query_params['page'] = self.current_page - 1
                # pre_li = '<li><a href="{1}?page={0}" aria-label="Previous"><span aria-hidden="true">«</span></a></li>'.format(
                pre_li = '<li><a href="{1}?{0}" aria-label="Previous"><span aria-hidden="true">«</span></a></li>'.format(
                    self.query_params.urlencode(), self.base_url)
    
            # 把上一页这个标签加到标签列表中
            html_list.append(pre_li)
    
            # 页面显示的页码标签
            for num in range(self.page_start, self.page_end + 1):
                self.query_params['page'] = num
                if self.current_page == num:
                    num_li = '<li class="active"><a href="{0}?{1}">{2}</a></li>'.format(self.base_url,self.query_params.urlencode(), num)
                else:
                    num_li = '<li><a href="{0}?{1}">{2}</a></li>'.format(self.base_url, self.query_params.urlencode(), num)
                html_list.append(num_li)
            # 判断当前页是否为尾页
            if self.current_page == self.total_num:
                next_li = '<li class="disabled"><a aria-label="Next"><span aria-hidden="true">»</span></a></li>'
            else:
                self.query_params['page'] = self.current_page + 1
                next_li = '<li><a href="{1}?{0}" aria-label="Next"><span aria-hidden="true">»</span></a></li>'.format(
                    self.query_params.urlencode(), self.base_url)
    
            # 把写一页这个标签加到标签列表中
            html_list.append(next_li)
    
            # 尾页标签
            self.query_params['page'] = self.total_num
            last_li = '<li><a href="{1}?{0}">尾页</a></li>'.format(self.query_params.urlencode(), self.base_url)
            html_list.append(last_li)
    
            # 把列表转化成字符串
            return mark_safe(''.join(html_list))
    

     views.py中的注册登录以及客户类|方法等

    from django.shortcuts import render, redirect, reverse, HttpResponse
    from django.contrib import auth
    from django.utils.safestring import mark_safe
    from utils.pagination import Pagination
    from crm import models
    from crm.forms import RegForm, ConsumerForm, ConsultRecordForm, EnrollmentForm
    from django.views import View
    from django.db.models import Q
    from django.http import QueryDict
    
    
    # Create your views here.
    
    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('consumer'))
            err_msg = '用户或密码错误'
        return render(request, 'login.html')
    
    
    # 注册
    def register(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, 'register.html', {'form_obj': form_obj})
    
    
    # # 展示客户
    # def consumer_list(request):
    #     if request.path_info == reverse('consumer'):
    #         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/consumer_list.html',
    #                   {'all_customer': all_customer[page.start:page.end],
    #                    'pagination': page.show_li})
    
    
    # 展示客户列表CBV
    class ConsumerList(View):
    
        def get(self, request):
    
            q = self.get_search_contion(['qq', 'name', 'last_consult_date'])
    
            if request.path_info == reverse('consumer'):
                # all_customer = models.Customer.objects.filter(consultant__isnull=True)
                all_customer = models.Customer.objects.filter(q, consultant__isnull=True)
            else:
                # all_customer = models.Customer.objects.filter(consultant=request.user)
                all_customer = models.Customer.objects.filter(q, consultant=request.user)
    
            # query_params = copy.deepcopy(request.GET)  # <QueryDict: {'query': ['alex']}>
            query_params = request.GET.copy()  # <QueryDict: {'query': ['alex']}>
            # query_params = request.GET  # <QueryDict: {'query': ['alex']}>
            #
            # query_params['page'] = 1  # <QueryDict: {'query': ['alex'],'page': ['1']}>
            # query=alex&page=1
    
            page = Pagination(request, all_customer.count(), query_params)
    
            add_btn, query_params = self.get_add_btn()
    
            return render(request, 'crm/consumer_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):
            # 处理post提交的action的动作
            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_pri(self):
            # 获取选择的数据的ID
            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))
    
        # 私户变公户
        def multi_pub(self):
            # 获取选择数据的ID
            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'
            # q.children.append(Q(('qq__contains', query)))
            # q.children.append(Q(('name__contains', query)))
            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="{}?{}" class="btn btn-info">添加</a>'.format(reverse('add_consumer'), query_params)
            return mark_safe(add_btn), query_params
    
    
    # 增加客户
    def add_consumer(request):
        # 实例化一个空的form对象
        form_obj = ConsumerForm()
        if request.method == 'POST':
            # 实例化一个带提交数据的form对象
            form_obj = ConsumerForm(request.POST)
            # 对提交的数据进行校验
            if form_obj.is_valid():
                # 创建对象
                form_obj.save()
                return redirect(reverse('consumer'))
        return render(request, 'crm/add_consumer.html', {'form_obj': form_obj})
    
    
    # 编辑客户
    def edit_consumer(request, edit_id):
        # 根据ID查出需要编辑的客户对象
        obj = models.Customer.objects.filter(id=edit_id).first()
        # 默认为None实例化一个对象
        form_obj = ConsumerForm(instance=obj)
        if request.method == 'POST':
            # 将提交的数据和要修改的实例交给form对象
            form_obj = ConsumerForm(request.POST, instance=obj)
            # 如果通过验证则进行保存到数据库
            if form_obj.is_valid():
                form_obj.save()
                # 反向解析跳转到客户可列表
                return redirect(reverse('consumer'))
        return render(request, 'crm/edit_consumer.html', {'form_obj': form_obj})
    
    
    # 新增和编辑客户
    def consumer(request, edit_id=None):
        obj = models.Customer.objects.filter(id=edit_id).first()
        form_obj = ConsumerForm(instance=obj)
        if request.method == 'POST':
            form_obj = ConsumerForm(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('consumer'))
        return render(request, 'crm/consumer.html', {'form_obj': form_obj, 'edit_id': edit_id})
    

     url.py中的路由匹配等

    from django.conf.urls import url
    from django.contrib import admin
    from crm.views import ConsumerList, ConsultRecordList,EnrollmentList
    from crm import views
    
    urlpatterns = [
        # 公户
        url(r'^consumer_list/', ConsumerList.as_view(), name='consumer'),
        # 私户
        url(r'^my_consumer/', ConsumerList.as_view(), name='my_consumer'),
        # # 增加客户
        # url(r'^add_consumer/', views.add_consumer, name='add_consumer'),
        # # 编辑客户
        # url(r'^edit_consumer/(d+)', views.edit_consumer, name='edit_consumer'),
        # 增加客户
        url(r'^add_consumer/', views.consumer, name='add_consumer'),
        # 编辑客户
        url(r'^edit_consumer/(d+)', views.consumer, name='edit_consumer'),
    
    
    # name的意义在于用来反向解析时用到
    

     forms.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 field in self.fields.values():
                # 给所有的字段增加class这个属性
                field.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 __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('两次密码不一致')
    
    
    # 客户form
    class ConsumerForm(BaseForm):
        class Meta:
            model = models.Customer
            fields = '__all__'
            widgets = {
                'course': forms.widgets.SelectMultiple
            }
    
        # def __init__(self, *args, **kwargs):
        #     super(ConsumerForm, self).__init__(*args, **kwargs)
        #     for field in self.fields.values():
        #         field.widget.attrs.update({'class': 'form-control'})
    

     展示客户列表HTML

    {% extends 'layout.html' %}
    
    {% block css %}
        <style>
            th, tr {
                text-align: center;
            }
        </style>
    {% endblock %}
    
    
    {% block content %}
        <div class="panel panel-primary">
            <div class="panel-heading">客户列表</div>
            <div class="panel-body">
                <div class="form-group">
                    {#                <a href="{% url 'add_consumer' %}" class="btn btn-info">添加</a>#}
                    <a href="{% url 'add_consumer' %}?{{ query_params }}" class="btn btn-primary">添加</a>
                    {#                {{ add_btn }}#}
                </div>
                <div>
                    <form action="" class="form-inline pull-right">
                        <input type="text" name="query" class="form-control">
                        <button class="btn btn-primary">搜索 <i class="fa fa-search"></i></button>
                    </form>
                </div>
                <div class="form-group">
                    <form action="" class="form-inline" method="post">
                        {% csrf_token %}
                        <select name="action" class="form-control" style="margin-bottom: 10px;">
                            <option value="">请选择</option>
                            <option value="multi_delete">删除</option>
                            <option value="multi_pri">放入私户</option>
                            <option value="multi_pub">放入公户</option>
                        </select>
                        <button class="btn btn-success" style="margin-bottom: 10px;">提交</button>
                        <table class="table table-bordered table-condensed table-hover ">
                            <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>
                                <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.birthday }}</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_consumer' customer.id %}?{{ query_params }}">
                                        <i class="fa fa-edit fa-fw"></i></a></td>
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </form>
                </div>
    
                <div style="text-align: center">
                    <nav aria-label="Page navigation">
                        <ul class="pagination">
                            {{ pagination }}
                        </ul>
                    </nav>
                </div>
            </div>
        </div>
    
    {% endblock %}
    

     增加和编辑客户合在一起的写法HTML

    {% extends 'layout.html' %}
    
    {% block content %}
        <div class="panel panel-primary">
            <div class="panel-heading">
                {% if edit_id %}
                    编辑客户
                    {% else %}
                    新增客户
                {% endif %}
            </div>
            <div class="panel-body">
                <div class="col-sm-8 col-sm-offset-2">
                    <form action="" method="post" novalidate>
                        {% csrf_token %}
                        {% for field in form_obj %}
                            <div class="form-group row {% if field.errors %}has-error{% endif %}">
                                <label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label>
                                <div class="col-sm-10">
                                    {{ field }}
                                    <span class="help-block">
                                        {{ field.errors.0 }}
                                    </span>
                                </div>
                            </div>
                        {% endfor %}
                        <div class="form-group">
                            <div class="col-sm-offset-2 col-sm-10">
                                <button type="submit" class="btn btn-primary">提交</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    {% endblock %}
    

     关于客户操作的一些知识点

    url中需知

    # 公户
    url(r'^consumer_list/', ConsumerList.as_view(), name='consumer'),
    # 私户
    url(r'^my_consumer/', ConsumerList.as_view(), name='my_consumer'),
    
    
    
    # name存在的意义在于反向解析时使用可以定位到 ConsumerList.as_view()这里,再去调用 ConsumerList.as_view()指向的函数。
    #  ConsumerList.as_view()是当需要调用的函数封装到类中的时候这样写,正常为views.consumer_list
    # 这里consumer_list为views中的函数名
    

     veiws中需知

    request.path_info  # 获取路径信息 当路径为http://127.0.0.1:8001/crm/enrollment_list/?page=1时,只会获取/crm/enrollment_list/这个部分不包含脚本前缀也不包含参数
    
    
    filter(q, consultant__isnull=True) # 支持多个条件的筛选、过滤
    
    
    query_params = request.GET.copy() # 修改时不会改变源码中的数值
    query_params['page'] = 1  # <QueryDict: {'query': ['alex'],'page': ['1']}>这是固定的方法
    
    
    qd = QueryDict()
    qd._mutable = True  # 改为True时对QueryDict进行修改
    qd['next'] = url
    query_params = qd.urlencode() # 将<QueryDict: {'query': ['alex'],'page': ['1']}>变为query=alex&page=1
    
    
    models.Customer.objects.filter(id__in=ids).update(consultant=None) # 把筛选出来的数据中的consultant字段更新为consultant=None
    
    
    request.get_full_path() -- 获取当前url,(包含参数)
    请求一个http://127.0.0.1:8000/200/?type=10
    request.get_full_path()返回的是【/200/?type=10】
    request.path --  获取当前url,(但不含参数)
    request.path返回的是 【/200/】
    
    
    from django.utils.safestring import mark_safe
    mark_safe(add_btn) # 不让add_btn中的内容进行转义正常当做标签来显示
    
    
    # 实例化一个带提交数据的form对象
    form_obj = ConsumerForm(request.POST)
    
    
    # 与数据库做校验(登录)
    obj = auth.authenticate(request, username=username, password=password)
    
    
    # 记录当前用户的登录状态
    auth.login(request, obj)
    
    
    # 设置密码
    obj.set_password(obj.password)
    
    
    # 将公户变为私户(add中放的必须是对象)
    self.request.user.customers.add(*models.Customer.objects.filter(id__in=ids))
    
        class Meta: # 写在类的后面(内部)
            verbose_name = '客户' # 修改admin中的显示名称会在后面加s
            verbose_name_plural = '客户' # 彻底修改admin中的显示名称
    
        def __str__(self):
            return '{}{}{}'.format(self.get_course_display(),self.semester,self.campuses)
    
    # 让admin中显示的内容为中文
    
        def __init__(self, *args, **kwargs):
            super(Consumer, self).__init__(*args, **kwargs)
            for field in self.fields.values():
                field.widgets.attrs.update({'class': 'form-control'})
    
    # 给所有的字段(也就是标签)添加属性class= form-control
    
  • 相关阅读:
    代码中回调的用法
    关于导入外部样式表的目录问题
    js操作元素样式
    JavaSE学习总结第10天_面向对象5
    JavaSE学习总结第09天_面向对象4
    JavaSE学习总结第08天_面向对象3
    JavaSE学习总结第07天_面向对象2
    JavaSE学习总结第06天_Java语言基础2 & 面向对象1
    JavaSE学习总结第05天_Java语言基础1
    JavaSE学习总结第03天_Java基础语法2
  • 原文地址:https://www.cnblogs.com/wjs521/p/9839073.html
Copyright © 2020-2023  润新知