本节内容

业务痛点分析

项目需求讨论

使用场景分析

表结构设计

业务痛点分析

我2013年刚加入老男孩教育的时候,学校就一间教室,2个招生老师,招了学生后,招生老师就在自己的excel表里记录一下,每月算工资时,就按这个表来统计招生提成, 后来学校大了点,教室变成了3间,招生老师变成了4个人,招的学生也开始多了起来,不过大家依然是招了学生就纪录在自己的excel里,但问题开始出现了,我们的招生多是通过网络qq聊天在线咨询,好多客户需要聊很久才能转化成学员,经常会出现一个客户跟一个招生老师聊过后,过了一段时间却在另一个招生老师那报名了。 于是争执就出现了,A销售说这个人是先找我咨询的,我帮他回答了大多数问题,报名提成却算成B的,不公平。B销售也会讲,这个客户找他咨询时并未说自己之前跟其它销售聊过呀,所以对B来讲,这就是个新客户呀,并且是在B这报的名, 凭什么要分提成给A呀。为了解决这个问题,于是想出一个办法,搞了一个机器做共享服务器,共享服务器上搞了个统一的客户信息excel表,大家每人聊过的客户都在这个表里纪录, 这样每个人录入前,先在表格里全局查一下,这个客户的qq号是不是已经在表里了,这样就能避免冲突了。 但用过共享文件的应该都遇到过一个问题就是,为保证数据一致性,同一时间系统只允许一个用户修改数据, 但我们的每个招生老师一上班,就会打开这个共享文件,随时聊的学生,随时会录入到表里。但这个限制使的大家,只要一有人录入信息,就得要求其它销售人员都把文件先关闭,录入信息后其它人才能再用,这搞的大家很难受。  所以销售人员经常让我帮开发个系统,可以解决一些类似这样的问题。

另外, 我们在讲课时, 为了保证和检验学员的学习效果, 每节课讲完时都会给学生部置一个作业,要求他必须在下节上课前完成并提交作业,老师会给学员统一批作业,作业成绩也纪录在excel表中,就像下图一样。每个老师维护自己班级的作业,校长如果想看各个班级的成绩,还要跟每个老师要学员成绩表,很不方便, 当时校长就跟我讲,学员的成绩\出勤情况等要是也能统一管理就好了。

于是我心里也一直纪着这个事,想帮学校开发一个学员开发一个类似CRM(客户关系管理软件)的系统,集销售管理\学员管理等功能于一体。但平常要上班,周未还要讲课,一直没时间,终于有一次,我要去美帝度假,飞机要飞13个小时才到纽约,在飞机上突然想,反正也是没事干, 不如就开发这个东西吧, 于是一路未眠,狂写了近千行代码,飞机的上的美女空乘都心疼的多给我倒了2杯咖啡,哈哈,下飞机时,已是两眼痛红,不过,这个学员管理系统的雏形也做出来了,接下来几天在纽约又完善了下细节,于是就开始上线让大家用了。到现在,这个系统在公司已经用了一年多了,大家每天都在使用,之前的痛点都解决了,特别是销售人员, 工作已经离不开这个系统了,自己做的东西帮别人解决具体问题,心里还是蛮开森的!

个人觉得这个学员管理系统的挺适合新手练习的,难度适中,用户的知识点也比较综合,所以把学校里的系统简化了一下,提练成了一个教学项目,下面我们就一起来做一个喽。

噢,对了,最后说一下这个项目涉及到的知识点,请确保你以充分掌握了下面所列知识点再学习此项目噢!

  1. Django

  2. JQuery

  3. BootStrap

  4. Ajax

  5. HTMS\CSS

项目需求讨论

首先给我们的项目起个名字吧,这个系统要同时支持销售管理\学员管理\讲师管理等功能,功能比较杂,不能称之为严格意上的CRM,因为CRM一般只包括销售管理,but anyway, who cares,我们就叫它”老男孩crm管理系统”吧,

起好了名字,开始动手写之前, 肯定要把需求想清楚,需求想不清楚就开始写的话,等于给自己挖坑,你肯定不想出现写了5千行代码后,突然发现,需求搞错了,还要重新推到重来的事情吧。所以,现在静下心,把需求想明白,画好思维导图,跟同事多讨论应用场景,做了各方面论证后,再开始动工噢。

