Django之路 - 项目实战之<教育行业CRM开发>
本节内容 业务痛点分析 项目需求讨论 使用场景分析 表结构设计 业务痛点分析
噢,对了,最后说一下这个项目涉及到的知识点,请确保你以充分掌握了下面所列知识点再学习此项目噢! <ol class="ol1">
项目需求讨论首先给我们的项目起个名字吧,这个系统要同时支持销售管理\学员管理\讲师管理等功能,功能比较杂,不能称之为严格意上的CRM,因为CRM一般只包括销售管理,but anyway,who cares,我们就叫它”老男孩crm管理系统”吧, 起好了名字,开始动手写之前, 肯定要把需求想清楚,需求想不清楚就开始写的话,等于给自己挖坑,你肯定不想出现写了5千行代码后,突然发现,需求搞错了,还要重新推到重来的事情吧。所以,现在静下心,把需求想明白,画好思维导图,跟同事多讨论应用场景,做了各方面论证后,再开始动工噢。 我们的系统的用户分3种,销售\学生\讲师,这3个角色关注的事情是不同的, 销售只关注招了多少学员,讲师关注自己管理的班级学习成绩怎样,学员只关注自己的学习成绩。 因此在想需求时,你要从每个角色出发,看他关心的是什么, 于是我画出了这个思维导图 根据思维导图,我们总结出以下具体需求:
使用场景分析为了更加理清我们的项目开发需求,在动手写代码前,建议再有一个业务场景分析的步骤,即从用户角度写出你使用这个项目的具体场景,我们这里分为销售、讲师、学员3个角色,我分别给每个角色列出了几个使用场景: 一. 销售:
二. 讲师:
三. 学员:
?好啦, 业务场景也分析完了,接下来我们终于可以开始搞事情啦! 表结构设计
? Create your models here.
class Customer(models.Model): class Enrollment(models.Model): class CustomerFollowUp(models.Model): class ClassList(models.Model): class Course(models.Model): class CourseRecord(models.Model): class StudyRecord(models.Model): class UserProfile(models.Model): class Role(models.Model): class Branch(models.Model): 接下来分别解释每张表 1. Customer表, 主要给销售人员用, 存储所有客户信息,里面要记录客户来源\姓名\qq \客户来源\咨询的内容等?
qq = models.CharField(max_length=64,unique=True,help_text=u= models.CharField(u,max_length=64,blank=True,null=
name = models.CharField(u,max_length=32,null== ((,u),(,u= models.CharField(u,choices=sex_type,default=,max_length=32= models.DateField(u,null=True,help_text== models.BigIntegerField(u,null== models.EmailField(u,null== models.CharField(u,max_length=64= ((,u,u,u,u,u,u,u,u
source = models.CharField(u,choices=source_type,default=
referral_from = models.ForeignKey(,verbose_name=u,help_text=u,related_name=
course = models.ForeignKey(,verbose_name=u= ((,u,u,u= models.CharField(u,choices== models.TextField(u,help_text=u= ((,),(,= models.CharField(u,choices=work_status_choices,default== models.CharField(u,null== models.CharField(u,null== ((,u),(,u= models.CharField(u,choices=status_choices,default=u,help_text=u
consultant = models.ForeignKey(,verbose_name=u= models.DateField(u,auto_now_add=
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> u<span style="color: #800000;">"</span><span style="color: #800000;">QQ:%s -- Name:%s</span><span style="color: #800000;">"</span> %<span style="color: #000000;">(self.qq,self.name)
</span><span style="color: #0000ff;">class</span> Meta: <span style="color: #008000;">#</span><span style="color: #008000;">这个是用来在admin页面上展示的,因为默认显示的是表名,加上这个就变成中文啦</span>
verbose_name = u<span style="color: #800000;">'</span><span style="color: #800000;">客户信息表</span><span style="color: #800000;">'</span><span style="color: #000000;">
verbose_name_plural </span>= u<span style="color: #800000;">"</span><span style="color: #800000;">客户信息表</span><span style="color: #800000;">"</span></pre>
2.?学员报名表,这里为什么要把客户信息表 和 这个学员报名表分开呢? 因内一个学员可以报多个课程 ,每个课程 都需要单独记录学习成绩呀什么的,所以每报一个课程 ,就在这里生成 一条相应的报名记录
3.?客户跟进表,这张表的意义很容易理解, 一个客户今天咨询后,你录入到了客户信息表,但他没报名, 所以过了一段时间,销售还得再跟他聊聊吧,聊完后,结果又没报,那也得纪录下来吧,因为每个销售每天要聊很多人,你不纪录的话, 可能下次再跟这个人聊时,你早已忘记上次聊了什么了, 这样会让客户觉得这个销售不专业,相反,如果把每次跟进的内容都纪录下来, 过了几个月,这个客户再跟你聊时,竟然发现,你还记得他的所有情况,他就觉得你很重视他,说不定一感动就报名了,哈哈。 所以,一条客户信息可能会对应多条跟进记录,是个1对多的关系,必须单独搞张表来记录 = models.ForeignKey(Customer,verbose_name=u= models.TextField(u= ((1,u2,u3,u4,u5,u6,u7,u8,u= models.IntegerField(u,help_text=u
consultant </span>= models.ForeignKey(<span style="color: #800000;">"</span><span style="color: #800000;">UserProfile</span><span style="color: #800000;">"</span>,verbose_name=u<span style="color: #800000;">"</span><span style="color: #800000;">跟踪人</span><span style="color: #800000;">"</span><span style="color: #000000;">)
date </span>= models.DateField(u<span style="color: #800000;">"</span><span style="color: #800000;">跟进日期</span><span style="color: #800000;">"</span>,auto_now_add=<span style="color: #000000;">True)
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> u<span style="color: #800000;">"</span><span style="color: #800000;">%s,%s</span><span style="color: #800000;">"</span> %<span style="color: #000000;">(self.customer,self.status)
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Meta:
verbose_name </span>= u<span style="color: #800000;">'</span><span style="color: #800000;">客户咨询跟进记录</span><span style="color: #800000;">'</span><span style="color: #000000;">
verbose_name_plural </span>= u<span style="color: #800000;">"</span><span style="color: #800000;">客户咨询跟进记录</span><span style="color: #800000;">"</span></pre>
4.?班级表, 学生以班级为单位管理,这个表被学员表反向关联, 即每个学员报名时需要选择班级
branch = models.ForeignKey(,verbose_name== models.ForeignKey(,verbose_name=u= ((0,),(1,= models.SmallIntegerField(choices=class_type_choices,default== models.PositiveIntegerField(,default=10= models.IntegerField(u= models.IntegerField(u,default=10000= models.DateField(u= models.DateField(u,null=
teachers = models.ManyToManyField(,verbose_name=u
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> <span style="color: #800000;">"</span><span style="color: #800000;">%s(%s)</span><span style="color: #800000;">"</span> %<span style="color: #000000;"> (self.course,self.semester)
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Meta:
verbose_name </span>= u<span style="color: #800000;">'</span><span style="color: #800000;">班级列表</span><span style="color: #800000;">'</span><span style="color: #000000;">
verbose_name_plural </span>= u<span style="color: #800000;">"</span><span style="color: #800000;">班级列表</span><span style="color: #800000;">"</span>
<span style="color: #008000;">#</span><span style="color: #008000;">为避免重复创建班级,课程名+学期做联合唯一</span>
unique_together = (<span style="color: #800000;">"</span><span style="color: #800000;">course</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">semester</span><span style="color: #800000;">"</span><span style="color: #000000;">)
</span><span style="color: #008000;">#</span><span style="color: #008000;">自定义方法,反向查找每个班级学员的数量,在后台admin里 list_display加上这个"get_student_num"就可以看到</span>
<span style="color: #0000ff;">def</span><span style="color: #000000;"> get_student_num(self):
</span><span style="color: #0000ff;">return</span> <span style="color: #800000;">"</span><span style="color: #800000;">%s</span><span style="color: #800000;">"</span> %<span style="color: #000000;"> self.customer_set.select_related().count()
get_student_num.short_description </span>= u<span style="color: #800000;">'</span><span style="color: #800000;">学员数量</span><span style="color: #800000;">'</span></pre>
5. 课程表,存储课程介绍\大纲等基本信息 = models.CharField(u,unique== models.TextField(= models.TextField(= models.IntegerField(
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> self.name</pre>
6.?上课纪录表,每个班级都要上很多次课,讲师每上一次课的纪录都要纪录下来,以后可以方便统计讲师工资什么的
7.?学员学习纪录表,思考一下,如果你想实现纪录每位学员的详细学习纪录,即精确到每节课的成绩\出勤情况,怎么办?其实很简单, 先来看一下此时班级\上课纪录\学员学习纪录的关系图: = models.ForeignKey(,verbose_name=u= models.ForeignKey(CourseRecord,verbose_name=u= ((,u,u,u,u= models.CharField(u,choices=record_choices,default=,max_length=64= ((100,),(90,85,),(80,70,),(60,50,),(40,-50,),(0,-100,),(-1000,= models.IntegerField(u,choices=score_choices,default=-1= models.DateTimeField(auto_now_add== models.CharField(u,max_length=255,null=
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> u<span style="color: #800000;">"</span><span style="color: #800000;">%s,学员:%s,纪录:%s,成绩:%s</span><span style="color: #800000;">"</span> %<span style="color: #000000;">(self.course_record,self.student.name,self.record,self.get_score_display())
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Meta:
verbose_name </span>= u<span style="color: #800000;">'</span><span style="color: #800000;">学员学习纪录</span><span style="color: #800000;">'</span><span style="color: #000000;">
verbose_name_plural </span>= u<span style="color: #800000;">"</span><span style="color: #800000;">学员学习纪录</span><span style="color: #800000;">"</span>
<span style="color: #008000;">#</span><span style="color: #008000;">一个学员,在同一节课只可能出现一次,所以这里把course_record + student 做成联合唯一</span>
unique_together = (<span style="color: #800000;">'</span><span style="color: #800000;">course_record</span><span style="color: #800000;">'</span>,<span style="color: #800000;">'</span><span style="color: #800000;">student</span><span style="color: #800000;">'</span>)</pre>
可以看出,一个班级对应多节课,每节课又对应多个学生的出勤和成绩,我们已经通过班级表(”ClassList”)和上课纪录表(”CourseRecord”)存储了班级信息和每节上课纪录,想存学生的每节课学习纪录的话只需要再搞一个表就可以了。 8. 用户表,存储销售、讲师账户信息 这里我们用django自带的认证系统,并对其进行自定制 ===255== models.CharField(_(),max_length=128=mark_safe(
is_active </span>= models.BooleanField(default=<span style="color: #000000;">True)
is_admin </span>= models.BooleanField(default=<span style="color: #000000;">False)
is_staff </span>=<span style="color: #000000;"> models.BooleanField(
verbose_name</span>=<span style="color: #800000;">'</span><span style="color: #800000;">staff status</span><span style="color: #800000;">'</span><span style="color: #000000;">,default</span>=<span style="color: #000000;">True,help_text</span>=<span style="color: #800000;">'</span><span style="color: #800000;">Designates whether the user can log into this admin site.</span><span style="color: #800000;">'</span><span style="color: #000000;">,)
name </span>= models.CharField(max_length=32<span style="color: #000000;">)
</span><span style="color: #008000;">#</span><span style="color: #008000;">role = models.ForeignKey("Role",verbose_name="权限角色")</span>
branch = models.ForeignKey(<span style="color: #800000;">"</span><span style="color: #800000;">Branch</span><span style="color: #800000;">"</span>,verbose_name=<span style="color: #800000;">"</span><span style="color: #800000;">所属校区</span><span style="color: #800000;">"</span>,null=<span style="color: #000000;">True)
roles </span>= models.ManyToManyField(<span style="color: #800000;">'</span><span style="color: #800000;">Role</span><span style="color: #800000;">'</span>,blank=<span style="color: #000000;">True)
memo </span>= models.TextField(<span style="color: #800000;">'</span><span style="color: #800000;">备注</span><span style="color: #800000;">'</span>,default=<span style="color: #000000;">None)
date_joined </span>= models.DateTimeField(blank=True,auto_now_add=<span style="color: #000000;">True)
USERNAME_FIELD </span>= <span style="color: #800000;">'</span><span style="color: #800000;">email</span><span style="color: #800000;">'</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> REQUIRED_FIELDS = ['name','token','department','tel','mobile','memo']</span>
REQUIRED_FIELDS = [<span style="color: #800000;">'</span><span style="color: #800000;">name</span><span style="color: #800000;">'</span><span style="color: #000000;">]
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_full_name(self):
</span><span style="color: #008000;">#</span><span style="color: #008000;"> The user is identified by their email address</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> self.email
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_short_name(self):
</span><span style="color: #008000;">#</span><span style="color: #008000;"> The user is identified by their email address</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> self.email
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span>(self): <span style="color: #008000;">#</span><span style="color: #008000;"> __str__ on Python 2</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> self.email
</span><span style="color: #008000;">#</span><span style="color: #008000;"> def has_perm(self,perm,obj=None):</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> "Does the user have a specific permission?"</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> # Simplest possible answer: Yes,always</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> return True</span>
<span style="color: #0000ff;">def</span> has_perms(self,obj=<span style="color: #000000;">None):
</span><span style="color: #800000;">"</span><span style="color: #800000;">Does the user have a specific permission?</span><span style="color: #800000;">"</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> Simplest possible answer: Yes,always</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> True
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> has_module_perms(self,app_label):
</span><span style="color: #800000;">"</span><span style="color: #800000;">Does the user have permissions to view the app `app_label`?</span><span style="color: #800000;">"</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> Simplest possible answer: Yes,always</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> True
@property
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> is_superuser(self):
</span><span style="color: #800000;">"</span><span style="color: #800000;">Is the user a member of staff?</span><span style="color: #800000;">"</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> Simplest possible answer: All admins are staff</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> self.is_admin
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Meta:
verbose_name </span>= <span style="color: #800000;">'</span><span style="color: #800000;">用户信息</span><span style="color: #800000;">'</span><span style="color: #000000;">
verbose_name_plural </span>= u<span style="color: #800000;">"</span><span style="color: #800000;">用户信息</span><span style="color: #800000;">"</span><span style="color: #000000;">
objects </span>=<span style="color: #000000;"> auth.UserManager()
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Meta:
verbose_name </span>= <span style="color: #800000;">'</span><span style="color: #800000;">CRM账户</span><span style="color: #800000;">'</span><span style="color: #000000;">
verbose_name_plural </span>= <span style="color: #800000;">'</span><span style="color: #800000;">CRM账户</span><span style="color: #800000;">'</span></pre>
9.?角色表,用于角色划分,用于权限管理,权限功能我们后面会实现,这里只在表里先存个简单的角色名 = models.CharField(max_length=32,unique== models.ManyToManyField(,blank=
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> self.name</pre>
10.?校区表,存储不同校区 = models.CharField(max_length=64,unique= self.name
11.?菜单表,不同的角色看到的菜单不同, 我们支持动态菜单 ,所以需要把菜单 以及 和角色的关联存下来 = models.CharField(,max_length=64= ((0,),= models.SmallIntegerField(choices=url_type_choices,default== models.CharField(max_length=64,unique== models.SmallIntegerField(default=0,verbose_name== models.ManyToManyField(,blank=
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> self.name</pre>
12. 二级菜单表, 还可以支持2级子菜单 name </span>= models.CharField(<span style="color: #800000;">'</span><span style="color: #800000;">二层菜单名</span><span style="color: #800000;">'</span>,verbose_name=<span style="color: #800000;">'</span><span style="color: #800000;">菜单排序</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> self.name</pre>
13. 缴费记录 = models.ForeignKey(= ((,u,u,u= models.CharField(,choices=pay_type_choices,default== models.IntegerField(,default== models.TextField(,null== models.DateTimeField(,auto_now_add== models.ForeignKey(UserProfile,verbose_name=,help_text=
</span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__str__</span><span style="color: #000000;">(self):
</span><span style="color: #0000ff;">return</span> <span style="color: #800000;">"</span><span style="color: #800000;">%s,类型:%s,数额:%s</span><span style="color: #800000;">"</span> %<span style="color: #000000;">(self.enrollment.customer,self.pay_type,self.paid_fee)
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Meta:
verbose_name </span>= <span style="color: #800000;">'</span><span style="color: #800000;">交款纪录</span><span style="color: #800000;">'</span><span style="color: #000000;">
verbose_name_plural </span>= <span style="color: #800000;">"</span><span style="color: #800000;">交款纪录</span><span style="color: #800000;">"</span></pre>
好啦,表基本都建完了,接下来同步数据库,不过在同步数据库前,要改一下settings.py,因为我们自定义了django的认证表,所以需要明确的告诉django,用我们改过的过来做默认的认证系统。 在settings.py中添加以下行,格式为 app.modelname?
最后一步,同步数据库!?
伸个懒腰,出去撸一发,敬请期待下篇....? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |