单爆手

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
1. 今日内容大纲
	1. 需求分析
		1. 项目要做什么,要实现什么样的功能,达到什么样的目的
		2. 需求的细节
		3. 架构设计
		4. 人员分工
		5. 项目的排期(里程碑节点)
		
	2. 讨论会
		1. 表结构设计  *****
		
	3. 项目需求:
		1. CRM   --> 客户管理系统
			业务流程:
					网络咨询师 
					↓
					客户池(公户)
					↓
					招生老师(私户150)
					↓
					沟通记录
					↓
					报名(填报名表、交定金)
					↓
					开班(签合同、交学费)
					↓
					课程上课记录(授课老师、课程内容、作业)
					↓
					出勤记录(签到情况)
					↓
						...
					↓
					毕业
						
				1小时后(10:10)组长发表结构给我(网络咨询师,招生老师都属于一张员工表) 

	
		2. 表结构设计常见问题:
			1. 业务逻辑不清晰,表结构的字段无法确定
		3. 邮件的问题:
			1. 正式沟通用email
			2. 发邮件
				1. 发件人
				2. 主题
				3. 邮件正文
					1. 敬语/Hi 称呼
					2. 言简意赅把事情说明白
					3. 落款
				4. 附件  --> 命名要有意义
	
                                                             

 

 

	
	
	4. CRM表结构分析
		1. 多选字段
			from multiselectfield import MultiSelectField
			pip install django-multiselectfield
	
2. 今日任务
	1. 登录示例写完
	2. 把models.py里面的每一个字段过三遍!!!


最近两周的安排:
	1. CRM(5天)
	2. 权限系统(5天)

Web开发:

项目经理/产品经理  UI  前端   后端   测试   运维

领导      你    --> 扁平化管理

 

1.创建项目:

 

 2.django项目配置:

settings.py中:

(1)INSTALLED_APPS = [
.......
'crm.apps.CrmConfig',#确保你的app已经放过来了。
]
(2)DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'knight',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'root',
'PASSWORD': '123',
}
}
(3)STATIC_URL = '/static/'
STATICFILES_DIR = [
os.path.join(BASE_DIR,'static'),
]
(4)#LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
#TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

创建数据库及配置:

cmd--->mysql -uroot -p123 ->create database knight

在settings.py同级的--init--.py中导入pymysql:

import pymysql
pymysql.install_as_MySQLdb()

3.拷贝static文件夹到项目中

4.models.py中创建表如下:11张表

用户表中邮箱作为唯一标识。

 

 客户表中用qq号作唯一标识。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager, User
from django.utils.translation import ugettext_lazy as _
from multiselectfield import MultiSelectField

course_choices = (('Linux', 'Linux中高级'),
                  ('PythonFullStack', 'Python高级全栈开发'),
                  ('Go', 'Golang高级开发'),
                  )

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', "退学"),
                    )

attendance_choices = (('checked', "已签到"),
                      ('vacate', "请假"),
                      ('late', "迟到"),
                      ('absence', "缺勤"),
                      ('leave_early', "早退"),)

score_choices = ((100, 'A+'),
                 (90, 'A'),
                 (85, 'B+'),
                 (80, 'B'),
                 (70, 'B-'),
                 (60, 'C+'),
                 (50, 'C'),
                 (40, 'C-'),
                 (0, ' D'),
                 (-1, 'N/A'),
                 (-100, 'COPY'),
                 (-1000, 'FAIL'),)


class Customer(models.Model):
    """
    客户表
    """
    qq = models.CharField('QQ', max_length=64, unique=True, help_text='QQ号必须唯一')
    qq_name = models.CharField('QQ昵称', max_length=64, blank=True, null=True)
    name = models.CharField('姓名', max_length=32, blank=True, null=True, help_text='学员报名后,请改为真实姓名')
    sex_type = (('male', '男'), ('female', '女'))
    sex = models.CharField("性别", choices=sex_type, max_length=16, default='male', blank=True, null=True)
    birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True)
    phone = models.BigIntegerField('手机号', blank=True, null=True)
    source = models.CharField('客户来源', max_length=64, choices=source_type, default='qq')
    introduce_from = models.ForeignKey('self', verbose_name="转介绍自学员", blank=True, null=True)
    course = MultiSelectField("咨询课程", choices=course_choices)
    class_type = models.CharField("班级类型", max_length=64, choices=class_type_choices, default='fulltime')
    customer_note = models.TextField("客户备注", blank=True, null=True, )
    status = models.CharField("状态", choices=enroll_status_choices, max_length=64, default="unregistered",
                              help_text="选择客户此时的状态")
    network_consult_note = models.TextField(blank=True, null=True, verbose_name='网络咨询师咨询内容')
    date = models.DateTimeField("咨询日期", auto_now_add=True)
    last_consult_date = models.DateField("最后跟进日期", auto_now_add=True)
    next_date = models.DateField("预计再次跟进时间", blank=True, null=True)
    network_consultant = models.ForeignKey('UserProfile', blank=True, null=True, verbose_name='咨询师',
                                           related_name='network_consultant')
    consultant = models.ForeignKey('UserProfile', verbose_name="销售", related_name='customers', blank=True, null=True, )
    class_list = models.ManyToManyField('ClassList', verbose_name="已报班级", )