我们的系统的用户分3种,销售\学生\讲师,这3个角色关注的事情是不同的, 销售只关注招了多少学员,讲师关注自己管理的班级学习成绩怎样,学员只关注自己的学习成绩。 因此在想需求时,你要从每个角色出发,看他关心的是什么, 于是我画出了这个思维导图

根据思维导图,我们总结出以下具体需求:

  1. 把用户分为销售\学员\讲师3种角色,每个角色关心的事情不同,所以有要不同的用户视图,即销售人员登陆后只需要看他与他工作相关的就可以,讲师关心的事情不需要在销售视图里显示
  2. 销售人员可以录入客户信息,和后续跟进信息,因为一个客户可能要跟进好几次才会报名。 可以多种条件过滤查询用户,可以查看自己的销售业绩报表
  3. 学员主要2个功能,查询成绩\提交作业
  4. 讲师的主要功能是管理班级\ 批作业\上课点名
  5. 虽然现在只有一个校区,但要考虑到以后的扩展,以后全国可能有好多个校区,不同校区之间的数据应该是隔离的
  6. 这个系统里存放着大量的客户信息\学员数据等,都是公司的机密信息,设计系统时就要考虑到权限问题,不同的角色要有不同的权限,同一角色的不同用户也要允许有不同的权限

使用场景分析

为了更加理清我们的项目开发需求,在动手写代码前,建议再有一个业务场景分析的步骤,即从用户角度写出你使用这个项目的具体场景,我们这里分为销售、讲师、学员3个角色,我分别给每个角色列出了几个使用场景:

一. 销售:

  1. 销售人员张三刚通过qq群聊了一个客户,咨询了"Python全栈开发课程",录入了CRM
  2. 张三 回访了一个 一个月多前咨询的老客户,老客户说最近考虑报名,让张三 下周联系他办报名手续, 张三把这个回访信息 录入了crm
  3. 李四 今天录入了8个新客户,但发现有一个客户在录入时,系统提示,此客户信息已存在,于是就在客户库查了一下,发现 此客户 已被  张 三  在上周录入了,销售部有规定,在客户被录入一个月内,其它销售是不能抢这个单的,于是就提醒张三,让他跟这个客户主动再跟进一下
  4. 李四今天 有一个客户要报名,李四给客户发了在线报名 链接,要求其填写一些个人信息,上传证件照片等,交了500报名费后,将其正式添加到了报名的 班级里
  5. 销售主管 BigSB  查看了今天的销售报表, 查看了每个销售的最近一周 咨询的客户量,成单比, 每个课程 分别 报名了多少学员, 这些客户 都是从哪些渠道 过来的

二. 讲师:

  1. 讲师Alex 今天 要给 Python自动化 周未班上课, 上课前要进行点名, 于是创建了一条生课纪录 ,这条上课记录关联 着每个学员 这节课的 出勤状态,alex 边点名边在系统 中纪录每个学员的状态 ,签到、迟到或缺勤。
  2. 今天的课上完后,ALEX给大家留了作业,在系统 里 找到今天 的上课记录,把作业 需求 写到了 上课纪录 里
  3. 过了一周, Alex 登录 系统 下载了所有学员这节课的作业 , 为每个学员批改了作业 并将学员的成绩纪录在了系统 里

三. 学员:

  1. 学员小明 报名了“Go语言开发课程第2期周未班”, 交完报名费后,招生老师 给他发了一个报名地址 要求其填写,填写了个人信息 并上传了证件信息, 提交后, 过了一会,就发现自己的qq邮箱里收到了一封 老男孩学校的邮件, 告知他 已为其 生成了 学员账号,他按照邮件 提示 登录 了 老男孩的学员系统 ,看到系统 中可以  进入自己的班级,不过现在班级里 还没有上课纪录, 他还能在线查看 他的学员合同
  2. 过了一周, 正式上课了,上了一天课,讲师Alex布置了作业 , 小明过了几天 完成 作业后,登录 学员管理系统 , 发现有一条 自己的上课记录, 他点进去,看到了当节课的 作业 需求,及提交作业的方式 ,他只需要把作业 打包成.zip格式 ,直接拖拽到页面上的交作业区就可以了。
  3. 过了两天, 小明收到邮件提醒 ,说自己作业 得了个A,非常 的开森。
  4. 小明学了几个月好,发现老男孩讲师讲的好,自己学的也不错,于是极力推荐自己的2个朋友也来学习, 于是在自己的学员系统 里录入了2条推荐朋友学习的信息, 没过一会, 小明之前的招生老师就联系他说,已经收到他的推荐名单,很快就会联系 小明的朋友 。

