精华内容
下载资源
问答
  • Django权限管理系统设计分析

    千次阅读 2019-07-14 17:40:58
    权限管理顾名思义,其实就是角色控制权限的系统,每个用户对应一个角色,每个角色有对应的权限,比如公司会有CEO,总监,销售经理,销售员,每个人的权限都不一样,那我们给他展示的url也都不同 一、首先创建项目,...

    权限管理顾名思义,其实就是角色控制权限的系统,每个用户对应一个角色,每个角色有对应的权限,比如公司会有CEO,总监,销售经理,销售员,每个人的权限都不一样,那我们给他展示的url也都不同

    一、首先创建项目,再创建一个名为rbac的app

      修改配置文件settings,将css以及js、img等放到static文件夹下

    二、表结构设计

     设计表:

      创建五个类,七张表:

      菜单表,权限组,权限表,用户表,角色表,

    角色表和权限表是多对多的关系(一个角色可以有多个权限,一个权限可以对应多个角色)

    用户表和角色表是多对多的关系(一个用户可以有多个角色,一个角色有多个用户)

     

    models.py

     

    我们一般是先看到的是列表页面,在这个页面上是否显示添加,是否显示编辑,是否显示删除,都是需要判断的, 有无添加权限,有无删除权限,有无编辑权限,我们可以给每一个url一个代号,将代号取出来放在一个列表里面

     

    把这个字典存到session中当你访问页面的时候我就知道你有什么权限一个url对应一个code 多个url对应一个组.

    三、通过Django-admin录入数据

    先创建一个超级用户:python3 manage.py createsuperuser

    用户名:root

    密码:a123456

     在admin.py中做如下配置:

     

    models.py中做如下配置,就可以在添加的时候显示中文了。

     

     

     

     

     四、编写登录

      利用Django的中间件进行控制,没有登录的用户不能直接访问内部的url,而且只能访问admin,login,index这三个url,将其设置为白名单,用户登录成功后,将用户信息方法哦session里边,进行权限访问时去session里边读取,如果有的话进行url跳转,如果没有的话返回‘’无权访问‘’

       1.在settings内设置白名单:

      2.编写中间件控制时,必须继承MiddlewareMixin这个类

     

    展开全文
  • 开发环境: Pycharm + Python3.7 + Django2.2 + sqlite,可以转mysql数据库 管理员登录后可以添加客户信息,查询修改客户信息;添加药品信息,查询修改药品信息;添加药品销售订单信息;查询和删除订单等!添加订单...

    开发环境: Pycharm + Python3.7 + Django2.2 + sqlite,可以转mysql数据库

    管理员登录后可以添加客户信息,查询修改客户信息;添加药品信息,查询修改药品信息;添加药品销售订单信息;查询和删除订单等!添加订单的时候可以选择好客户,然后可以同时添加多个药品,每个药品数量自定义的,界面设计很方便合理呢!

    登录地址: http://localhost:8000/mgr/sign.html
    账户密码: manage/123456 (注意:这个项目添加新用户需要手动创建管理员,注册功能无效的哈)

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • Django-ERPDjango-ERP是一款基于Django开发的ERP管理软件,包含常用的销售管理、采购管理、库存管理、组织管理等,支持按项目归集费用,支持工作流审批,支持采购单、报价单的批量导入。Forked from zhuinfo Django-...

    Django-ERP

    Django-ERP是一款基于Django开发的ERP管理软件,包含常用的销售管理、采购管理、库存管理、组织管理等,支持按项目归集费用,支持工作流审批,支持采购单、报价单的批量导入。

    Forked from zhuinfo Django-ERP 感谢他的付出。

    安装指南

    我的开发、测试环境是Python2.7的,所以这个文档大多数情况我默认会使用这个条件,有个别测试不到位的可能还需要慢慢完善。

    请先确保您已安装了Python 2.7,并已配置好了数据库,本文档会略过这部分内容(理论上Django是可以支持MYSql、PGSQL、SQLite、Oracle等主流数据库的,但是建议不要嘬,用自己熟悉的数据库,因为数据是无价的。)

    验证方法请通过python --version查看版本,以及数据库 确认用户名和密码是否登录正常

    后续我争取不上requirements.txt,远期目标我个人是希望能做个dock镜像,并让它能慢慢顺着Python3和新版本的django平滑过渡上去。

    数据库配置

    数据库配置项在mis/settings.py文件中

    在88-96行为Mysql数据库配置

    DATABASES = {

    'default': {

    'ENGINE': 'django.db.backends.mysql',#MYSQL类型数据库,Python会依赖MySQL的驱动器

    'HOST': 'localhost',#数据库主机地址

    'NAME': 'mis',#数据库名称

    'USER': 'root',#数据库用户名(建议不要加入root敢死队)

    'PASSWORD': 'root',#数据库密码

    }

    }

    克隆代码

    导入数据库

    mysql -uroot -proot mis < Install/mis.sql

    运行测试服务器

    python manage.py runserver

    修改管理员账户密码

    C:\Django-ERP>python manage.py changepassword admin

    You have 3 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth.

    Run 'python manage.py migrate' to apply them.

    Changing password for user 'admin'

    Password:

    Password (again):

    Password changed successfully for user 'admin'

    C:\Django-ERP>

    排错

    MYSQL驱动错误

    django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named MySQLdb

    出现No module named MySQLdb是django找不到MySQL驱动导致的问题,所以需要先安装一个数据库驱动。

    展开全文
  • 通常所指的CRM,指用计算机自动化分析销售、市场营销、客户服务以及应用等流程的软件系统。它的目标是通过提高客户的价值、满意度、赢利性和忠实度来缩减销售周期和销售成本、增加收入、寻找扩展业务所需的新的市场...

    CRM介绍:

      CRM即客户关系管理,是指企业用CRM技术来管理与客户之间的关系。在不同场合下,CRM可能是一个管理学术语,可能是一个软件系统。通常所指的CRM,指用计算机自动化分析销售、市场营销、客户服务以及应用等流程的软件系统。它的目标是通过提高客户的价值、满意度、赢利性和忠实度来缩减销售周期和销售成本、增加收入、寻找扩展业务所需的新的市场和渠道。CRM是选择和管理有价值客户及其关系的一种商业策略,CRM要求以客户为中心的企业文化来支持有效的市场营销、销售与服务流程。

    本次CRM项目的需求以及特点:

      本次项目的特点基于教学系统,在此基础上进行的开发,不仅有传统意义CRM的功能,还具备了一些扩展的功能,目的旨在提高效率,解决办公上的一些痛点问题.

      需求:

        1, 不同的用户使用该系统, 显示不同的展示页面.

        2, 解决销售在联系客户时产生的冲突.

        3, 客户(学员)可以实时查看自己的有关信息,以及课程进度

        4, 老师可以布置作业,给自己所在的班级的同学评分.

        ...

      掌握的知识:

        1, python

        2, Django框架的使用

        3, bootstrap的使用

        4, jQuery的使用

        5, ajax

        6, HTML/CSS/JS

        ...

    花里胡哨的说这么多,下面一步一步来吧!!!

    第一部分: 创建crm_system的Django项目, 并完成基础的配置

    第一步:

    下载安装Django

    pip install django==1.11.15

    第二步:

    打开pycharm创建一个项目

    第三步:

    配置settings.py文件

      1, 手动创建static文件,用于存放静态文件

      2, settings文件的配置

    """
    Django settings for blog_crm project.
    
    Generated by 'django-admin startproject' using Django 1.11.15.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/1.11/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/1.11/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = 'xx%re+j4h-@mwr_%u8c@46im%m==e877jadvqz@4lszx*fl!33'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = []
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'crm_manage.apps.CrmManageConfig',  # 注册的app
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',  # csrf中间件
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'blog_crm.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')]
            ,
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'blog_crm.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': "blog",
            'USER': 'root',
            'PASSWORD': '123456',
            'HOST': 'localhost',
            'PORT': 3306,
    
        }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/1.11/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    
    STATIC_URL = '/static/'  # 静态文件的别名
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, "static")
    ]
    
    AUTH_USER_MODEL = "crm_manage.UserProfile"  # 不是用admin提供的表,自己对auth的表进行扩展后的表名.规则: app.表名
    
    LOGIN_URL = " login"  # 不使用Django中自己跳转的/account/login,重新配置为login(自己创建的)
    settings.py的配置

      3, 使用MySQL,使用pymysql

    # settings文件中配置完成后.
    # 1, 在settings同级目录下的__init__文件中导入pymysql模块
    import pymysql
    pymysql.install_as_MySQLdb()

      4,创建库,建表

    # Django不能创建库
    # 1, 打开CMD,进入mysql
    mysql -uroot -p
    
    # 2, 创建数据库
    create database crm_data;

      5, 在app下的models中创建项目用的表(表结构复杂,不贴出来了)

    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 multiselectfield import MultiSelectField
    from django.utils.translation import ugettext_lazy as _
    
    course_choices = (('LinuxL', "Linux中高级"),
                      ("PythonFullStack", "Python高级全栈开发"),
                      )
    
    class_type_choices = (("fulltime", "脱产班",),
                          ("online", "网络班"),
                          ("weekend", "周末班"),
                          )
    
    source_type = (("qq", "qq群"),
                   ("referral", "内部转介绍"),
                   ("website", "官方网站"),
                   ("baidu_ads", "百度推广"),
                   ("WoM", "口碑"),
                   ("public_class", "公开课"),
                   ("website_luffy", "路飞官网"),
                   ("others", "其他"),
                   )
    
    enroll_status_choices = (("signed", "已报名"),
                             ("unregistered", "未报名"),
                             ("studying", "学习中"),
                             ("paid_in_full", "学费已交齐")
                             )
    
    seek_status_choices = (('A', '近期无报名计划'), ('B', '一个月内包名'),
                           ('C', '2周内报名'), ('D', '1周内报名'),
                           ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '无效'))
    
    pay_type_choices = (('deposit', "订金/报名费"),
                        ("tuition", "学费"),
                        ("transfer", "转班"),
                        ("dropout", "退学"),
                        ("refund", "退款"),
                        )
    
    attendance_choice = (("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, help_text=True)
        status = models.CharField("状态", choices=enroll_status_choices, max_length=64, default="unregistered",
                                  help_text="选择客户此时的状态")
        network_cosult_note = models.TextField(blank=True, null=True, verbose_name="网络咨询师咨询内容")
        date = models.DateTimeField("咨询日期", auto_now_add=True)
        last_consult_date = models.DateField("最后跟进日期", blank=True, null=True)
        next_date = models.DateField("预计再次跟进事件", blank=True, null=True)
        private = models.BooleanField(verbose_name="私人客户", default=True)
        # 关联对象
        network_consultant = models.ForeignKey("UserProfile", blank=True, null=True, verbose_name="咨询师")
        consultant = models.ForeignKey("UserProfile", verbose_name="销售", related_name="consultant")
        class_list = models.ManyToManyField("ClassList", verbose_name="已报班级")
    
        def __str__(self):
            return "{}+{}".format(self.name, self.qq)
    
    
    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 self.course
    
    
    class ConsultRecord(models.Model):
        """
        跟进记录
        """
        consultant = models.ForeignKey("Customer", verbose_name="所咨询客户")
        note = models.TextField(verbose_name="跟进内容...")
        status = models.CharField("跟进状态", max_length=8, choices=seek_status_choices, help_text="选择客户此时的状态")
        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("我已经认真阅读完培训协议并同意全部协议内容")
        contract_approved = models.BooleanField("审批通过", help_text="在审批完学员的资料无误后勾选此项,合同即生效")
        enrolled_date = models.DateTimeField(auto_now_add=True, verbose_name="报名日期")
        memo = models.TextField("备注", blank=True, null=True)
        delete_status = models.ForeignKey("Customer", verbose_name="客户名称")
        school = models.ForeignKey('Campuses')
        enrolment_class = models.ForeignKey("ClassList", verbose_name="所报班级")
    
    
    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)
        delete_status = models.BooleanField(verbose_name="删除状态", default=False)
        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="销售")
    
    
    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)
        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_choice, 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 UserManage(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 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_active = models.BooleanField(default=True)
        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 = UserManage()
    models下创建表

      6, 执行数据迁移的命令

    # 记录数据表有哪些改变
    python manage.py makemigrations
    # 在数据库中真正写入数据
    python manage.py migrate

     

    第二部分:完成登陆注册功能

    第一步:设计url,创建html页面

     

    第二步:

    在app中创建myforms.py文件,创建自定义的form表单

    第三步:自定义form表单

    #  myforms.py

    # ! /usr/bin/env python3.6
    # -*- coding: utf-8 -*-
    # 2018/9/25 20:28
    
    
    from crm_manage import models
    from django import forms
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    
    
    def check(value):
        if "alex" in value:
            raise ValidationError("含有敏感字符")
    
    
    class LoginForm(forms.Form):
        username = forms.CharField(
            label="用户名",
            min_length=5,
            max_length=20,
            # initial="张三",
            required=True,
            validators=[check, ],
            widget=widgets.TextInput(attrs={"class": "form-control", "placeholder": "Email address"}),
            error_messages={"min_length": "用户名最少是5位", "max_length": "用户名最长不能超过20位"}
        )
        pwd = forms.CharField(
            label="密码",
            min_length=8,
            required=True,
            widget=widgets.PasswordInput(attrs={"class": "form-control", "placeholder": "Password"}),
            error_messages={"min_length": "密码最少需要8位"}
        )
    
    
    class RegForm(forms.ModelForm):
        # 还可以添加ModelForm中没有的字段
        re_password = forms.CharField(label="确认密码", widget=forms.PasswordInput(attrs={"class": "form-control"}))
    
        class Meta:
            model = models.UserProfile
            # 使用所有的字段
            fields = "__all__"
            # fields = ["username", "password"]
            # 派出列表中的字段
            exclude = ["is_active"]
            labels = {
                "username": "用户名",
                "name": "真实姓名",
                "password": "密码",
                "department": "部门",
            }
            widgets = {
                "username": forms.widgets.TextInput(attrs={"class": "form-control"}),
                "password": forms.widgets.PasswordInput(attrs={"class": "form-control"}),
            }
    
        # labels = {
        #     "username": "用户名",
        #     "password": "密码",
        # }
    
        # 对每个字段添加属性, self.fields是一个有序的字典, self.fields.values()获取出每个字段的values值,即每个对象,
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            for field in self.fields.values():
                field.widget.attrs.update({"class": "form-control"})
    
        # 校验两次密码是否一致
        def clean(self):
            if self.cleaned_data.get("password") != self.cleaned_data.get("re_password"):
                self.add_error("re_password", "两次密码不一致")
                raise ValidationError("两次密码不一致")
            return self.cleaned_data
    自定义forms

    第四步:视图函数中使用自定义的form

    # views.py

    from django.shortcuts import render, redirect
    from . import forms
    from django.contrib import auth
    from django.contrib.auth.decorators import login_required
    from crm_manage.forms import RegForm
    from . import models
    
    
    def login(request):
        msg = ''
        loginForm = forms.LoginForm()
        if request.method == "POST":
            loginForm = forms.LoginForm(request.POST)
            username = request.POST.get('username')
            pwd = request.POST.get("pwd")
            obj = auth.authenticate(request, username=username, password=pwd)
            if obj:
                auth.login(request, obj)
                return redirect("/index/")
            else:
                msg = "用户名密码错误"
        return render(request, "login.html", {"loginForm": loginForm, "msg": msg})
    
    
    def regForm(request):
        reg_obj = RegForm()
        if request.method == "POST":
            reg_obj = RegForm(request.POST)
            if reg_obj.is_valid():
                # 数据库中写入数据
                # 第一种方法
                # reg_obj.cleaned_data.pop("groups")
                # reg_obj.cleaned_data.pop("user_permissions")
                # reg_obj.cleaned_data.pop("re_password")
                # models.UserProfile.objects.create_user(**reg_obj.cleaned_data)
    
                # 第二种方法(此方法写入数据库中的密码是明文,所以多了一步设置密码的操作)
                password = reg_obj.cleaned_data.get("password")
                user = reg_obj.save()
                user.set_password(password)
                user.save()
    
                return redirect("/login/")
        return render(request, "reg.html", {"reg_obj": reg_obj})
    
    
    @login_required
    def index(request):
        return render(request, "index.html")
    
    
    def logout(request):
        auth.logout(request)
        return redirect("/login/")
    
    
    @login_required
    def control(request):
        customers = models.Customer.objects.all()
        return render(request, "control.html", {"customers": customers})
    视图函数

    第五步:

    前端中展示

    <!DOCTYPE html>
    <html lang="zh-CN">
    <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标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
        <title>Bootstrap 101 Template</title>
        <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    
    </head>
    <body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-4 col-md-offset-4">
                <form class="form-signin" method="post" novalidate>
                    {% csrf_token %}
                    <h2 class="form-signin-heading">欢迎登陆</h2>
                    <label for="inputEmail" class="sr-only">Email address</label>
                    {{ loginForm.username }}
                    <br>
                    <label for="inputPassword" class="sr-only">{{ loginForm.pwd.label }}</label>
                    {{ loginForm.pwd }}
                    <div class="checkbox">
                        <label>
                            <input type="checkbox" value="remember-me"> Remember me
                        </label>
                    </div>
                    <button class="btn btn-lg btn-primary btn-block" type="submit">登陆</button>
                    <br>
                    <a href="/reg/">
                        <button class="btn btn-lg btn-primary btn-block" type="button">注册</button>
                    </a>
                </form>
            </div>
        </div>
    </div>
    </body>
    </html>
    登陆页面
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    
    </head>
    <body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form action="" class="form-horizontal" novalidate method="post">
                    {% csrf_token %}
                    <h2 class="form-signin-heading">注册</h2>
                    <div class="form-group {% if reg_obj.username.errors.0 %}has-error{% endif %}">
                        <label for="{{ reg_obj.username.id_for_label }}" class="col-sm-2 control-label">
                            {{ reg_obj.username.label }}
                        </label>
                        <div class="col-sm-10">
                            {{ reg_obj.username }}
                        </div>
                        <span id="helpBlock2" class="help-block">{{ reg_obj.username.errors.0 }}</span>
                    </div>
    
                    <div class="form-group {% if reg_obj.name.errors.0 %}has-error{% endif %}">
                        <label for="{{ reg_obj.name.id_for_label }}" class="col-sm-2 control-label">
                            {{ reg_obj.name.label }}
                        </label>
                        <div class="col-sm-10">
                            {{ reg_obj.name }}
                        </div>
                        <span id="helpBlock2" class="help-block">{{ reg_obj.name.errors.0 }}</span>
                    </div>
    
                    <div class="form-group {% if reg_obj.password.errors %}has-error{% endif %}">
                        <label for="{{ reg_obj.password.id_for_label }}" class="col-sm-2 control-label">
                            {{ reg_obj.password.label }}
                        </label>
                        <div class="col-sm-10">
                            {{ reg_obj.password }}
                        </div>
                        <span id="helpBlock2" class="help-block">{{ reg_obj.password.errors.0 }}</span>
                    </div>
    
                    <div class="form-group {% if reg_obj.re_password.errors %}has-error{% endif %}">
                        <label for="{{ reg_obj.password.id_for_label }}" class="col-sm-2 control-label">
                            {{ reg_obj.re_password.label }}
                        </label>
                        <div class="col-sm-10">
                            {{ reg_obj.re_password }}
                        </div>
                        <span id="helpBlock2" class="help-block">{{ reg_obj.re_password.errors.0 }}</span>
                    </div>
    
                    <div class="form-group">
                        <label for="{{ reg_obj.department.id_for_label }}" class="col-sm-2 control-label">
                            {{ reg_obj.department.label }}
                        </label>
                        <div class="col-sm-10">
                            {{ reg_obj.department }}
                        </div>
                    </div>
    
                    <button class="btn btn-lg btn-primary btn-block" type="submit">提交</button>
                </form>
            </div>
        </div>
    </div>
    </body>
    </html>
    注册页面
    <!doctype html>
    <html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1, user-scalable=no">
        <title>实名认证</title>
        <link href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" title="" rel="stylesheet">
        <link title="" href="/static/css/style.css" rel="stylesheet" type="text/css">
        <link title="blue" href="/static/css/dermadefault.css" rel="stylesheet" type="text/css">
        <link href="/static/css/templatecss.css" rel="stylesheet" title="" type="text/css">
        <script src="/static/jquery/jquery-1.10.2.js"></script>
        <script src="/static/js/jquery.cookie.js" type="text/javascript"></script>
        <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js" type="text/javascript"></script>
    </head>
    <body style="">
    <nav class="nav navbar-default navbar-mystyle navbar-fixed-top">
        <div class="navbar-header">
            <button class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand mystyle-brand"><span class="glyphicon glyphicon-home"></span></a></div>
        <div class="collapse navbar-collapse">
            <ul class="nav navbar-nav">
                <li class="li-border"><a class="mystyle-color" href="#">管理控制台</a></li>
            </ul>
            <ul class="nav navbar-nav pull-right">
                <li class="li-border">
                    <a href="#" class="mystyle-color">
                        <span class="glyphicon glyphicon-bell"></span>
                        <span class="topbar-num">0</span>
                    </a>
                </li>
                <li class="li-border dropdown"><a href="#" class="mystyle-color" data-toggle="dropdown">
                    <span class="glyphicon glyphicon-search"></span> 搜索</a>
                    <div class="dropdown-menu search-dropdown">
                        <div class="input-group">
                            <input type="text" class="form-control">
                            <span class="input-group-btn">
    <button type="button" class="btn btn-default">搜索</button>
    </span>
                        </div>
                    </div>
                </li>
                <li class="dropdown li-border"><a href="#" class="dropdown-toggle mystyle-color" data-toggle="dropdown">帮助与文档<span
                        class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">帮助与文档</a></li>
                        <li class="divider"></li>
                        <li><a href="#">论坛</a></li>
                        <li class="divider"></li>
                        <li><a href="#">博客</a></li>
                    </ul>
                </li>
                {#            <li class="dropdown li-border"><a href="#" class="dropdown-toggle mystyle-color" data-toggle="dropdown">605875855@qq.com<span#}
                {#                    class="caret"></span></a>#}
                {#                <ul class="dropdown-menu">#}
                {#                    <li><a href="#">退出</a></li>#}
                {#                </ul>#}
                {#            </li>#}
                <li class="li-border"><a href="/login/" class="mystyle-color">登陆</a></li>
            </ul>
        </div>
    </nav>
    <div class="down-main">
        <div class="left-main left-off">
            <div class="sidebar-fold"><span class="glyphicon glyphicon-menu-hamburger"></span></div>
            <div class="subNavBox">
                <div class="sBox">
                    <div class="subNav"><span class="title-icon glyphicon glyphicon-chevron-up"></span><span
                            class="sublist-title">用户中心</span>
                    </div>
                    <ul class="navContent" style="display: block;">
                        <li>
                            <div class="showtitle" style="width: 100px; display: none;"><img src="/static/img/leftimg.png">账号管理
                            </div>
                            <a href=""><span class="sublist-icon glyphicon glyphicon-user"></span><span
                                    class="sub-title">账号管理</span></a></li>
                        <li>
                            <div class="showtitle" style="width: 100px; display: none;"><img src="/static/img/leftimg.png">消息中心
                            </div>
                            <a href=""><span class="sublist-icon glyphicon glyphicon-envelope"></span><span
                                    class="sub-title">消息中心</span></a></li>
                        <li>
                            <div class="showtitle" style="width:100px;"><img src="/static/img/leftimg.png">短信</div>
                            <a href=""><span class="sublist-icon glyphicon glyphicon-bullhorn"></span><span
                                    class="sub-title">短信</span></a></li>
                        <li class="active">
                            <div class="showtitle" style="width: 100px; display: none;"><img src="/static/img/leftimg.png">实名认证
                            </div>
                            <a href=""><span class="sublist-icon glyphicon glyphicon-credit-card"></span><span
                                    class="sub-title">实名认证</span></a></li>
                    </ul>
                </div>
            </div>
        </div>
        <div class="right-product view-product right-off">
            <div class="table-responsive">
                {% block main_info %}
                    <table class="table table-striped">
                        <thead>
                        <tr>
    {#                        <th>序号</th>#}
                            <th>ID</th>
                            <th>姓名</th>
                            <th>qq</th>
                            <th>性别</th>
                            <th>客户来源</th>
                            <th>咨询课程</th>
                            <th>最后一次咨询时间</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for customer in customers %}
                            {% if customer.private == 0 %}
                                <tr>
    {#                                <td>{{ forloop.counter }}</td>#}
                                    <td>{{ customer.id }}</td>
                                    <td>{{ customer.name }}</td>
                                    <td>{{ customer.qq }}</td>
                                    <td>{{ customer.sex }}</td>
                                    <td>{{ customer.source }}</td>
                                    <td>{{ customer.course }}</td>
                                    <td>{{ customer.last_consult_date }}</td>
                                </tr>
                            {% endif %}
    
                        {% endfor %}
    
                        </tbody>
                    </table>
                    <a href="/logout/"><button class="btn btn-block">注销</button></a>
                {% endblock %}
    
            </div>
        </div>
    </div>
    <script type="text/javascript">
        $(function () {
            /*左侧导航栏显示隐藏功能*/
            $(".subNav").click(function () {
                /*显示*/
                if ($(this).find("span:first-child").attr('class') == "title-icon glyphicon glyphicon-chevron-down") {
                    $(this).find("span:first-child").removeClass("glyphicon-chevron-down");
                    $(this).find("span:first-child").addClass("glyphicon-chevron-up");
                    $(this).removeClass("sublist-down");
                    $(this).addClass("sublist-up");
                }
                /*隐藏*/
                else {
                    $(this).find("span:first-child").removeClass("glyphicon-chevron-up");
                    $(this).find("span:first-child").addClass("glyphicon-chevron-down");
                    $(this).removeClass("sublist-up");
                    $(this).addClass("sublist-down");
                }
                // 修改数字控制速度, slideUp(500)控制卷起速度
                $(this).next(".navContent").slideToggle(300).siblings(".navContent").slideUp(300);
            });
            /*左侧导航栏缩进功能*/
            $(".left-main .sidebar-fold").click(function () {
    
                if ($(this).parent().attr('class') == "left-main left-full") {
                    $(this).parent().removeClass("left-full");
                    $(this).parent().addClass("left-off");
    
                    $(this).parent().parent().find(".right-product").removeClass("right-full");
                    $(this).parent().parent().find(".right-product").addClass("right-off");
    
    
                }
                else {
                    $(this).parent().removeClass("left-off");
                    $(this).parent().addClass("left-full");
    
                    $(this).parent().parent().find(".right-product").removeClass("right-off");
                    $(this).parent().parent().find(".right-product").addClass("right-full");
    
    
                }
            });
        })
    </script>
    
    
    </body>
    </html>
    主页面

    部分页面效果展示:

    登陆页面:

    注册页面:

     

    第三部分:完成主页面展示信息功能和分页功能

     主页面代码:

    主模板:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        {% load static %}
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="icon" href="{% static "img/luffy-logo.png" %}">
        <link rel="stylesheet" href="{% static "bootstrap-3.3.7-dist/css/bootstrap.min.css" %}">
        <link rel="stylesheet" href="{% static "css/layout.css" %}">
        <link rel="stylesheet" href="{% static "font-awesome-4.7.0/css/font-awesome.min.css" %}">
    
    
    </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>
    
                <a class="navbar-brand" href="#"><i class="fa fa-tripadvisor fa-fw" aria-hidden="true"
                                                    style="margin-right: 6px;"></i>CRM管理系统</a>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
                <div class="nav navbar-nav navbar-right">
                    <img src="{% static "img/default.png" %}" alt="" class="dropdown-toggle img-circle" width="46px" id="dropdownMenu1" data-toggle="dropdown"
                            aria-haspopup="true" aria-expanded="true">
                    <img src="" alt="">
                    <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">Separated link</a></li>
                    </ul>
                </div>
                <ul class="nav navbar-nav navbar-right">
                    <li>
                        <a href="#">任务<i class="fa fa-bell-o fa-fw" aria-hidden="true"></i>
                            <span class="badge">4</span>
                        </a>
                    </li>
                    <li>
                        <a href="#">通知<i class="fa fa-envelope-o fa-fw" aria-hidden="true"></i>
                            <span class="badge">2</span>
                        </a>
                    </li>
                    <li>
                        <a href="#">消息<i class="fa fa-comment-o fa-fw" aria-hidden="true"></i>
                            <span class="badge">3</span>
                        </a>
                    </li>
                    <li>
                        <a href="#">更多<i class="fa fa-ellipsis-v fa-fw" aria-hidden="true"></i></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="#">信息广场 <span class="sr-only">(current)</span></a></li>
                    <li><a href="#">个人中心</a></li>
                    <li><a href="#">帮助</a></li>
                    <li><a href="#">更多</a></li>
                </ul>
            </div>
            <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
    
                {% block content %}
                    <h1 class="page-header">Dashboard</h1>
    
                    <h2 class="sub-header">Section title</h2>
    
                    <div class="table-responsive">
                        <table class="table table-striped">
                            <thead>
                            <tr>
                                <th>#</th>
                                <th>Header</th>
                                <th>Header</th>
                                <th>Header</th>
                                <th>Header</th>
                            </tr>
                            </thead>
                            <tbody>
                            <tr>
                                <td>1,001</td>
                                <td>Lorem</td>
                                <td>ipsum</td>
                                <td>dolor</td>
                                <td>sit</td>
                            </tr>
                            <tr>
                                <td>1,002</td>
                                <td>amet</td>
                                <td>consectetur</td>
                                <td>adipiscing</td>
                                <td>elit</td>
                            </tr>
                            <tr>
                                <td>1,003</td>
                                <td>Integer</td>
                                <td>nec</td>
                                <td>odio</td>
                                <td>Praesent</td>
                            </tr>
                            </tbody>
                        </table>
                    </div>
                {% endblock %}
    
            </div>
        </div>
    </div>
    
    <script src="{% static "jquery/jquery-1.10.2.js" %}"></script>
    <script src="{% static "bootstrap-3.3.7-dist/js/bootstrap.js" %}"></script>
    </body>
    </html>
    主页面

    子模板:继承主模板

    {% extends "layout.html" %}
    {% block content %}
        <h2 class="sub-header" style="display: inline-block">公户信息</h2>
    
        <a href="/add/">
            <button type="button" class="btn btn-success" style="float: right; margin-top: 30px; margin-right: 50px;">添加
            </button>
        </a>
        <div class="table-responsive">
            <table class="table table-striped">
                <thead>
                <tr>
                    <th style="text-align: center">序号</th>
                    <th style="text-align: center">ID</th>
                    <th style="text-align: center">QQ</th>
                    <th style="text-align: center">QQ昵称</th>
                    <th style="text-align: center">姓名</th>
                    <th style="text-align: center">客户来源</th>
                    <th style="text-align: center">班级类型</th>
                    <th style="text-align: center">销售</th>
                    <th style="text-align: center">状态</th>
                    <th style="text-align: center">日期</th>
                    <th style="text-align: center">咨询日期</th>
                    <th style="text-align: center">已报班级</th>
                    <th style="text-align: center">
                        操作
                    </th>
                </tr>
                </thead>
                <tbody>
                {% for customer in customers %}
                    {% if customer.private == 0 %}
                        <tr>
                            <td>{{ forloop.counter }}</td>
                            <td>{{ customer.id }}</td>
                            <td>{{ customer.qq }}</td>
                            <td>{{ customer.qq_name|default:"暂无" }}</td>
                            <td>{{ customer.name|default:"暂无"  }}</td>
                            <td>{{ customer.get_source_display }}</td>
                            <td>{{ customer.get_class_type_display }}</td>
                            <td>{{ customer.consultant }}</td>
                            <td>
                                {{ customer.show_status }}
                            </td>
                            <td>{{ customer.date }}</td>
                            <td>{{ customer.last_consult_date }}</td>
                            <td>{{ customer.show_class }}</td>
                            <td>
                                <a href="/edit/">
                                    <button type="button" class="btn btn-info">编辑</button>
                                </a>
    
                                <a href="/remove/">
                                    <button type="button" class="btn btn-danger">删除</button>
                                </a>
                            </td>
                        </tr>
                    {% endif %}
    
                {% endfor %}
    
                </tbody>
            </table>
        </div>
    {% endblock %}
    主页面HTML

    form表单的代码:

    # ! /usr/bin/env python3.6
    # -*- coding: utf-8 -*-
    # 2018/9/25 20:28
    
    
    from crm_manage import models
    from django import forms
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    
    
    def check(value):
        if "alex" in value:
            raise ValidationError("含有敏感字符")
    
    
    def checkio(s):
        fs = "".join(filter(str.isalnum, s))
        return (not fs.isalpha() and not fs.isdigit() and not fs.islower() and not fs.isupper())
    
    
    class LoginForm(forms.Form):
        username = forms.CharField(
            label="用户名",
            min_length=5,
            max_length=20,
            # initial="张三",
            required=True,
            validators=[check, ],
            widget=widgets.TextInput(attrs={"class": "form-control", "placeholder": "Email address"}),
            error_messages={"min_length": "用户名最少是5位", "max_length": "用户名最长不能超过20位"}
        )
        pwd = forms.CharField(
            label="密码",
            min_length=8,
            required=True,
            widget=widgets.PasswordInput(attrs={"class": "form-control", "placeholder": "Password"}),
            error_messages={"min_length": "密码最少需要8位"}
        )
    
    
    class AddForm(forms.ModelForm):
        class Meta:
            model = models.Customer
            fields = "__all__"
    
        # 给每个input标签添加form-control.
        def __init__(self, *args, **kwargs):
            super(AddForm, self).__init__(*args, **kwargs)
            for field in self.fields.values():
                field.widget.attrs.update({"class": "form-control"})
    
    
    class RegForm(forms.ModelForm):
        # 还可以添加ModelForm中没有的字段
        re_password = forms.CharField(label="确认密码", widget=forms.PasswordInput(attrs={"class": "form-control"}))
    
        class Meta:
            model = models.UserProfile
            # 使用所有的字段
            # fields = "__all__"
            fields = ["username", "name", "password", "re_password", "department"]
            # 派出列表中的字段
            exclude = ["is_active"]
            labels = {
                "username": "用户名",
                "name": "真实姓名",
                "password": "密码",
                "department": "部门",
            }
            widgets = {
                "username": forms.widgets.TextInput(attrs={"class": "form-control"}),
                "password": forms.widgets.PasswordInput(attrs={"class": "form-control"}),
            }
    
        # 对每个字段添加属性, self.fields是一个有序的字典, self.fields.values()获取出每个字段的values值,即每个对象,
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            for field in self.fields.values():
                field.widget.attrs.update({"class": "form-control"})
    
        # 校验两次密码是否一致
        def clean(self):
            if self.cleaned_data.get("password") != self.cleaned_data.get("re_password"):
                self.add_error("re_password", "两次密码不一致")
                raise ValidationError("两次密码不一致")
            return self.cleaned_data
    
        def clean_password(self):
            password = self.cleaned_data.get("password")
            status = checkio(password)
            if status:
                return password
            else:
                self.add_error("password", "密码太简单了")
                raise ValidationError("密码不合格")
    主要是ModelForm的使用

     

    分页功能的实现代码:

    # ! /usr/bin/env python3.6
    # -*- coding: utf-8 -*-
    # 2018/9/27 21:36
    
    from django.utils.html import mark_safe
    
    
    class Pagination(object):
        def __init__(self, request, all_count,base_url, per_num=10, max_show=11, ):
            try:
                current_page = int(request.GET.get("page"))
                if current_page <= 0:  # 判断页码是否为负数
                    raise Exception()
            except Exception as e:
                current_page = 1
            self.base_url = base_url
            self.current_page = current_page
            self.max_show = max_show
            self.half_show = max_show // 2
            self.all_count = all_count
            self.per_num = per_num  # 每页显示的数量
            self.total_page, more = divmod(self.all_count, self.per_num)  # 计算显示的总页数
            if more:
                self.total_page += 1
    
        def start(self):
            return (self.current_page - 1) * self.per_num
    
        def end(self):
            return self.current_page * self.per_num
    
        def html_str(self):
    
            # 总页码数小于最大显示
            if self.total_page < self.max_show:
                page_start = 1
                page_end = self.total_page
            # 总页码大于显示页码
            else:
                if self.current_page < self.half_show:  # 当前页面小于显示的一半,防止有负数
                    page_start = 1
                    page_end = self.max_show
    
                elif self.current_page + self.half_show > self.total_page:  # 限制当前+一半大于总页数
                    page_start = self.total_page - self.max_show + 1
                    page_end = self.total_page
                else:
                    page_start = self.current_page - self.half_show
                    page_end = self.current_page + self.half_show
    
            html_list = []
    
            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 i in range(page_start, page_end + 1):
                if i == self.current_page:
                    li_html = '<li class="active"><a href="{1}?page={0}">{0}</a></li>'.format(i, self.base_url)
                else:
                    li_html = '<li><a href="{1}?page={0}">{0}</a></li>'.format(i, self.base_url)
                html_list.append(li_html)
            if self.current_page >= self.total_page:
                last_li = '<li class="disabled"><a>下一页</a></li>'
            else:
                last_li = '<li><a href="{1}?page={0}">下一页</a></li>'.format(self.current_page + 1, self.base_url)
            html_list.append(last_li)
    
            html_str = mark_safe("".join(html_list))
    
            return html_str
    将分页功能封装为类

    使用封装好的类实现分页功能

    视图函数的使用:

    def user_list(request):
        # 实例化一个对象
        p = Pagination(request, len(users), request.path_info)
        return render(request, "user_list.html", {"user": users[p.start(): p.end()], "html_str": p.html_str()})
    views.py视图函数的使用

    前端模板的使用:

    {% extends "layout.html" %}
    {% block content %}
        <h2 class="sub-header" style="display: inline-block">公户信息</h2>
    
        <a href="/add/">
            <button type="button" class="btn btn-success" style="float: right; margin-top: 30px; margin-right: 50px;">添加
            </button>
        </a>
        <div class="table-responsive">
            <table class="table table-striped">
                <thead>
                <tr>
                    <th style="text-align: center">用户名</th>
                    <th style="text-align: center">密码</th>
                </tr>
                </thead>
                <tbody>
                {% for ret in user %}
                    <tr>
                        <td>{{ ret.name }}</td>
                        <td>{{ ret.password }}</td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        <div class="text-center">
            <nav aria-label="Page navigation">
                <ul class="pagination">
    
                    {{ html_str }}
    
                    {#                {% for page in total_page %}#}
                    {#                    <li><a href="/user_list/?page={{ page }}">{{ page }}</a></li>#}
                    {#                {% endfor %}#}
    
    
                </ul>
            </nav>
        </div>
    
        </div>
    {% endblock %}
    前端模板的使用

     

    第四部分:很多!!!

    完成的主要内容:

    1, 完成私户和公户的区分,以及互相转换的功能

      基于以上代码的修改:models.py中的Customer中的consultant字段的related_name="customers", null=True, blank="True"; private注释掉,因为可以通过销售来判断是否为公户.

    url设计:

    前端页面的展示:

    {% extends "layout.html" %}
    {% block content %}
        {% if request.path_info == "/index/" %}
            <h2 class="sub-header" style="display: inline-block">公户信息</h2>
        {% else %}
            <h2 class="sub-header" style="display: inline-block">拥有的客户信息</h2>
        {% endif %}
    
    
        <form action="" class="form-inline" method="post">
            {% csrf_token %}
            <div class="table-responsive">
                <div class="container-fluid">
                    <div class="row">
                        <div class="col-md-2">
                            <select name="actions" id="" class="form-control">
                                <option value="">请选择</option>
                                <option value="">删除</option>
                                {% if request.path_info == "/index/" %}
                                    <option value="mutil_apply">转为私户</option>
                                {% else %}
                                    <option value="mutil_pub">转为公户</option>
                                {% endif %}
    
    
                            </select>
                            <button type="submit" class="btn btn-info">执行
                            </button>
                        </div>
    
                        <div class="col-md-1 col-md-offset-9">
                            <a href="/add/">
                                <button type="button" class="btn btn-success">添加
                                </button>
                            </a>
                        </div>
                    </div>
                </div>
                <table class="table table-striped">
                    <thead>
                    <tr>
                        <th style="text-align: center">选择</th>
                        <th style="text-align: center">序号</th>
                        {#                <th style="text-align: center">ID</th>#}
                        <th style="text-align: center">QQ</th>
                        <th style="text-align: center">QQ昵称</th>
                        <th style="text-align: center">姓名</th>
                        <th style="text-align: center">客户来源</th>
                        <th style="text-align: center">班级类型</th>
                        <th style="text-align: center">销售</th>
                        <th style="text-align: center">状态</th>
                        <th style="text-align: center">日期</th>
                        <th style="text-align: center">咨询日期</th>
                        <th style="text-align: center">已报班级</th>
                        <th style="text-align: center">
                            操作
                        </th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for customer in customers %}
                        <tr>
                            <td style="text-align: center"><input type="checkbox" value="{{ customer.id }}" name="id"></td>
                            <td style="text-align: center">{{ forloop.counter }}</td>
                            {#                    <td>{{ customer.id }}</td>#}
                            <td style="text-align: center">{{ customer.qq }}</td>
                            <td style="text-align: center">{{ customer.qq_name|default:"暂无" }}</td>
                            <td style="text-align: center">{{ customer.name|default:"暂无" }}</td>
                            <td style="text-align: center">{{ customer.get_source_display }}</td>
                            <td style="text-align: center">{{ customer.get_class_type_display }}</td>
                            <td style="text-align: center">{{ customer.consultant }}</td>
                            <td style="text-align: center">
                                {{ customer.show_status }}
                            </td>
                            <td style="text-align: center">{{ customer.date }}</td>
                            <td style="text-align: center">{{ customer.last_consult_date }}</td>
                            <td style="text-align: center">{{ customer.show_class }}</td>
                            <td style="text-align: center">
                                <a href="/edit/{{ customer.id }}/">
                                    <button type="button" class="btn btn-info">编辑</button>
                                </a>
    
                                <a href="/remove/{{ customer.id }}">
                                    <button type="button" class="btn btn-danger">删除</button>
                                </a>
                            </td>
                        </tr>
    
    
                    {% endfor %}
    
                    </tbody>
                </table>
            </div>
        </form>
        <div class="container-fluid">
            <div class="row">
                <div class="row">
                    <div class="col-md-6 col-md-offset-6">
                        <div class="pull-right">
                            <form action="" class="form-inline">
                                <input type="text" placeholder="请输入内容" class="form-control" name="query">
                                <button type="submit" class="btn btn-info">搜索<i class="fa fa-search"></i>
                                </button>
                            </form>
                        </div>
                    </div>
                    <div class="col-md-12">
                        <div class="text-center">
                            <nav aria-label="Page navigation">
                                <ul class="pagination">
                                    {{ html_str }}
                                </ul>
                            </nav>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    
    
    {% endblock %}
    主页面的HTML 

     

    后端使用类去写:

    class UserInex(View):
    
        @method_decorator(login_required)
        def dispatch(self, request, *args, **kwargs):
            ret = super(UserInex, self).dispatch(request, *args, **kwargs)
            return ret
    
        def get(self, request):
    
            # 获取所有字段
            field_obj = forms.Customer()
            field_list = [i for i in field_obj.fields]
            print(field_list)
            q = self.get_search(field_list)
    
            if request.path_info == "/user_index/":
                user_obj = models.Customer.objects.filter(q, consultant=request.user)
            else:
                user_obj = models.Customer.objects.filter(q, consultant__isnull=True)
    
            query_params = deepcopy(request.GET)
            query_params._mutable = True
            # query_params["page"] = 2
            # # 需要修改配置
            # print(query_params.urlencode())
    
            # 实例化一个分页
            pagination = Pagination(request, len(user_obj), request.path_info, query_params, per_num=3, max_show=5)
            html_str = pagination.html_str
    
            return render(request, "user_index.html",
                          {"customers": user_obj[pagination.start: pagination.end], "html_str": html_str})
    
        def post(self, request):
            action = request.POST.get("actions")
            if not hasattr(self, action):
                return HttpResponse("非法操作")
            getattr(self, action)()
            return self.get(request)
    
        def mutil_pub(self):
            obj_ids = self.request.POST.getlist("id")
            self.request.user.customers.remove(*models.Customer.objects.filter(id__in=obj_ids))
    
        def mutil_apply(self):
            obj_ids = self.request.POST.getlist("id")
            self.request.user.customers.add(*models.Customer.objects.filter(id__in=obj_ids))
    
        def get_search(self, search_list):
            query = self.request.GET.get("query", "")
            q = Q()
            q.connector = "OR"
            for field in search_list:
                q.children.append(Q(("{}__contains".format(field), query)))
            return q
    后端代码

     

     

    2, 完成批量操作

     

    3, 完成搜索功能

    4, 加入分页功能 

    修改之后的分页类;

    # ! /usr/bin/env python3.6
    # -*- coding: utf-8 -*-
    # 2018/9/27 21:36
    
    from django.utils.html import mark_safe
    
    
    class Pagination(object):
        def __init__(self, request, all_count, base_url,query_params, per_num=10, max_show=11, ):
            try:
                current_page = int(request.GET.get("page"))
                if current_page <= 0:  # 判断页码是否为负数
                    raise Exception()
            except Exception as e:
                current_page = 1
            self.base_url = base_url
            self.current_page = current_page
            self.max_show = max_show
            self.half_show = max_show // 2
            self.all_count = all_count
            self.per_num = per_num  # 每页显示的数量
            self.total_page, more = divmod(self.all_count, self.per_num)  # 计算显示的总页数
            self.query_params = query_params
            if more:
                self.total_page += 1
    
        @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 html_str(self):
    
            # 总页码数小于最大显示
            if self.total_page < self.max_show:
                page_start = 1
                page_end = self.total_page
            # 总页码大于显示页码
            else:
                if self.current_page < self.half_show:  # 当前页面小于显示的一半,防止有负数
                    page_start = 1
                    page_end = self.max_show
    
                elif self.current_page + self.half_show > self.total_page:  # 限制当前+一半大于总页数
                    page_start = self.total_page - self.max_show + 1
                    page_end = self.total_page
                else:
                    page_start = self.current_page - self.half_show
                    page_end = self.current_page + self.half_show
    
            html_list = []
    
            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="{1}?{0}">上一页</a></li>'.format(self.query_params.urlencode(), self.base_url)
            html_list.append(prev_li)
    
            for i in range(page_start, page_end + 1):
                self.query_params["page"] = i
                if i == self.current_page:
                    li_html = '<li class="active"><a href="{1}?{0}">{2}</a></li>'.format(self.query_params.urlencode(), self.base_url, i)
                else:
                    li_html = '<li><a href="{1}?{2}">{0}</a></li>'.format(i, self.base_url, self.query_params.urlencode())
                html_list.append(li_html)
            if self.current_page >= self.total_page:
                last_li = '<li class="disabled"><a>下一页</a></li>'
            else:
                self.query_params["page"] = self.current_page + 1
                last_li = '<li><a href="{1}?{0}">下一页</a></li>'.format(self.query_params.urlencode(), self.base_url)
            html_list.append(last_li)
    
            html_str = mark_safe("".join(html_list))
    
            return html_str
    分页功能代码示例

    使用:

     

    5, 添加编辑

    from . import forms
    from . import models
    from django.views import View
    from django.db.models import Q
    from django.contrib import auth
    from crm_manage.forms import RegForm
    from django.utils.html import mark_safe
    from utils.pagination import Pagination
    from django.utils.decorators import method_decorator
    from django.contrib.auth.decorators import login_required
    from django.shortcuts import render, redirect, reverse, HttpResponse
    from utils.pagination import Pagination
    from django.http import QueryDict
    from copy import deepcopy
    
    
    class UserInex(View):
    
        @method_decorator(login_required)
        def dispatch(self, request, *args, **kwargs):
            ret = super(UserInex, self).dispatch(request, *args, **kwargs)
            return ret
    
        def get(self, request):
    
            # 获取所有字段
            field_obj = forms.Customer()
            field_list = [i for i in field_obj.fields]
            print(field_list)
            q = self.get_search(field_list)
    
            if request.path_info == "/user_index/":
                user_obj = models.Customer.objects.filter(q, consultant=request.user)
            else:
                user_obj = models.Customer.objects.filter(q, consultant__isnull=True)
    
            query_params = deepcopy(request.GET)
            query_params._mutable = True
            # query_params["page"] = 2
            # # 需要修改配置
            # print(query_params.urlencode())
    
            # 实例化一个分页
            pagination = Pagination(request, len(user_obj), request.path_info, query_params, per_num=3, max_show=5)
            html_str = pagination.html_str
    
            return render(request, "user_index.html",
                          {"customers": user_obj[pagination.start: pagination.end], "html_str": html_str})
    
        def post(self, request):
            action = request.POST.get("actions")
            if not hasattr(self, action):
                return HttpResponse("非法操作")
            getattr(self, action)()
            return self.get(request)
    
        def mutil_pub(self):
            obj_ids = self.request.POST.getlist("id")
            self.request.user.customers.remove(*models.Customer.objects.filter(id__in=obj_ids))
    
        def mutil_apply(self):
            obj_ids = self.request.POST.getlist("id")
            self.request.user.customers.add(*models.Customer.objects.filter(id__in=obj_ids))
    
        def get_search(self, search_list):
            query = self.request.GET.get("query", "")
            q = Q()
            q.connector = "OR"
            for field in search_list:
                q.children.append(Q(("{}__contains".format(field), query)))
            return q
    
    
    def login(request):
        msg = ''
        loginForm = forms.LoginForm()
        if request.method == "POST":
            loginForm = forms.LoginForm(request.POST)
            username = request.POST.get('username')
            pwd = request.POST.get("pwd")
            obj = auth.authenticate(request, username=username, password=pwd)
            if obj:
                auth.login(request, obj)
                return redirect("/index/")
            else:
                msg = "用户名密码错误"
        return render(request, "login.html", {"loginForm": loginForm, "msg": msg})
    
    
    def regForm(request):
        reg_obj = RegForm()
        if request.method == "POST":
            reg_obj = RegForm(request.POST)
            if reg_obj.is_valid():
                # 数据库中写入数据
                # 第一种方法
                # reg_obj.cleaned_data.pop("groups")
                # reg_obj.cleaned_data.pop("user_permissions")
                # reg_obj.cleaned_data.pop("re_password")
                # models.UserProfile.objects.create_user(**reg_obj.cleaned_data)
    
                # 第二种方法(此方法写入数据库中的密码是明文,所以多了一步设置密码的操作)
                password = reg_obj.cleaned_data.get("password")
                user = reg_obj.save()
                user.set_password(password)
                user.save()
    
                return redirect("/login/")
        return render(request, "reg.html", {"reg_obj": reg_obj})
    
    
    # @login_required
    # def index(request):
    #     customers = models.Customer.objects.filter(consultant__isnull=True)
    #     return render(request, "index.html", {"customers": customers})
    
    
    def logout(request):
        auth.logout(request)
        return redirect("/login/")
    
    
    # @login_required
    # def control(request):
    #     customers = models.Customer.objects.all()
    #     return render(request, "control.html", {"customers": customers})
    
    
    # 增加和添加
    def add_edit(request, edit_id=None):
        edit_obj = models.Customer.objects.filter(id=edit_id).first()
        form_obj = forms.AddForm(instance=edit_obj)
        if request.method == "POST":
            form_obj = forms.AddForm(request.POST, instance=edit_obj)
            if form_obj.is_valid():
                form_obj.save()
                return redirect("/index/")
        return render(request, "add.html", {"form_obj": form_obj})
    
    
    def remove(request):
        return render(request, "index.html")
    
    
    # 分页功能
    
    users = [{"name": "chenrun{}".format(i), "password": "chenrunasb{}".format(i)} for i in range(1, 302)]
    
    # def user_list(request):
    #     """
    #     :param current_page:  当前页码
    #     :param all_count: 总数据条数
    #     :param per_num: 每页显示数据条数
    #     :param max_show: 最多显示页码数
    #     :param total_page: 总页码数
    #     :param start: 数据切片起始索引
    #     :param end: 数据切片终止索引
    #     :return:
    #     """
    #     max_show = 11
    #     half_show = max_show//2
    #
    #     all_count = len(users)  # 所有的数据数
    #
    #     per_num = 10  # 每页显示的数量
    #
    #     total_page, more = divmod(all_count, per_num)  # 计算显示的总页数
    #
    #     # 获取用户点击的那一页
    #     current_page = 1
    #     try:
    #         current_page = int(request.GET.get("page"))
    #         if current_page <= 0:  # 判断页码是否为负数
    #             raise Exception()
    #     except Exception as e:
    #         current_page = 1
    #
    #     # 分割数据并显示
    #     """
    #     1 1     10  0   10
    #     2 11    20  10  20
    #     """
    #     start = (current_page - 1) * 10
    #     end = current_page * 10
    #
    #     # 判断more时候有值,如果有余数,需要在总页数上加1
    #     if more:
    #         total_page += 1
    #
    #     # 总页码数小于最大显示
    #     if total_page < max_show:
    #         page_start = 1
    #         page_end = total_page
    #     # 总页码大于显示页码
    #     else:
    #         if current_page < half_show:  # 当前页面小于显示的一半,防止有负数
    #             page_start = 1
    #             page_end = max_show
    #
    #         elif current_page + half_show > total_page:  # 限制当前+一半大于总页数
    #             page_start = total_page - max_show + 1
    #             page_end = total_page
    #         else:
    #             page_start = current_page - half_show
    #             page_end = current_page + half_show
    #
    #
    #     html_list = []
    #
    #     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 i in range(page_start, page_end+1):
    #         if i == current_page:
    #             li_html = '<li class="active"><a href="/user_list/?page={0}">{0}</a></li>'.format(i)
    #         else:
    #             li_html = '<li><a href="/user_list/?page={0}">{0}</a></li>'.format(i)
    #         html_list.append(li_html)
    #     if current_page >= total_page:
    #         last_li = '<li class="disabled"><a>下一页</a></li>'
    #     else:
    #         last_li = '<li><a href="/user_list/?page={0}">下一页</a></li>'.format(current_page+1)
    #     html_list.append(last_li)
    #
    #     html_str = mark_safe("".join(html_list))
    #
    #     return render(request, "user_list.html", {
    #         "user": users[start:end],
    #         "html_str": html_str,
    #     })
    
    # return render(request, "user_list.html",
    #               {
    #                   "user": users[start: end],
    #                   "total_page": range(page_start, page_end+1),  # 因为range顾头不顾尾,所以要加一
    #
    #               }
    #               )
    
    # def user_list(request):
    #     # 实例化一个对象
    #     p = Pagination(request, len(users), request.path_info)
    #     return render(request, "user_list.html", {"user": users[p.start(): p.end()], "html_str": p.html_str()})
    目前所有的后端代码(包含添加)

     

    第五部分: 遇到一些问题;并加以解决

    问题一: 在个人用户添加或编辑完成之后跳转的公户信息.

    解决思路: 在访问添加或编辑的时候,将url的信息添加到next=...后边,提交过去,添加或者修改之后,拿到提交的next的url地址返回即.

    第一步: 记录删一条的搜索地址和查询条件; 将地址拼接到添加的buttun的按钮上.

    修改之前的添加按钮:

    <a href="/add/">
        <button type="button" class="btn btn-success">添加</button>
    </a>

    在后端的cbv中定义方法:获取url的路径以及查询条件

        def get_add_btn(self, request):
            """
            生成按钮的标签
            :param request:
            :return:
            """
            url = request.path_info
            param = request.GET.copy()  # 得到的是一个querydict对象
            qd = QueryDict()
            qd._mutable = True
    
            qd["next"] = url
            qd["_query"] = param.urlencode()  # 通过urlencode得到字符串
            query = qd.urlencode()
            add_btn = '<a href="{}?{}"><button type="button" class="btn btn-success">添加</button></a>'.format(reverse('add'), query)
            return mark_safe(add_btn)
    生成add_btn按钮

    然后再添加的函数中添加:

     同样的编辑也是需要添加筛选条件的

    # 接受query并传递到前端页面
    add_btn, query = self.get_add_btn(request)

    前端接收:

     

    问题二:

    需要添加客户的跟进记录和展示客户的跟进记录

    先定义跟进记录表:

    class BaseForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(BaseForm, self).__init__(*args, **kwargs)
            for filed in self.fields.values():
                filed.widget.attrs.update({"class": "form-control"})
    
    # 定义跟进记录
    class ConsultRecord(BaseForm):
        class Meta:
            model = models.ConsultRecord
            fields = "__all__"
    
            widgets = {
    
            }
    再forms中定义跟进记录表

     

     

    第六部分:继续完善

    问题一:

    当两个销售同时将同一个公户转为私户时,理应时先到先得,而现在是后来的可以转换成功。

    解决:使用数据库中的锁。

    # 1, 开始使用锁
    $ begin;
    # 2, 使用锁
    $ select * from table where id = 11 for update;
    # 此时另外一个终端去开启mysql使用同一张表修改这个字段的时候就会夯住。只有释放掉锁另一边才可以修改成功
    # 3,结束事物
    $ commit;

    views.py中如何加锁

    from django.db import transaction

    此时应该判断这两个用户是否是私户, 进而判断是否销售能否进行修改

        def mutil_apply(self):
            flag = False
            obj_ids = self.request.POST.getlist("id")
            # self.request.user.customers.add(*models.Customer.objects.filter(id__in=obj_ids))
            with transaction.atomic():
                old = models.Customer.objects.filter(id__in=obj_ids, consultant__isnull=True).select_for_update()
                if len(obj_ids) == len(old):
                    models.Customer.objects.filter(id__in=obj_ids).update(consultant=self.request.user)
                    flag = True
            if not flag:
                return HttpResponse("下手满了,已经被别人抢走了")

    问题2: 销售不能无限制将公户添加到自己的私户中

    解决:第一步:在settings.py中配置最大的私户限制

    MAX_CUSTOMER_NUM = 3

    第二步:

    倒入settins文件

    from django.conf import settings

    在views.py中的转私户的函数中添加判断限制最大人数

    obj_ids = self.request.POST.getlist("id")
    count
    = models.Customer.objects.filter(consultant=self.request.user).count() if count + len(obj_ids) > settings.MAX_CUSTOMER_NUM:   return HttpResponse("你的私户人数太多了")

     

    转载于:https://www.cnblogs.com/chenrun/p/9709471.html

    展开全文
  • 4.表结构的设计权限表IDURL1/user_list/2/customer_list/用户表IDUSER_NAME1root2root 2角色/用户组表ID组1销售2开发用户与角色的关系表IDUSER_ID角色ID111212321422角色与权限的关系表ID角色ID权限ID111212321422...
  • 企业管理信息系统 Django-ERP

    千次阅读 2018-12-24 10:42:50
    Django-ERP是一款面向中小企业的信息管理软件,实现了ERP和OA的有效融合,涵盖销售管理、采购管理、库存管理、OA流程以及员工自助平台功能,通过本平台,可以实时监控企业的收入、成本与库存,为管理者制定决策提供...
  • Django 开发收银系统

    2020-02-08 17:36:11
    作为管理员,对销售的明细的查询是必要的。 前端的界面如下: 虽然saleList表中有数据,但是并没有写入到表格中,所以希纳是的界面销售前端页面没有数据,要完成的是将表中的数据列入表格。 展示销售概况,在views....
  • 本篇我们介绍图书管理系统需求分析和系统设计 目录 系统分析: 背景和意义 可行性分析 需求分析 1.功能需求 2.性能需求 3.软件质量要求 4.灵活性 系统设计 系统总体设计 系统数据库设计 数据库概述 ...
  • 有的时候,我们便利店的销售员不需要获取一个顾客的所有信息,只需要获取某些特殊信息就好。比如销售员需要通过知道顾客的电话号码来获取顾客的VIP等级以便给顾客打折,这时候,就要用到过滤查询了。 流程:当销售员...
  • 建了项目,运行起了Django...1打开【终端】,cd到manage.py文件这里,输入python3 manage.py startapp sales,创建一个应用--销售管理系统(sales)。sales/__init__.pyadmin.pyapps.pymigrations/__init__.pymodels.p...
  • Django CRM系统设计开发

    2020-03-10 18:43:35
    设计一个教育机构的客户管理系统(CRM),对该系统分析如下: 系统分析 CRM customer relationship management 客户管理系统 1. 干什么用的? 管理客户 维护客户关系 2. 谁去使用? 销售 班主任 项目经理 ...
  • 好程序设计擅长JAVA(SSM,SSH,SPRINGBOOT)、PYTHON(DJANGO/FLASK)、THINKPHP、C#、安卓、微信小程序、MYSQL、SQLSERVER等,欢迎咨询 今天记录的项目是超市销售管理系统,超市销售管理系统项目是这么回事:随着信息...
  •  而CRM客户关系管理系统,是可以按照需求来定制的,进而能够灵活部署,避免浪费的。这样就能够打造适合自身企业的管理软件。 一、那么,客户关系管理的价值又有哪些呢? 针对企业的需求,进而能够知道客...
  • 二、需求分析1、报名流程如以下描述:销售 发起报名流程,选择班级,发报名链接给学员学员 填写在线报名表,提交个人信息,上传证件信息,同意培训协议销售 审核报名表,审核通过后,创建一条缴费记录,自动把学员...
  • 好程序设计擅长JAVA(SSM,SSH,SPRINGBOOT)、PYTHON(DJANGO/FLASK)、THINKPHP、C#、安卓、微信小程序、MYSQL、SQLSERVER等,欢迎咨询 今天将为大家分析一个会议管理系统(随着国内外学术交流活动的日渐频繁,国内举办的...
  • 假装自己拥有一家小便利店,名叫——7-Twelve,我希望制作一个便利店管理系统对我的顾客、销售人员、货物等数据进行管理,先不管前端,后端总免不了对数据的一顿操作,那如何在Django中对数据库进行增删查改呢?...
  • 一个便利店要想运转顺利,肯定要有合理的管理与分配,不止老板有查看客户数据的权限,我们店里的销售人员,也应该有一个利用浏览器向服务端访问数据的权限,这篇文章带大家走一个读取数据库数据的流程。学会了读取...
  • Django商店收银系统开发教程教程目录开发前准备项目需求分析数据库设计开始准备项目系统登录前端收银提交商品到数据库后台管理界面实现销售查询实现商品管理项目总结 教程目录 本教程从零基础深入讲解Django。项目的...
  • Django简单笔记

    2021-04-16 03:35:01
    之前用Django写了一个简单的销售管理系统,使用之前是不曾了解Django的,相当于快速上手了一下。便想着记录一下,但一直拖延,正好今晚熬夜啥也不想干,不如随便写写。 0. 首先 Django的D不发音 对了,本人用的是...
  • Django的应用资源

    2019-03-02 10:15:31
    Django-ERP是一款基于Django开发的企业管理信息系统,包含了OA、销售管理、采购管理、库存管理、项目管理、文档管理以及组织管理模块。Django独有的大工单与大物料设计能够方便用户快速的增加新业务流程,满足企业...
  • 客户管理系统是提供给网咨、销售人员来查看客户信息、增加客户、修改客户信息的。当然客户在我们项目中还有公户和私户的区别,公户私户实际是由销售总监根据销售的业务能力来合理分配,从而使客户转化率最大化。 ...
  • 店铺管理系统 我将使用Django,MySql,Django Rest Framework和React Js来实现此项目。 功能包括:-药品库存管理(CRUD)-具有帐单和报告生成的销售管理-组织主数据管理-客户管理-管理员登录
  • 如果实现销售管理系统,还要想实现部门管理系统那么狼狈的话,那要Django有啥用了?你要知道,Django可是号称:只要很少的代码,程序员就可以轻松轻松地完成一个后台管理系统所需要的大部分内容,并进一步开发出全...
  • django后台很强大,关键看...# admin 页面显示标题admin.site.site_header = '销售管理系统'# admin 页面头部标题admin.site.site_title = '销售管理系统'30.2 应用名称调整修改base应用显示名称(后台一级菜单名称)...
  • django的url分层

    2021-04-14 10:40:10
    比如销售管理系统,销售员登陆后网页是sales/orders查看订单。 在django里,针对销售员的一组功能建立app。 python manage.py startapp sales 在sales的view.py里建立sales/orders对应的处理函数listOrders...
  • 本节内容需求讨论权限设计代码设计自定义权限钩子业务场景分析假设我们在开发一个培训机构的 客户关系管理系统,系统分客户管理、学员管理、教学管理3个大模块,每个模块大体功能如下客户管理销售人员可以录入客户...
  • Django-erp 企业erp安装实录

    千次阅读 2017-12-11 11:49:32
    Django-ERP是一款基于Django开发的企业管理信息系统,包含了OA、销售管理、采购管理、库存管理、项目管理、文档管理以及组织管理模块。Django独有的大工单与大物料设计能够方便用户快速的增加新业务流程,满足企业...
  • 本节内容需求讨论权限设计代码设计自定义权限钩子业务场景分析假设我们在开发一个培训机构的 客户关系管理系统,系统分客户管理、学员管理、教学管理3个大模块,每个模块大体功能如下客户管理销售人员可以录入客户...

空空如也

空空如也

1 2 3
收藏数 57
精华内容 22
关键字:

django销售管理系统