class Campuses(models.Model):
    """
    校区表
    """
    name = models.CharField(verbose_name='校区', max_length=64)
    address = models.CharField(verbose_name='详细地址', max_length=512, blank=True, null=True)


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')


class ConsultRecord(models.Model):
    """
    跟进记录表
    """
    customer = models.ForeignKey('Customer', verbose_name="所咨询客户")
    note = models.TextField(verbose_name="跟进内容...")
    status = models.CharField("跟进状态", max_length=8, choices=seek_status_choices, help_text="选择客户此时的状态")
    consultant = models.ForeignKey("UserProfile", verbose_name="跟进人", related_name='records')
    date = models.DateTimeField("跟进日期", auto_now_add=True)
    delete_status = models.BooleanField(verbose_name='删除状态', default=False)


class Enrollment(models.Model):
    """
    报名表
    """

    why_us = models.TextField("为什么报名", max_length=1024, default=None, blank=True, null=True)
    your_expectation = models.TextField("学完想达到的具体期望", max_length=1024, blank=True, null=True)
    contract_agreed = models.BooleanField("我已认真阅读完培训协议并同意全部协议内容", default=False)
    contract_approved = models.BooleanField("审批通过", help_text="在审阅完学员的资料无误后勾选此项,合同即生效", default=False)
    enrolled_date = models.DateTimeField(auto_now_add=True, verbose_name="报名日期")
    memo = models.TextField('备注', blank=True, null=True)
    delete_status = models.BooleanField(verbose_name='删除状态', default=False)
    customer = models.ForeignKey('Customer', verbose_name='客户名称')
    school = models.ForeignKey('Campuses')
    enrolment_class = models.ForeignKey("ClassList", verbose_name="所报班级")

    class Meta:
        unique_together = ('enrolment_class', 'customer')


class PaymentRecord(models.Model):
    """
    缴费记录表
    """
    pay_type = models.CharField("费用类型", choices=pay_type_choices, max_length=64, default="deposit")
    paid_fee = models.IntegerField("费用数额", default=0)
    note = models.TextField("备注", blank=True, null=True)
    date = models.DateTimeField("交款日期", auto_now_add=True)
    course = models.CharField("课程名", choices=course_choices, max_length=64, blank=True, null=True, default='N/A')
    class_type = models.CharField("班级类型", choices=class_type_choices, max_length=64, blank=True, null=True,
                                  default='N/A')
    enrolment_class = models.ForeignKey('ClassList', verbose_name='所报班级', blank=True, null=True)
    customer = models.ForeignKey('Customer', verbose_name="客户")
    consultant = models.ForeignKey('UserProfile', verbose_name="销售")
    delete_status = models.BooleanField(verbose_name='删除状态', default=False)

    status_choices = (
        (1, '未审核'),
        (2, '已审核'),
    )
    status = models.IntegerField(verbose_name='审核', default=1, choices=status_choices)

    confirm_date = models.DateTimeField(verbose_name="确认日期", null=True, blank=True)
    confirm_user = models.ForeignKey(verbose_name="确认人", to='UserProfile', related_name='confirms', null=True,
                                     blank=True)


class CourseRecord(models.Model):
    """课程记录表"""
    day_num = models.IntegerField("节次", help_text="此处填写第几节课或第几天课程...,必须为数字")
    date = models.DateField(auto_now_add=True, verbose_name="上课日期")
    course_title = models.CharField('本节课程标题', max_length=64, blank=True, null=True)
    course_memo = models.TextField('本节课程内容', max_length=300, blank=True, null=True)
    has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
    homework_title = models.CharField('本节作业标题', max_length=64, blank=True, null=True)
    homework_memo = models.TextField('作业描述', max_length=500, blank=True, null=True)
    scoring_point = models.TextField('得分点', max_length=300, blank=True, null=True)
    re_class = models.ForeignKey('ClassList', verbose_name="班级")
    teacher = models.ForeignKey('UserProfile', verbose_name="讲师")

    class Meta:
        unique_together = ('re_class', 'day_num')


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 Department(models.Model):
    name = models.CharField(max_length=32, verbose_name="部门名称")
    count = models.IntegerField(verbose_name="人数", default=0)

    def __str__(self):
        return self.name


# 定义UserProfile这个类的管理类(定义用户表UserProfile类的额外方法给它扩展)
class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):#定义创建用户的私有方法,供下面两函数调用
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email) #格式化操作去掉空格
        user = self.model(email=email, **extra_fields)  # 创建对象实例化UserProfile(email='', password='')
        user.set_password(password)  # 把密码加密后再写入数据库
        user.save(using=self._db)  # 保存到数据库
        return user

    def create_user(self, email, password=None, **extra_fields):#创建普通用户
        extra_fields.setdefault('is_staff', False) #setdefault是给字典设置默认值
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)#调用上面的私有方法创建用户

    def create_superuser(self, email, 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(email, password, **extra_fields)


class UserProfile(AbstractBaseUser, PermissionsMixin):
    email = 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(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    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)

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'email'  # 用来唯一确定auth中的用户(python manage.py create_superuser 时输入的用户)
    REQUIRED_FIELDS = ['name']  # auth指定除了上面两个配置项的字段还有那些字段是必填

    class Meta:
        verbose_name = '账户信息'
        verbose_name_plural = "账户信息"

    def clean(self):
        super(UserProfile, self).clean()#继承父类的clean方法
        # 对邮件字段做校验
        self.email = self.__class__.objects.normalize_email(self.email)

    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.email  #返回邮箱

    def __str__(self):  # __unicode__ on Python 2
        return self.email

    # 给ORM添加管理类
    objects = UserManager()

  

 

补充:UserProfile表

  1.auth认证模块中,User表中有很多默认奇怪的字段:first_name,last_name等,而我不想要这些字段。auth模块中User表是继承AbstractUser类,它又继承

  class AbstractUser(AbstractBaseUser, PermissionsMixin):

这两个类,(里边有firest_name等字段,我不需要这些字段且想给它扩充一些字段:手机号,部门等)?----我自定UserProfile表(相当于照 着写User表自定写类),让其继承那两类即可,并在其中增删对应字段:

  class UserProfile(AbstractBaseUser, PermissionsMixin):

   2.objects = UserManager()是给orm添加管理类,auth模块中有create_user和create_superuser方法这两方法就是UserManager()管理类中提供的.

 5.创建表:

  (1)自己新建了userprofile表,所以得在settings.py中告诉django(否则它会去默认的auth_user表做用户认证)

    #指定使用crm.Userprofile表代替默认的auth_user表
    AUTH_USER_MODEL = 'crm.UserProfile'

  (2)F:\knight>python manage.py makemigrations

执行 python manage.py makemigrations 报错:

  点击报错位置找到这两行 注释掉,如下:

1
2
3
version = Database.version_info
# if version < (1, 3, 13):
#    raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)

   (3)python manage.py migrate

  (4)创建超级用户:

 

 查看mysql数据:

 

第一次使用时,它最下方有一个:Download missing driver files 这个需要先下载一下,才可以用。

如果一个也不行则链接不上,或可在上方的Driver :MySQL 可以来选择版本。

连接成功后,如下可以看到userprofile表中有数据了且没有first_name等字段:

 6.创建注册页面

  (1)knight/usrls.py中:

from django.conf.urls import url
from django.contrib import admin
from crm import views

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', views.LoginView.as_view()),
url(r'^index/', views.index),
]

(2)views.py中:

from django.shortcuts import render, redirect, HttpResponse
from django import views
from django.contrib import auth
from django.contrib.auth.decorators import login_required

class LoginView(views.View):
    def get(self, request):
        return render(request,'login.html')

    def post(self,request):
        # 1. 获取用户输入的内容
        email = request.POST.get('email')
        pwd = request.POST.get('password')
        is_check = (request.POST.get('is_check',None) == '777')
        # 2. 校验用户名密码是否正确
        user_obj = auth.authenticate(request, email=email, password=pwd)
        if user_obj:
            # 登录成功
            # 存Session数据并且回写Cookie
            auth.login(request, user_obj)  # auth认证中间件的源码
            if is_check:
                request.session.set_expiry(7*24*60*60)
            else:
                request.session.set_expiry(0)
            # 跳转到首页
            return redirect('/index/')
        else:
            # 登录失败
            return render(request, 'login.html', {'error_msg': '邮箱或密码错误'})


@login_required
def index(request):
    return HttpResponse('不错')

  

(3)tempaltes/login.html中:

  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.css' %}">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1>欢迎登录</h1>
            <form action="" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <label for="email">邮箱</label>
                    <input type="email" class="form-control" name="email" id="email" placeholder="Email">
                </div>
                <div class="form-group">
                    <label for="password">密码</label>
                    <input type="password" class="form-control" name="password" id="password" placeholder="Password">
                </div>
                <div class="checkbox">
                    <label>
                        <input type="checkbox" name="is_check" value="777"> 7天免登录
                    </label>
                </div>
                <button type="submit" class="btn btn-success">登录</button>
            </form>
        </div>
    </div>
</div>

</body>
</html>  
(4)settings.py中:

#配置默认登录页面的url
LOGIN_URL = '/login'

 

 

 

 

如下输入:http://127.0.0.1:8000/index/  未登录时会跳转到如下登录页面了

 

如下登录后跳转成功了!!!! 

到此登录页面就成功了

 

posted on 2020-04-02 08:47  单爆手  阅读(554)  评论(0)    收藏  举报