好啦, 业务场景也分析完了,接下来我们终于可以开始搞事情啦!

表结构设计

在设计表结构前,咱们先创建好一个项目吧,项目名就叫PerfectCRM,app名叫crm

我这里先列出来我们大体需要的表,尔等先过目一下,然后我们再分别为什么需要每个具体的表,及这个表里要存什么样的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from django.db import models
 
# Create your models here.
 
 
class Customer(models.Model):
    '''存储所有客户信息'''
    pass
 
class Enrollment(models.Model):
    '''存储已报名学员的信息'''
    pass
 
class CustomerFollowUp(models.Model):
    '''存储客户的后续跟进信息'''
    pass
 
class ClassList(models.Model):
    '''存储班级信息'''
    pass
 
class Course(models.Model):
    '''存储所开设课程的信息'''
    pass
 
class CourseRecord(models.Model):
    '''存储各班级的上课记录'''
    pass
 
class StudyRecord(models.Model):
    '''存储所有学员的详细的学习成绩情况'''
    pass
 
class UserProfile(models.Model):
    '''存储所有讲师\销售人员\ 学员 用户信息'''
    pass
 
 
class Role(models.Model):
    '''角色信息'''
    pass
 
class Branch(models.Model):
    '''存储所有校区'''
    pass

接下来分别解释每张表

1. Customer表, 主要给销售人员用, 存储所有客户信息,里面要记录客户来源\姓名\qq \客户来源\咨询的内容等

class Customer(models.Model):
'''存储所有客户信息'''
#客户在咨询时,多是通过qq,所以这里就把qq号做为唯一标记客户的值,不能重复
qq = models.CharField(max_length=64,unique=True,help_text=u'QQ号必须唯一')
qq_name = models.CharField(u'QQ名称',max_length=64,blank=True,null=True)
#客户只要没报名,你没理由要求人家必须告诉你真实姓名及其它更多私人信息呀
name = models.CharField(u'姓名',max_length=32,blank=True,null=True)
sex_type = (('male',u'男'),('female',u'女'))
sex = models.CharField(u"性别",choices=sex_type,default='male',max_length=32)
birthday = models.DateField(u'出生日期',max_length=64,blank=True,null=True,help_text="格式yyyy-mm-dd")
phone = models.BigIntegerField(u'手机号',blank=True,null=True)
email = models.EmailField(u'常用邮箱',blank=True,null=True)
id_num = models.CharField(u'身份证号',blank=True,null=True,max_length=64)
source_type = (('qq',u"qq群"),
('referral',u"内部转介绍"),
('website',u"官方网站"),
('baidu_ads',u"百度广告"),
('qq_class',u"腾讯课堂"),
('school_propaganda',u"高校宣讲"),
('51cto',u"51cto"),
('others',u"其它"),
)
#这个客户来源渠道是为了以后统计各渠道的客户量\成单量,先分类出来
source = models.CharField(u'客户来源',max_length=64, choices=source_type,default='qq')
#我们的很多新客户都是老学员转介绍来了,如果是转介绍的,就在这里纪录是谁介绍的他,前提这个介绍人必须是我们的老学员噢,要不然系统里找不到
referral_from = models.ForeignKey('self',verbose_name=u"转介绍自学员",help_text=u"若此客户是转介绍自内部学员,请在此处选择内部\学员姓名",blank=True,null=True,related_name="internal_referral")
#已开设的课程单独搞了张表,客户想咨询哪个课程,直接在这里关联就可以
course = models.ForeignKey("Course",verbose_name=u"咨询课程")
class_type_choices = (('online', u'网络班'),
('offline_weekend', u'面授班(周末)',),
('offline_fulltime', u'面授班(脱产)',),
)
class_type = models.CharField(u"班级类型",max_length=64,choices=class_type_choices)
customer_note = models.TextField(u"客户咨询内容详情",help_text=u"客户咨询的大概情况,客户个人信息备注等...")
work_status_choices = (('employed','在职'),('unemployed','无业'))
work_status = models.CharField(u"职业状态",choices=work_status_choices,max_length=32,default='employed')
company = models.CharField(u"目前就职公司",max_length=64,blank=True,null=True)
salary = models.CharField(u"当前薪资",max_length=64,blank=True,null=True)
status_choices = (('signed',u"已报名"),('unregistered',u"未报名"))
status = models.CharField(u"状态",choices=status_choices,max_length=64,default=u"unregistered",help_text=u"选择客户此时的状态")
#课程顾问很得要噢,每个招生老师录入自己的客户
consultant = models.ForeignKey("UserProfile",verbose_name=u"课程顾问")
date = models.DateField(u"咨询日期",auto_now_add=True) def __str__(self):
return u"QQ:%s -- Name:%s" %(self.qq,self.name) class Meta: #这个是用来在admin页面上展示的,因为默认显示的是表名,加上这个就变成中文啦
verbose_name = u'客户信息表'
verbose_name_plural = u"客户信息表"

