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/ 未登录时会跳转到如下登录页面了

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

到此登录页面就成功了
浙公网安备 33010602011771号