使用自己的stark组件实现crm系统
因为stark用到了templates里面的html页面文件,所以要整合在一个app里,在stark里面创建名字为templates的PythonPackage,将之前的html页面拷贝在stark组件里面的templates里面,然后拷贝stark组件
二、实现crm逻辑
CRM即客户关系管理,是指企业用CRM技术来管理与客户之间的关系
1、创建数据库
在app01应用下的models.py文件:
fromdjango.dbimportmodelsclassDepartment(models.Model):"""部门表市场部1000销售1001"""title=models.CharField(verbose_name='部门名称',max_length=16)code=models.IntegerField(verbose_name='部门编号',unique=True,null=False)def__str__(self):returnself.titleclassUserInfo(models.Model):"""员工表"""name=models.CharField(verbose_name='员工姓名',max_length=16)email=models.EmailField(verbose_name='邮箱',max_length=64)depart=models.ForeignKey(verbose_name='部门',to="Department",to_field="code")def__str__(self):returnself.nameclassCourse(models.Model):"""课程表如:Linux基础Linux架构师Python自动化开发精英班Python自动化开发架构师班Python基础班go基础班"""name=models.CharField(verbose_name='课程名称',max_length=32)def__str__(self):returnself.nameclassSchool(models.Model):"""校区表如:北京沙河校区上海校区"""title=models.CharField(verbose_name='校区名称',max_length=32)def__str__(self):returnself.titleclassClassList(models.Model):"""班级表如:Python全栈面授班5期100002017-11-112018-5-11"""school=models.ForeignKey(verbose_name='校区',to='School')course=models.ForeignKey(verbose_name='课程名称',to='Course')semester=models.IntegerField(verbose_name="班级(期)")price=models.IntegerField(verbose_name="学费")start_date=models.DateField(verbose_name="开班日期")graduate_date=models.DateField(verbose_name="结业日期",null=True,blank=True)memo=models.CharField(verbose_name='说明',max_length=256,blank=True,null=True,)#teachers=models.ManyToManyField(verbose_name='任课老师',to='UserInfo',limit_choices_to={'depart_id__in':[1003,1004],})teachers=models.ManyToManyField(verbose_name='任课老师',to='UserInfo',related_name="abc",limit_choices_to={"depart_id__in":[1002,1003]})tutor=models.ForeignKey(verbose_name='班主任',to='UserInfo',related_name='classes',limit_choices_to={"depart":1005})def__str__(self):return"{0}({1}期)".format(self.course.name,self.semester)classCustomer(models.Model):"""客户表"""qq=models.CharField(verbose_name='qq',max_length=64,unique=True,help_text='QQ号必须唯一')name=models.CharField(verbose_name='学生姓名',max_length=16)gender_choices=((1,'男'),(2,'女'))gender=models.SmallIntegerField(verbose_name='性别',choices=gender_choices)education_choices=((1,'重点大学'),(2,'普通本科'),(3,'独立院校'),(4,'民办本科'),(5,'大专'),(6,'民办专科'),(7,'高中'),(8,'其他'))education=models.IntegerField(verbose_name='学历',choices=education_choices,blank=True,null=True,)graduation_school=models.CharField(verbose_name='毕业学校',max_length=64,blank=True,null=True)major=models.CharField(verbose_name='所学专业',max_length=64,blank=True,null=True)experience_choices=[(1,'在校生'),(2,'应届毕业'),(3,'半年以内'),(4,'半年至一年'),(5,'一年至三年'),(6,'三年至五年'),(7,'五年以上'),]experience=models.IntegerField(verbose_name='工作经验',blank=True,null=True,choices=experience_choices)work_status_choices=[(1,'在职'),(2,'无业')]work_status=models.IntegerField(verbose_name="职业状态",choices=work_status_choices,default=1,blank=True,null=True)company=models.CharField(verbose_name="目前就职公司",max_length=64,blank=True,null=True)salary=models.CharField(verbose_name="当前薪资",max_length=64,blank=True,null=True)source_choices=[(1,"qq群"),(2,"内部转介绍"),(3,"官方网站"),(4,"百度推广"),(5,"360推广"),(6,"搜狗推广"),(7,"腾讯课堂"),(8,"广点通"),(9,"高校宣讲"),(10,"渠道代理"),(11,"51cto"),(12,"智汇推"),(13,"网盟"),(14,"DSP"),(15,"SEO"),(16,"其它"),]source=models.SmallIntegerField('客户来源',choices=source_choices,default=1)referral_from=models.ForeignKey('self',blank=True,null=True,verbose_name="转介绍自学员",help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名",related_name="internal_referral")course=models.ManyToManyField(verbose_name="咨询课程",to="Course")status_choices=[(1,"已报名"),(2,"未报名")]status=models.IntegerField(verbose_name="状态",choices=status_choices,default=2,help_text=u"选择客户此时的状态")consultant=models.ForeignKey(verbose_name="课程顾问",to='UserInfo',related_name='consultanter',limit_choices_to={'depart_id':1001})date=models.DateField(verbose_name="咨询日期",auto_now_add=True)recv_date=models.DateField(verbose_name="当前课程顾问的接单日期",null=True)last_consult_date=models.DateField(verbose_name="最后跟进日期",)def__str__(self):returnself.nameclassConsultRecord(models.Model):"""客户跟进记录"""customer=models.ForeignKey(verbose_name="所咨询客户",to='Customer')consultant=models.ForeignKey(verbose_name="跟踪人",to='UserInfo',limit_choices_to={"depart_id":1001})date=models.DateField(verbose_name="跟进日期",auto_now_add=True)note=models.TextField(verbose_name="跟进内容...")def__str__(self):returnself.customer.name+":"+self.consultant.nameclassStudent(models.Model):"""学生表(已报名)"""customer=models.OneToOneField(verbose_name='客户信息',to='Customer')emergency_contract=models.CharField(max_length=32,blank=True,null=True,verbose_name='紧急联系人')class_list=models.ManyToManyField(verbose_name="已报班级",to='ClassList',blank=True)company=models.CharField(verbose_name='公司',max_length=128,blank=True,null=True)location=models.CharField(max_length=64,verbose_name='所在区域',blank=True,null=True)position=models.CharField(verbose_name='岗位',max_length=64,blank=True,null=True)salary=models.IntegerField(verbose_name='薪资',blank=True,null=True)welfare=models.CharField(verbose_name='福利',max_length=256,blank=True,null=True)date=models.DateField(verbose_name='入职时间',help_text='格式yyyy-mm-dd',blank=True,null=True)memo=models.CharField(verbose_name='备注',max_length=256,blank=True,null=True)def__str__(self):returnstr(self.customer)classClassStudyRecord(models.Model):"""上课记录表(班级记录)"""class_obj=models.ForeignKey(verbose_name="班级",to="ClassList")day_num=models.IntegerField(verbose_name="节次",help_text=u"此处填写第几节课或第几天课程...,必须为数字")teacher=models.ForeignKey(verbose_name="讲师",to='UserInfo',limit_choices_to={"depart_id__in":[1002,1003]})#过滤id为1002和1003的部门date=models.DateField(verbose_name="上课日期",auto_now_add=True)course_title=models.CharField(verbose_name='本节课程标题',max_length=64,blank=True,null=True)course_memo=models.TextField(verbose_name='本节课程内容概要',blank=True,null=True)has_homework=models.BooleanField(default=True,verbose_name="本节有作业")homework_title=models.CharField(verbose_name='本节作业标题',max_length=64,blank=True,null=True)homework_memo=models.TextField(verbose_name='作业描述',max_length=500,blank=True,null=True)exam=models.TextField(verbose_name='踩分点',max_length=300,blank=True,null=True)def__str__(self):return"{0}day{1}".format(self.class_obj,self.day_num)classStudentStudyRecord(models.Model):'''学生记录'''class_study_record=models.ForeignKey(verbose_name="第几天课程",to="ClassStudyRecord")student=models.ForeignKey(verbose_name="学员",to='Student')record_choices=(('checked',"已签到"),('vacate',"请假"),('late',"迟到"),('noshow',"缺勤"),('leave_early',"早退"),)record=models.CharField("上课纪录",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-'),(0,'D'),(-1,'N/A'),(-100,'COPY'),(-1000,'FAIL'),)score=models.IntegerField("本节成绩",choices=score_choices,default=-1)homework_note=models.CharField(verbose_name='作业评语',max_length=255,blank=True,null=True)note=models.CharField(verbose_name="备注",max_length=255,blank=True,null=True)homework=models.FileField(verbose_name='作业文件',blank=True,null=True,default=None)stu_memo=models.TextField(verbose_name='学员备注',blank=True,null=True)date=models.DateTimeField(verbose_name='提交作业日期',auto_now_add=True)def__str__(self):return"{0}-{1}".format(self.class_study_record,self.student)#数据库实例化,使用下面的命令#python3manage.pymakemigrations#python3manage.pymigrate
2、相关项目设置
settings.py文件设置内容:
INSTALLED_APPS=['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01.apps.App01Config',"stark.apps.StarkConfig"]STATIC_URL='/static/'STATICFILES_DIRS=[os.path.join(BASE_DIR,"static")]
注:
#static目录下面是Highcharts-6.1.0,是显示后面的柱形图所需的插件
#下载地址: https://www.hcharts.cn/download
3、逻辑部分及显示页面
urls.py文件:
fromdjango.conf.urlsimporturlfromdjango.contribimportadminfromstark.service.sitesimportsiteurlpatterns=[url(r'^admin/',admin.site.urls),url(r'^stark/',site.urls),]
stark组件service目录下sites.py文件:
fromdjango.conf.urlsimporturlfromdjango.shortcutsimportHttpResponse,render,redirectfromdjango.utils.safestringimportmark_safefromdjango.urlsimportreverseclassShow_List(object):def__init__(self,config,data_list):self.config=configself.data_list=data_listdefget_header(self):#处理表头#header_list=["ID","名称","价格"]header_list=[]forfieldinself.config.new_list_display():ifisinstance(field,str):iffield=="__str__":val=self.config.model._meta.model_name.upper()else:field_obj=self.config.model._meta.get_field(field)val=field_obj.verbose_nameelse:val=field(self.config,is_header=True)header_list.append(val)returnheader_listdefget_body(self):#处理表单数据new_data_list=[]forobjinself.data_list:temp=[]forfieldinself.config.new_list_display():#["nid","title","price","authors",edit]['__str__']["title","price"]ifisinstance(field,str):try:fromdjango.db.models.fields.relatedimportManyToManyFieldfield_obj=self.config.model._meta.get_field(field)ifisinstance(field_obj,ManyToManyField):l=[]foriingetattr(obj,field).all():l.append(str(i))val=",".join(l)else:val=getattr(obj,field)print("val",val)exceptExceptionase:val=getattr(obj,field)else:val=field(self.config,obj)temp.append(val)new_data_list.append(temp)returnnew_data_listdefget_new_actions(self):action_list=[]foriinself.config.actions:#[patch_init,]action_list.append({"desc":i.desc,"name":i.__name__,})returnaction_listclassModelStark():list_display=["__str__",]search_fields=[]actions=[]def__init__(self,model,site):self.model=modelself.site=sitedefedit(self,obj=None,is_header=False):ifis_header:return"操作"returnmark_safe("<ahref='%s/change'>编辑</a>"%obj.pk)defdelete(self,obj=None,is_header=False):ifis_header:return"操作"returnmark_safe("<ahref='%s/delete'>删除</a>"%obj.pk)defcheckbox(self,obj=None,is_header=False):ifis_header:return"选择"returnmark_safe("<inputtype='checkbox'name='selected_pk'value=%s>"%obj.pk)defget_list_url(self):model_name=self.model._meta.model_nameapp_label=self.model._meta.app_label_url=reverse("%s_%s_list"%(app_label,model_name))return_urldefnew_list_display(self):temp=[]temp.append(ModelStark.checkbox)temp.extend(self.list_display)temp.append(ModelStark.edit)temp.append(ModelStark.delete)returntempdefget_search_condition(self,request):fromdjango.db.modelsimportQsearch_condition=Q()val=request.GET.get("q")ifval:search_condition.connector="or"forfieldinself.search_fields:search_condition.children.append((field+"__contains",val))returnsearch_conditiondeflist_view(self,request):ifrequest.method=="POST":action=request.POST.get("action")selected_pk=request.POST.getlist("selected_pk")action=getattr(self,action)action(selected_pk)#searchsearch_condition=self.get_search_condition(request)#fliterfromdjango.db.modelsimportQfilter_condition=Q()forkey,valueinrequest.GET.items():filter_condition.children.append((key,value))data_list=self.model.objects.all().filter(search_condition).filter(filter_condition)print("list_display",self.list_display)#["nid","title","price",edit]sl=Show_List(self,data_list)returnrender(request,"list_view.html",locals())defget_mdoelForm(self):fromdjango.formsimportModelFormclassDemoModelForm(ModelForm):classMeta:model=self.modelfields="__all__"returnDemoModelFormdefadd(self,request):ifrequest.method=="POST":form=self.get_mdoelForm()(request.POST)ifform.is_valid():form.save()returnredirect(self.get_list_url())else:returnrender(request,"add.html",locals())form=form=self.get_mdoelForm()()returnrender(request,"add.html",locals())defchange(self,request,id):obj=self.model.objects.filter(pk=id).first()ifrequest.method=="POST":form=self.get_mdoelForm()(request.POST,instance=obj)ifform.is_valid():form.save()returnredirect(self.get_list_url())form=self.get_mdoelForm()(instance=obj)returnrender(request,"change.html",locals())defdelete_view(self,request,id):ifrequest.method=="POST":self.model.objects.get(pk=id).delete()returnredirect(self.get_list_url())url=self.get_list_url()returnrender(request,"delete.html",locals())defextra_url(self):return[]defget_urls2(self):model_name=self.model._meta.model_nameapp_label=self.model._meta.app_labeltemp=[url("^add/$",self.add,name="%s_%s_add"%(app_label,model_name)),url("^$",self.list_view,name="%s_%s_list"%(app_label,model_name)),url("^(\d+)/change/$",self.change,name="%s_%s_change"%(app_label,model_name)),url("^(\d+)/delete/$",self.delete_view,name="%s_%s_delete"%(app_label,model_name)),]temp.extend(self.extra_url())#额外添加一条urlreturntemp@propertydefurls2(self):returnself.get_urls2(),None,NoneclassStarkSite():def__init__(self,):self._registry={}#一级分发defget_urls(self):temp=[]formodel,model_class_objinself._registry.items():#{Book:ModelAdmin(Book),Publish:ModelAdmn(Publish),....}app_name=model._meta.app_labelmodel_name=model._meta.model_nametemp.append(url(r"%s/%s/"%(app_name,model_name),model_class_obj.urls2))returntemp@propertydefurls(self):returnself.get_urls(),None,Nonedefregister(self,model,admin_class=None,**options):ifnotadmin_class:admin_class=ModelStarkself._registry[model]=admin_class(model,self)site=StarkSite()
app01应用下的stark.py文件:
fromstark.service.sitesimportsite,ModelStarkfromdjango.utils.safestringimportmark_safefromapp01importmodelsfromdjango.conf.urlsimporturlfromdjango.shortcutsimportHttpResponse,redirect,renderfromdjango.httpimportJsonResponsesite.register(models.Department)site.register(models.School)site.register(models.UserInfo)site.register(models.Course)site.register(models.ClassList)classStudentconfig(ModelStark):#学生表的配置类defdisplay_score(self,obj=None,is_header=False):ifis_header:return"个人成绩"returnmark_safe("<ahref='score/%s'>个人成绩</a>"%obj.pk)list_display=["customer","class_list",display_score]#定义显示学生字段增加客户信息,已报班级,个人成绩三组字段defscore(self,request,sid):ifrequest.is_ajax():cid=request.GET.get("cid")sid=request.GET.get("sid")ret=student_study_record_list=list(models.StudentStudyRecord.objects.filter(student_id=sid,class_study_record__class_obj_id=cid).values_list("class_study_record__day_num","score"))print(ret)ret=[["day"+str(i[0]),i[1]]foriinret]#print(ret)returnJsonResponse(ret,safe=False)student=models.Student.objects.get(pk=sid)class_list=student.class_list.all()returnrender(request,"score.html",locals())defextra_url(self):temp=[]temp.append(url("score/(\d+)",self.score))returntempsite.register(models.Student,Studentconfig)site.register(models.Customer)site.register(models.ConsultRecord)classClassStudyRecordConfig(ModelStark):defdetail(self,obj=None,is_header=False):ifis_header:return"详细信息"returnmark_safe("<ahref='/stark/app01/studentstudyrecord/?class_study_record=%s'>详细信息</a>"%obj.pk)#url跳转到当前班级的详细信息defrecord_score(self,obj=None,is_header=False):ifis_header:return"录入成绩"returnmark_safe("<ahref='record_score/%s'>录入成绩</a>"%obj.pk)#url跳转到当前班级的录入成绩list_display=["class_obj","day_num",detail,record_score]defpatch_init(self,selected_pk):#定义批量初始化,对所有班级批量生成对应的学生记录对象classstudyrecord_list=self.model.objects.filter(pk__in=selected_pk)forclassstudyrecordinclassstudyrecord_list:student_list=models.Student.objects.filter(class_list=classstudyrecord.class_obj)forstudentinstudent_list:models.StudentStudyRecord.objects.create(class_study_record=classstudyrecord,student=student)patch_init.desc="批量初始化"actions=[patch_init]defrecord_score(self,request,id):csr=models.ClassStudyRecord.objects.get(pk=id)#班级学习对象student_study_record_list=models.StudentStudyRecord.objects.filter(class_study_record=csr)score_choices=models.StudentStudyRecord.score_choicesupdate=Falseifrequest.method=="POST":#提交数据print("POST",request.POST)forkey,valinrequest.POST.items():#页面取到的键值进行处理ifkey=="csrfmiddlewaretoken":continuefield,pk=key.rsplit("_",1)#以下划线作为分割标准,取右边第一个dic={field:val}models.StudentStudyRecord.objects.filter(pk=pk).update(**dic)#更新的字典,使用**update=Truereturnrender(request,"record_score.html",locals())defextra_url(self):temp=[]temp.append(url("record_score/(\d+)",self.record_score))returntempsite.register(models.ClassStudyRecord,ClassStudyRecordConfig)classStudentStudyRecordConfig(ModelStark):defdisplay_record(self,obj=None,is_header=False):ifis_header:return"考勤"returnobj.get_record_display()#返回需要的考勤状态defdisplay_score(self,obj=None,is_header=False):ifis_header:return"成绩"returnobj.get_score_display()#显示对应的后面的字母表示的成绩,而不是数字list_display=["student","class_study_record",display_record,display_score]defpatch_late(self,selected_pk):#批量修改考勤状态为迟到self.model.objects.filter(pk__in=selected_pk).update(record="late")patch_late.desc="迟到"actions=[patch_late]site.register(models.StudentStudyRecord,StudentStudyRecordConfig)
record_score.html录入成绩页面:
<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>Title</title><linkrel="stylesheet"href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.css"></head><body><h4>录入成绩</h4><div><div><div><formaction=""method="post">{%csrf_token%}<tableclass="tabletable-borderedtable-stripedtable-hover"><thead><tr><th>编号</th><th>姓名</th><th>考勤</th><th>成绩</th><th>批语</th></tr></thead><tbody>{%forstudent_study_recordinstudent_study_record_list%}<tr><td>{{forloop.counter}}</td><td>{{student_study_record.student}}</td><td>{{student_study_record.get_record_display}}</td><td><selectname="score_{{student_study_record.pk}}"id="">{#下拉框显示当前学生成绩#}{%forfooinscore_choices%}{%ifstudent_study_record.score==foo.0%}<optionselectedvalue="{{foo.0}}">{{foo.1}}</option>{#已经有成绩的显示器成绩,没有的显示默认#}{%else%}<optionvalue="{{foo.0}}">{{foo.1}}</option>{%endif%}{%endfor%}</select></td><td><textareaname="homework_note_{{student_study_record.pk}}"id=""cols="40"rows="5">{{student_study_record.homework_note|default_if_none:""}}</textarea></td></tr>{%endfor%}</tbody></table><inputtype="submit"class="btnbtn-warningpull-right"value="保存">{#保存按钮#}{%ifupdate%}<spanstyle="color:green">更新成功</span>{%endif%}</form></div></div></div></body><script>setTimeout(function(){document.getElementById("show_text").style.display="none";},2000);{#更新成功显示2秒后消失#}</script></html>
score.html查看个人成绩页面:
<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>Title</title><linkrel="stylesheet"href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.css"><scriptsrc="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script><scriptsrc="/static/Highcharts-6.1.0/code/highcharts.js"></script></head><body><h3>查看{{student}}成绩</h3><div><div><divclass="col-md-8col-md-offset-1"><tableclass="tabletable-borderedtable-stripedtable-hover"><thead><tr><th>编号</th><th>班级</th><th>班主任</th><th>成绩柱状图</th></tr></thead><tbody>{%forclsinclass_list%}<tr><td>{{forloop.counter}}</td><td>{{cls.course}}({{cls.semester}})</td><td>{{cls.tutor}}</td><td><acid="{{cls.pk}}">成绩柱状图</a></td></tr>{%endfor%}</tbody></table></div></div></div><divid="container"style="width:600px"></div><script>$(".chart_score").click(function(){varcid=$(this).attr("cid");$.ajax({url:"",type:"get",data:{sid:"{{student.pk}}",cid:cid},success:function(data){varchart=Highcharts.chart('container',{chart:{type:'column'},title:{text:'个人成绩分布图'},subtitle:{text:'数据截止2017-03,来源:<ahref="https://en.wikipedia.org/wiki/List_of_cities_proper_by_population">Wikipedia</a>'},xAxis:{type:'category',labels:{rotation:-45//设置轴标签旋转角度}},yAxis:{min:0,title:{text:'分数'}},legend:{enabled:false},tooltip:{pointFormat:'分数:<b>{point.y:.2f}百万</b>'},series:[{name:'总人口',data:data,dataLabels:{enabled:true,rotation:-90,color:'red',align:'right',format:'{point.y:.1f}',//:.1f为保留1位小数y:10}}]});}})})</script></body></html>
页面显示效果图如下:
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。