Customer

2. 学员报名表,这里为什么要把客户信息表 和 这个学员报名表分开呢? 因内一个学员可以报多个课程 ,每个课程 都需要单独记录学习成绩呀什么的,所以每报一个课程 ,就在这里生成 一条相应的报名记录

class Enrollment(models.Model):
'''存储学员报名的信息''' #所有报名的学生 肯定是来源于客户信息表的,先咨询,后报名嘛
customer = models.ForeignKey(Customer)
school = models.ForeignKey('Branch', verbose_name='校区') #选择他报的班级,班级是关联课程的,比如python开发10期
course_grade = models.ForeignKey("ClassList", verbose_name="所报班级")
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=u"在审阅完学员的资料无误后勾选此项,合同即生效")
enrolled_date = models.DateTimeField(auto_now_add=True, auto_created=True,
verbose_name="报名日期")
memo = models.TextField('备注', blank=True, null=True) def __str__(self):
return "<%s 课程:%s>" %(self.customer ,self.course_grade) class Meta:
verbose_name = '学员报名表'
verbose_name_plural = "学员报名表"
unique_together = ("customer", "course_grade")
#这里为什么要做个unique_together联合唯一?因为老男孩有很多个课程, 学生学完了一个觉得好的话,以后还可以再报其它班级,
#每报一个班级,就得单独创建一条报名记录,所以这里想避免重复数据的话,就得搞个"客户 + 班级"的联合唯一喽

Enrollment

3. 客户跟进表,这张表的意义很容易理解, 一个客户今天咨询后,你录入到了客户信息表,但他没报名, 所以过了一段时间,销售还得再跟他聊聊吧,聊完后,结果又没报,那也得纪录下来吧,因为每个销售每天要聊很多人,你不纪录的话, 可能下次再跟这个人聊时,你早已忘记上次聊了什么了, 这样会让客户觉得这个销售不专业,相反,如果把每次跟进的内容都纪录下来, 过了几个月,这个客户再跟你聊时,竟然发现,你还记得他的所有情况,他就觉得你很重视他,说不定一感动就报名了,哈哈。 所以,一条客户信息可能会对应多条跟进记录,是个1对多的关系,必须单独搞张表来记录

class CustomerFollowUp(models.Model):
'''存储客户的后续跟进信息'''
customer = models.ForeignKey(Customer,verbose_name=u"所咨询客户")
note = models.TextField(u"跟进内容...")
status_choices = ((1,u"近期无报名计划"),
(2,u"2个月内报名"),
(3,u"1个月内报名"),
(4,u"2周内报名"),
(5,u"1周内报名"),
(6,u"2天内报名"),
(7,u"已报名"),
(8,u"已交全款"),
)
status = models.IntegerField(u"状态",choices=status_choices,help_text=u"选择客户此时的状态") consultant = models.ForeignKey("UserProfile",verbose_name=u"跟踪人")
date = models.DateField(u"跟进日期",auto_now_add=True) def __str__(self):
return u"%s, %s" %(self.customer,self.status) class Meta:
verbose_name = u'客户咨询跟进记录'
verbose_name_plural = u"客户咨询跟进记录"

CustomerFollowUp

4. 班级表, 学生以班级为单位管理,这个表被学员表反向关联, 即每个学员报名时需要选择班级

