前言: 今晚写一篇关于学员/讲师/销售员CRM系统。这个小项目是27号开始做的,大概搞了一星期不到。我把一些知识点总结下,还写下当时克服的BUG。
Django练习小项目:学员管理系统设计开发
带着项目需求学习是最有趣和效率最高的,今天就来基于下面的需求来继续学习Django
项目需求:
- 分讲师\学员\课程顾问角色
- 学员可以属于多个班级,学员成绩按课程分别统计
- 每个班级至少包含一个或多个讲师
- 一个学员要有状态转化的过程 ,比如未报名前,报名后,毕业老学员
- 客户要有咨询纪录, 后续的定期跟踪纪录也要保存
- 每个学员的所有上课出勤情况\学习成绩都要保存
- 学校可以有分校区,默认每个校区的员工只能查看和管理自己校区的学员
- 客户咨询要区分来源
拿到需求后,先要分析,再设计表结构: 超级重要!!
1 from django.db import models 2 3 from django.contrib.auth.models import User #django自带的用户认证表 4 # Create your models here. 5 course_type_choice = (("online", u"网络班"), 6 ("offline_weekend", u"面授班(周末)"), 7 ("offline_fulltime", u"面授班(脱产)"), 8 ) # 课程类型 9 10 class School(models.Model): #学校表 11 name = models.CharField(max_length=128, unique=True) 12 city = models.CharField(max_length=64) 13 addr = models.CharField(max_length=128) 14 15 def __str__(self): #给前端界面显示学校名 16 return self.name 17 18 19 class UserProfile(models.Model): #内部员工表 20 # User是一张表,在UserProfile关联User表,类似继承User表,也可以拓展别的字段 21 # 这里不能用ForeignKey(一对多),比如User表里有一个zcl, 22 # 用FK,则可以在UserProfile创建多个zcl用户,实际上UserProfile应当只有一个用户 23 # 用OneToOne关联,只能有一个UserProfile用户与User关联,其它用户不能关联, 24 # 在数据库层面OneToOne与ForeignKey实现是相同的,都是用FK, OneToOne是django admin层面做限制的 25 user = models.OneToOneField(User,verbose_name=u"登陆用户名") 26 name = models.CharField(max_length=64, verbose_name=u"全名") 27 school = models.ForeignKey("School") #比如领导可以管理多个学校,但有些老师就只能对应一个学校 28 user_type_choice = (("salespeople", u"销售员"), 29 ("teachers", u"讲师"), 30 ("others", u"其它"), 31 ) 32 user_type = models.CharField(verbose_name=u"用户类型",max_length=64, choices=user_type_choice, default="others") 33 34 def __str__(self): 35 return self.name 36 37 class Meta: 38 # 加上权限。can_del_customer是存在数据库中的,"可以删除用户"是显示在界面的 39 # permissions = (("can_del_customer",u"可以删除用户"),) 40 # 加入三条权限 41 permissions = (("view_customer_list",u"可以查看客户列表"), # 对销售员的权限 42 ("view_customer_info", u"可以查看客户详情"), 43 ("edit_own_customer_info", u"可以修改自己的客户信息"), 44 45 ("view_class_list", u"可以查看班级列表"), # 对讲师的权限 46 ("view_class_info", u"可以查看班级详情"), 47 ("edit_own_class_info", u"可以修改自己的班级信息"), 48 49 ) 50 51 52 class CustomerTrackRecord(models.Model): #客户跟踪记录表 53 customer = models.ForeignKey("Customer") #一个客户可有多个跟踪记录 54 track_record = models.TextField(u"跟踪记录") 55 track_date = models.DateField(auto_now_add=True) #跟踪日期 56 tracker = models.ForeignKey(UserProfile) #一条跟踪记录只能有一个追踪人 57 status_choices = ((1, u"近期无报名计划"), 58 (2, u"2个月内报名"), 59 (3, u"1个月内报名"), 60 (4, u"2周内报名"), 61 (5, u"1周内报名"), 62 (6, u"2天内报名"), 63 (7, u"已报名"), 64 ) 65 status = models.IntegerField(u"状态",choices=status_choices,help_text=u"选择客户此时的状态") 66 67 def __str__(self): 68 return self.customer.qq 69 70 71 class Course(models.Model): #课程表 72 name = models.CharField(max_length=64, unique=True) #课程名 73 online_price = models.IntegerField() #网络班课程价格 74 offline_price = models.IntegerField() #面授班课程价格 75 introduction = models.TextField() #课程介绍 76 77 def __str__(self): 78 return self.name 79 80 81 class ClassList(models.Model): # 班级表 82 course = models.ForeignKey(Course, verbose_name=u"课程") # 关联课程表 83 semester = models.IntegerField(verbose_name=u"学期") 84 teachers = models.ManyToManyField(UserProfile, verbose_name=u"讲师") # 多对多关联 85 start_date = models.DateField(verbose_name=u"开班日期") # 开班日期 86 graduate_date = models.DateField(blank=True,null=True) # 结业日期 87 # 课程类型 88 course_type = models.CharField(max_length=64, choices=course_type_choice,default="offline_weekend") 89 90 def __str__(self): 91 return "%s[%s期][%s]" % (self.course, self.semester, self.get_course_type_display()) 92 93 class Meta: 94 # 联合唯一,python网络班15期只能有一个 95 unique_together = ("course", "semester", "course_type") 96 97 98 class Customer(models.Model): # 学员表 99 qq = models.CharField(max_length=64, unique=True) 100 # 名字可为空,刚来咨询时不会告诉name 101 name = models.CharField(max_length=64, blank=True, null=True) 102 phone = models.BigIntegerField(blank=True, null=True) # 不用IntegerField,不够长 103 course = models.ForeignKey("Course") # 学员咨询的课程,只记录咨询的一个课程,若有多个可备注说明 104 course_type = models.CharField(verbose_name=u"课程类型", max_length=64, choices=course_type_choice, default="offline_weekend") 105 consult_memo = models.TextField(verbose_name=u"咨询备注") # 咨询内容 106 source_type_choice = (("qq", u"qq群"), 107 ("referral", u"内部转介绍"), 108 ("51CTO", u"51CTO"), 109 ("agent", u"招生代理"), 110 ("others", u"其它"), 111 ) #客户来源 112 source_type = models.CharField(max_length=64, choices=source_type_choice, default="others") 113 # 表示自关联(Customer表关联Customer表),也可用referral_from = models.ForeignKey("Customer") 114 # 1.加上self 2.自关联要加上related_name,通过internal_referral反查数据 115 # 反向关联得加上related_name: eg:A介绍B来上课,对A通过referral_from可找到B;反之需通过referral 116 # 该字段表示该学生被谁介绍来上课的 117 referral_from = models.ForeignKey("self", blank=True, null=True, related_name="referral") 118 119 status_choices = (("singed", u"已报名"), 120 ("unregistered", u"未报名"), 121 ("graduated", u"已毕业"), 122 ("drop_off", u"退学"), 123 ) # 客户来源 124 status = models.CharField(max_length=64, choices=status_choices, default="unregistered") 125 consultant = models.ForeignKey("UserProfile", verbose_name="课程顾问") 126 date = models.DateField(u"咨询日期", auto_now_add=True) # auto_now_add创建时自动添加当前日期 127 class_list = models.ManyToManyField("ClassList", blank=True) # 对于多对多字段,不需要null=true 128 129 def __str__(self): 130 return "%s[%s]" % (self.qq, self.name) 131 132 133 class CourseRecord(models.Model): # 上课记录表 134 class_obj = models.ForeignKey(ClassList) # 关联班级 135 day_num = models.IntegerField(u"第几节课") 136 course_date = models.DateField(auto_now_add=True, verbose_name=u"上课时间") 137 teacher = models.ForeignKey(UserProfile) # 讲师 138 139 # students = models.ManyToManyField(Customer) 不能在这里多对多,if do this,can't 查看出勤情况 140 def __str__(self): 141 return "%s[day%s]" % (self.class_obj, self.day_num) 142 143 class Meta: # 联合唯一 python自动化12期网络班 12;只能有一个12天 144 unique_together = ("class_obj", "day_num") 145 146 147 class StudyRecord(models.Model): 148 # 关联上课记录表,上课记录表有第几节课字段,同时也与ClassList关联,可知道是哪个班第几期 149 course_record = models.ForeignKey(CourseRecord) 150 student = models.ForeignKey(Customer) # 关联学员表 151 record_choices = (('checked', u"已签到"), 152 ('late',u"迟到"), 153 ('no_show',u"缺勤"), 154 ('leave_early',u"早退"), 155 ) 156 record = models.CharField(u"状态", choices=record_choices,default="no_show",max_length=64) 157 score_choices = ((100, 'A+'), 158 (90,'A'), 159 (85,'B+'), 160 (80,'B'), 161 (70,'B-'), 162 (60,'C+'), 163 (50,'C'), 164 (40,'C-'), 165 (0,'D'), 166 (-1,'N/A'), # 暂无成绩 167 (-100,'COPY'), 168 (-1000,'FAIL'), 169 ) 170 score = models.IntegerField(u"本节成绩",choices=score_choices,default=-1) 171 date = models.DateTimeField(auto_now_add=True) 172 note = models.CharField(u"备注",max_length=255,blank=True,null=True) 173 174 def __str__(self): 175 return "%s,%s,%s" % (self.course_record,self.student,self.get_record_display())