class ClassList(models.Model):
'''存储班级信息'''
#创建班级时需要选择这个班所学的课程
branch = models.ForeignKey("Branch",verbose_name="校区")
course = models.ForeignKey("Course",verbose_name=u"课程")
class_type_choices = ((0,'面授'),(1,'随到随学网络'))
class_type = models.SmallIntegerField(choices=class_type_choices,default=0)
total_class_nums = models.PositiveIntegerField("课程总节次",default=10)
semester = models.IntegerField(u"学期")
price = models.IntegerField(u"学费", default=10000)
start_date = models.DateField(u"开班日期")
graduate_date = models.DateField(u"结业日期", blank=True, null=True)
#选择这个班包括的讲师,可以是多个
teachers = models.ManyToManyField("UserProfile", verbose_name=u"讲师") def __str__(self):
return "%s(%s)" % (self.course, self.semester) class Meta:
verbose_name = u'班级列表'
verbose_name_plural = u"班级列表"
#为避免重复创建班级,课程名+学期做联合唯一
unique_together = ("course", "semester")
#自定义方法,反向查找每个班级学员的数量,在后台admin里 list_display加上这个"get_student_num"就可以看到
def get_student_num(self):
return "%s" % self.customer_set.select_related().count() get_student_num.short_description = u'学员数量'

ClassList

5. 课程表,存储课程介绍\大纲等基本信息

class Course(models.Model):
'''存储所开设课程的信息'''
name = models.CharField(u"课程名",max_length=64,unique=True)
description = models.TextField("课程描述")
outline = models.TextField("课程大纲")
period = models.IntegerField("课程周期(Month)") def __str__(self):
return self.name

Course

6. 上课纪录表,每个班级都要上很多次课,讲师每上一次课的纪录都要纪录下来,以后可以方便统计讲师工资什么的

class CourseRecord(models.Model):
'''存储各班级的上课记录''' #讲师创建上课纪录时要选择是上哪个班的课
course = models.ForeignKey(ClassList, verbose_name="班级(课程)")
day_num = models.IntegerField("节次", help_text="此处填写第几节课或第几天课程...,必须为数字")
date = models.DateField(auto_now_add=True, verbose_name="上课日期")
teacher = models.ForeignKey("UserProfile", verbose_name="讲师")
has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
homework_title = models.CharField(max_length=128,blank=True,null=True)
homework_requirement = models.TextField(blank=True,null=True) def __str__(self):
return "%s 第%s天" % (self.course, self.day_num) class Meta:
verbose_name = u'上课纪录'
verbose_name_plural = u"上课纪录"
unique_together = ('course', 'day_num')

CourseRecord

7. 学员学习纪录表,思考一下,如果你想实现纪录每位学员的详细学习纪录,即精确到每节课的成绩\出勤情况,怎么办?其实很简单, 先来看一下此时班级\上课纪录\学员学习纪录的关系图:

class StudyRecord(models.Model):
'''存储所有学员的详细的学习成绩情况'''
student = models.ForeignKey("Customer",verbose_name=u"学员")
course_record = models.ForeignKey(CourseRecord, verbose_name=u"第几天课程")
record_choices = (('checked', u"已签到"),
('late',u"迟到"),
('noshow',u"缺勤"),
('leave_early',u"早退"),
)
record = models.CharField(u"上课纪录",choices=record_choices,default="checked",max_length=64)
score_choices = ((100, 'A+'), (90,'A'),
(85,'B+'), (80,'B'),
(70,'B-'), (60,'C+'),
(50,'C'), (40,'C-'),
(-50,'D'), (0,'N/A'),
(-100,'COPY'), (-1000,'FAIL'),
)
score = models.IntegerField(u"本节成绩",choices=score_choices,default=-1)
date = models.DateTimeField(auto_now_add=True)
note = models.CharField(u"备注",max_length=255,blank=True,null=True) def __str__(self):
return u"%s,学员:%s,纪录:%s, 成绩:%s" %(self.course_record,self.student.name,self.record,self.get_score_display()) class Meta:
verbose_name = u'学员学习纪录'
verbose_name_plural = u"学员学习纪录"
#一个学员,在同一节课只可能出现一次,所以这里把course_record + student 做成联合唯一
unique_together = ('course_record','student')

StudyRecord

可以看出,一个班级对应多节课,每节课又对应多个学生的出勤和成绩,我们已经通过班级表(”ClassList”)和上课纪录表(”CourseRecord”)存储了班级信息和每节上课纪录,想存学生的每节课学习纪录的话只需要再搞一个表就可以了。

8. 用户表,存储销售、讲师账户信息

这里我们用django自带的认证系统,并对其进行自定制

class UserProfile(auth.AbstractBaseUser, auth.PermissionsMixin):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True, )
password = models.CharField(_('password'), max_length=128,
help_text=mark_safe('''<a class='btn-link' href='password'>重置密码</a>''')) is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(
verbose_name='staff status',
default=True,
help_text='Designates whether the user can log into this admin site.',
)
name = models.CharField(max_length=32)
#role = models.ForeignKey("Role",verbose_name="权限角色")
branch = models.ForeignKey("Branch",verbose_name="所属校区",blank=True,null=True)
roles = models.ManyToManyField('Role',blank=True)
memo = models.TextField('备注', blank=True, null=True, default=None)
date_joined = models.DateTimeField(blank=True, null=True, auto_now_add=True) USERNAME_FIELD = 'email'
# REQUIRED_FIELDS = ['name','token','department','tel','mobile','memo']
REQUIRED_FIELDS = ['name'] def get_full_name(self):
# The user is identified by their email address
return self.email def get_short_name(self):
# The user is identified by their email address
return self.email def __str__(self): # __str__ on Python 2
return self.email # def has_perm(self, perm, obj=None):
# "Does the user have a specific permission?"
# # Simplest possible answer: Yes, always
# return True
def has_perms(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
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
return True @property
def is_superuser(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin class Meta:
verbose_name = '用户信息'
verbose_name_plural = u"用户信息" objects = auth.UserManager() class Meta:
verbose_name = 'CRM账户'
verbose_name_plural = 'CRM账户'

UserProfile

9. 角色表,用于角色划分,用于权限管理,权限功能我们后面会实现,这里只在表里先存个简单的角色名

class Role(models.Model):
'''角色信息'''
name = models.CharField(max_length=32,unique=True)
menus = models.ManyToManyField('FirstLayerMenu',blank=True) def __str__(self):
return self.name

Role

10. 校区表,存储不同校区

class Branch(models.Model):
'''存储所有校区'''
name = models.CharField(max_length=64,unique=True)
def __str__(self):
return self.name

Branch

11. 菜单表,不同的角色看到的菜单不同, 我们支持动态菜单 ,所以需要把菜单 以及 和角色的关联存下来

class FirstLayerMenu(models.Model):
'''第一层侧边栏菜单'''
name = models.CharField('菜单名',max_length=64)
url_type_choices = ((0,'related_name'),(1,'absolute_url'))
url_type = models.SmallIntegerField(choices=url_type_choices,default=0)
url_name = models.CharField(max_length=64,unique=True)
order = models.SmallIntegerField(default=0,verbose_name='菜单排序')
sub_menus = models.ManyToManyField('SubMenu',blank=True) def __str__(self):
return self.name

FirstLayerMenu

12. 二级菜单表, 还可以支持2级子菜单

class SubMenu(models.Model):
'''第二层侧边栏菜单''' name = models.CharField('二层菜单名', max_length=64)
url_type_choices = ((0,'related_name'),(1,'absolute_url'))
url_type = models.SmallIntegerField(choices=url_type_choices,default=0)
url_name = models.CharField(max_length=64, unique=True)
order = models.SmallIntegerField(default=0, verbose_name='菜单排序') def __str__(self):
return self.name

SubMenu

13. 缴费记录

class PaymentRecord(models.Model):
enrollment = models.ForeignKey("Enrollment")
pay_type_choices = (('deposit', u"订金/报名费"),
('tution', u"学费"),
('refund', u"退款"),
)
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)
consultant = models.ForeignKey(UserProfile, verbose_name="负责老师", help_text="谁签的单就选谁") def __str__(self):
return "%s, 类型:%s,数额:%s" %(self.enrollment.customer, self.pay_type, self.paid_fee) class Meta:
verbose_name = '交款纪录'
verbose_name_plural = "交款纪录"

PaymentRecord

好啦,表基本都建完了,接下来同步数据库,不过在同步数据库前,要改一下settings.py, 因为我们自定义了django的认证表,所以需要明确的告诉django,用我们改过的过来做默认的认证系统。

在settings.py中添加以下行,格式为 app.modelname

1
AUTH_USER_MODEL = 'crm.UserProfile'

  

最后一步,同步数据库! 

1
2
python3 manage.py makemigrations
python3 manage.py migrate

  

伸个懒腰,出去撸一发,敬请期待下篇....




Django CRM系统的更多相关文章

  1. django模型系统(一)

    django模型系统(一) djangode ORM ORM:对像关系映射 用python概念去表达数据库 数据库配置(mysql) 安装pumysql 修改项目目录下的__init__.py imp ...

  2. CRM系统(第四部分)

      阅读目录 1.引入权限组件rbac 2.分配权限 3.登录.引入中间件 1.引入权限组件rbac 1.settings配置app.中间件   INSTALLED_APPS = [ ... ... ...

  3. CRM系统(第一部分)

      阅读目录 1.需求分析 2.数据库表设计 3.起步 4.录入数据 5.知识点 1.需求分析 CRM客户关系管理软件---> 学员管理 用户:企业内部用户 用户量: 业务场景: 2.数据库表设 ...

  4. CRM系统之stark组件流程分析

    CRM系统主要通过自定义stark组件来实现的(参照admin系统自定义): STARK组件: 1 admin组件 1 如何使用admin 2 admin源码 3 创建自己的admin组件:stark ...

  5. crm 系统项目(二) admin 后台操作表格

    crm 系统项目(二) admin 后台操作表格 1. app下创建 templates  运行的时候 先找全局的templates——> 按照app的注册顺序找templates中的文件 2. ...

  6. crm 系统项目(一) 登录,注册,校验

    crm 系统项目(一) 登录,注册,校验 首先创建一个Django项目,关于配置信息不多说,前面有~ models.py文件下创建需要的表格信息,之后导入数据库 from django.db impo ...

  7. 如何在CRM系统中集成ActiveReports最终报表设计器

    有时候,将ActiveReports设计器集成到业务系统中,为用户提供一些自定义的数据表,用户不需要了解如何底层的逻辑关系和后台代码,只需要选择几张关联的数据表,我们会根据用户的选择生成可供用户直接使 ...

  8. CRM系统简析

    寄语: 简单阐述一下对CRM系统应用的理解,此内容参考网上资料所整理. CRM是Customer Relationship Management的缩写,简称客户关系管理. CRM系统可以从三个方面来分 ...

  9. 面向企业客户的制造业CRM系统的不成熟思考

    CRM就是客户关系管理(Customer Relationship Management),一直一知半解,最近有涉及这方面的需求,所以稍作研究,并思考一些相关问题. CRM是什么? CRM具体如何定义 ...

随机推荐

  1. leetcode-三数之和(java)

     三数之和     给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组. 注意:答案中不可 ...

  2. I Hate It:线段树:单点修改+区间查询

    I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  3. OpenPAI大规模人工智能平台安装部署文档

    环境要求: 如果需要图形界面,需要在Ubuntu系统安装,否则centos系统安装时是没有问题的(web端和命令行进行任务提交) 安装过程需要有另外一台控制端机器(注意:区别于集群所在的任何一台服务器 ...

  4. C# 生成行和列

    private DataTable GetListBind() { DataTable dt = new DataTable(); try { dt.Columns.Add("1" ...

  5. SpringMVC拦截器实现登录认证(转发)

    感谢原作者,转发自:http://blog.csdn.net/u014427391/article/details/51419521 以Demo的形式讲诉拦截器的使用 项目结构如图: 需要的jar:有 ...

  6. Sparsity Invariant CNNs

    文章链接 Abstract 本文研究稀疏输入下的卷积神经网络,并将其应用于稀疏的激光扫描数据的深度信息完成实验.首先,我们表明,即使当丢失数据的位置提供给网络时,传统卷积网络在应用于稀疏数据时性能也很 ...

  7. .net下将exe改造为既能双击独立运行又能注册为windows服务

    最近项目中需要将一些业务的处理程序改造为windows服务,但是考虑到实际需求,也需要能够直接双击运行这些处理程序.首先第一步想到的就是原来的项目不变,只需要在加一个windows服务的项目就行.但是 ...

  8. CentOS 7 开放防火墙端口

    我:最近在使 CentOS 7时发现在本地不能访问linux上8080端口,以上是我的操作,修改后访问成功 CentOS 7 开放防火墙端口 命令 最近公司新的server要求用CentOS7, 发现 ...

  9. 大全Kafka Streams

    本文将从以下三个方面全面介绍Kafka Streams 一. Kafka Streams 概念 二. Kafka Streams 使用 三. Kafka Streams WordCount   一. ...

  10. cacti 添加mysql 监控 (远程服务器)

    监控主机 192.168.24.69 ,以下用A表示 被监控主机 192.168.24.79,以下用B标识   记得在A服务器的cacti中导入监控mysql的templates文件   1.在B上安 ...