1、需求分析

CRM客户关系管理软件---》 学员管理

用户:企业内部用户

用户量:

业务场景:

2、数据库表设计

1 、表之间的对应关系

  1. from django.db import models
  2.  
  3. class Customer(models.Model):
  4. '''客户信息表'''
  5. pass
  6.  
  7. class CustomerFollowUp(models.Model):
  8. '''客户跟进表'''
  9. pass
  10.  
  11. class Course(models.Model):
  12. '''课程表'''
  13. pass
  14.  
  15. class ClassList(models.Model):
  16. '''班级表'''
  17. pass
  18.  
  19. class CourseRecord(models.Model):
  20. '''上课记录'''
  21. pass
  22.  
  23. class StudyRecord(models.Model):
  24. '''学习记录'''
  25. pass
  26.  
  27. class Enrollment(models.Model):
  28. '''报名表'''
  29. pass
  30.  
  31. class UserProfile(models.Model):
  32. '''账号表'''
  33. pass
  34.  
  35. class Role(models.Model):
  36. '''角色表'''
  37. pass

2、详细表设计models.py

  1. from django.db import models
  2.  
  3. # Create your models here.
  4. class Department(models.Model):
  5. """
  6. 部门表
  7. 市场部 1000
  8. 销售 1001
  9.  
  10. """
  11. title = models.CharField(verbose_name='部门名称', max_length=16)
  12. code = models.IntegerField(verbose_name='部门编号', unique=True, null=False)
  13.  
  14. def __str__(self):
  15. return self.title
  16.  
  17. from rbac.models import User
  18. class UserInfo(models.Model):
  19. """
  20. 员工表
  21. """
  22.  
  23. name = models.CharField(verbose_name='员工姓名', max_length=16)
  24. email = models.EmailField(verbose_name='邮箱', max_length=64)
  25. depart = models.ForeignKey(verbose_name='部门', to="Department", to_field="code",on_delete=models.CASCADE)
  26.  
  27. user = models.OneToOneField(to=User, null=True, on_delete=True)
  28. def __str__(self):
  29. return self.name
  30.  
  31. class Course(models.Model):
  32. """
  33. 课程表
  34. 如:
  35. Linux基础
  36. Linux架构师
  37. Python自动化开发精英班
  38. Python自动化开发架构师班
  39. Python基础班
  40. go基础班
  41. """
  42. name = models.CharField(verbose_name='课程名称', max_length=32)
  43.  
  44. def __str__(self):
  45. return self.name
  46.  
  47. class School(models.Model):
  48. """
  49. 校区表
  50. 如:
  51. 北京沙河校区
  52. 上海校区
  53.  
  54. """
  55. title = models.CharField(verbose_name='校区名称', max_length=32)
  56.  
  57. def __str__(self):
  58. return self.title
  59.  
  60. class ClassList(models.Model):
  61. """
  62. 班级表
  63. 如:
  64. Python全栈 面授班 5期 10000 2017-11-11 2018-5-11
  65. """
  66. school = models.ForeignKey(verbose_name='校区', to='School',on_delete=models.CASCADE)
  67. course = models.ForeignKey(verbose_name='课程名称', to='Course',on_delete=models.CASCADE)
  68.  
  69. semester = models.IntegerField(verbose_name="班级(期)")
  70. price = models.IntegerField(verbose_name="学费")
  71. start_date = models.DateField(verbose_name="开班日期")
  72. graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True)
  73. memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True, )
  74. # teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo',limit_choices_to={'depart_id__in':[1003,1004],})
  75. teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo',related_name="abc",limit_choices_to={"depart__in":[1001,1005]})
  76. tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes',limit_choices_to={"depart":1003},on_delete=models.CASCADE)
  77.  
  78. def __str__(self):
  79. return "{0}({1}期)".format(self.course.name, self.semester)
  80.  
  81. class Customer(models.Model):
  82. """
  83. 客户表
  84. """
  85. qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ号必须唯一')
  86.  
  87. name = models.CharField(verbose_name='学生姓名', max_length=16)
  88. gender_choices = ((1, '男'), (2, '女'))
  89. gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices)
  90.  
  91. education_choices = (
  92. (1, '重点大学'),
  93. (2, '普通本科'),
  94. (3, '独立院校'),
  95. (4, '民办本科'),
  96. (5, '大专'),
  97. (6, '民办专科'),
  98. (7, '高中'),
  99. (8, '其他')
  100. )
  101. education = models.IntegerField(verbose_name='学历', choices=education_choices, blank=True, null=True, )
  102. graduation_school = models.CharField(verbose_name='毕业学校', max_length=64, blank=True, null=True)
  103. major = models.CharField(verbose_name='所学专业', max_length=64, blank=True, null=True)
  104.  
  105. experience_choices = [
  106. (1, '在校生'),
  107. (2, '应届毕业'),
  108. (3, '半年以内'),
  109. (4, '半年至一年'),
  110. (5, '一年至三年'),
  111. (6, '三年至五年'),
  112. (7, '五年以上'),
  113. ]
  114. experience = models.IntegerField(verbose_name='工作经验', blank=True, null=True, choices=experience_choices)
  115. work_status_choices = [
  116. (1, '在职'),
  117. (2, '无业')
  118. ]
  119. work_status = models.IntegerField(verbose_name="职业状态", choices=work_status_choices, default=1, blank=True,
  120. null=True)
  121. company = models.CharField(verbose_name="目前就职公司", max_length=64, blank=True, null=True)
  122. salary = models.CharField(verbose_name="当前薪资", max_length=64, blank=True, null=True)
  123.  
  124. source_choices = [
  125. (1, "qq群"),
  126. (2, "内部转介绍"),
  127. (3, "官方网站"),
  128. (4, "百度推广"),
  129. (5, "360推广"),
  130. (6, "搜狗推广"),
  131. (7, "腾讯课堂"),
  132. (8, "广点通"),
  133. (9, "高校宣讲"),
  134. (10, "渠道代理"),
  135. (11, "51cto"),
  136. (12, "智汇推"),
  137. (13, "网盟"),
  138. (14, "DSP"),
  139. (15, "SEO"),
  140. (16, "其它"),
  141. ]
  142. source = models.SmallIntegerField('客户来源', choices=source_choices, default=1)
  143. referral_from = models.ForeignKey(
  144. 'self',
  145. blank=True,
  146. null=True,
  147. verbose_name="转介绍自学员",
  148. help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名",
  149. related_name="internal_referral"
  150. ,on_delete=models.CASCADE)
  151. course = models.ManyToManyField(verbose_name="咨询课程", to="Course")
  152.  
  153. status_choices = [
  154. (1, "已报名"),
  155. (2, "未报名")
  156. ]
  157. status = models.IntegerField(
  158. verbose_name="状态",
  159. choices=status_choices,
  160. default=2,
  161. help_text=u"选择客户此时的状态"
  162. )
  163.  
  164. consultant = models.ForeignKey(verbose_name="课程顾问", to='UserInfo', related_name='consultanter',
  165. limit_choices_to={'depart_id': 1001},on_delete=models.CASCADE)
  166.  
  167. date = models.DateField(verbose_name="咨询日期", auto_now_add=True)
  168. recv_date = models.DateField(verbose_name="当前课程顾问的接单日期", null=True)
  169. last_consult_date = models.DateField(verbose_name="最后跟进日期", )
  170.  
  171. def __str__(self):
  172. return "姓名:{0},QQ:{1}".format(self.name, self.qq, )
  173.  
  174. class ConsultRecord(models.Model):
  175. """
  176. 客户跟进记录
  177. """
  178. customer = models.ForeignKey(verbose_name="所咨询客户", to='Customer',on_delete=models.CASCADE)
  179. consultant = models.ForeignKey(verbose_name="跟踪人", to='UserInfo',on_delete=models.CASCADE)
  180. date = models.DateField(verbose_name="跟进日期", auto_now_add=True)
  181. note = models.TextField(verbose_name="跟进内容...")
  182.  
  183. def __str__(self):
  184. return self.customer.name + ":" + self.consultant.name
  185.  
  186. class Student(models.Model):
  187. """
  188. 学生表(已报名)
  189. """
  190. customer = models.OneToOneField(verbose_name='客户信息', to='Customer',on_delete=models.CASCADE)
  191.  
  192. username = models.CharField(verbose_name='用户名', max_length=32)
  193. password = models.CharField(verbose_name='密码', max_length=64)
  194. emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人')
  195.  
  196. class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True)
  197. company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True)
  198. location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True)
  199. position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True)
  200. salary = models.IntegerField(verbose_name='薪资', blank=True, null=True)
  201. welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True)
  202. date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True)
  203. memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True)
  204.  
  205. def __str__(self):
  206. return self.username
  207.  
  208. class CourseRecord(models.Model):
  209. """
  210. 上课记录表
  211. """
  212. class_obj = models.ForeignKey(verbose_name="班级", to="ClassList",on_delete=models.CASCADE)
  213. day_num = models.IntegerField(verbose_name="节次", help_text="此处填写第几节课或第几天课程...,必须为数字")
  214. teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo',limit_choices_to={"depart_id__in":[1001,1005]},on_delete=models.CASCADE)
  215. date = models.DateField(verbose_name="上课日期", auto_now_add=True)
  216.  
  217. course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True)
  218. course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True)
  219. has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
  220. homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True)
  221. homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True)
  222. exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True)
  223.  
  224. def __str__(self):
  225. return "{0} day{1}".format(self.class_obj, self.day_num)
  226.  
  227. class StudyRecord(models.Model):
  228. course_record = models.ForeignKey(verbose_name="第几天课程", to="CourseRecord",on_delete=models.CASCADE)
  229. student = models.ForeignKey(verbose_name="学员", to='Student',on_delete=models.CASCADE)
  230. record_choices = (('checked', "已签到"),
  231. ('vacate', "请假"),
  232. ('late', "迟到"),
  233. ('noshow', "缺勤"),
  234. ('leave_early', "早退"),
  235. )
  236. record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
  237. score_choices = ((100, 'A+'),
  238. (90, 'A'),
  239. (85, 'B+'),
  240. (80, 'B'),
  241. (70, 'B-'),
  242. (60, 'C+'),
  243. (50, 'C'),
  244. (40, 'C-'),
  245. (0, ' D'),
  246. (-1, 'N/A'),
  247. (-100, 'COPY'),
  248. (-1000, 'FAIL'),
  249. )
  250. score = models.IntegerField("本节成绩", choices=score_choices, default=-1)
  251. homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True)
  252. note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True)
  253.  
  254. homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None)
  255. stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True)
  256. date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True)
  257.  
  258. def __str__(self):
  259. return "{0}-{1}".format(self.course_record, self.student)
  260.  
  261. class CustomerDistrbute(models.Model):
  262. customer = models.ForeignKey("Customer", related_name="customers",on_delete=models.CASCADE)
  263. consultant = models.ForeignKey(verbose_name="课程顾问", to="UserInfo", limit_choices_to={"depart_id": 1001},on_delete=models.CASCADE)
  264. date = models.DateField()
  265. status = (
  266. (1, "正在跟进"),
  267. (2, "已报名"),
  268. (3, "三天未跟进"),
  269. (4, "15天未成单"),
  270. )
  271. status = models.IntegerField(choices=status, default=1)
  272. memo = models.CharField(max_length=255)
  273.  
  274. def __str__(self):
  275. return self.customer.name+":"+self.consultant.name

3、起步

1、urls

  1. from django.conf.urls import url
  2. from django.contrib import admin
  3.  
  4. from stark.service.stark import site
  5. urlpatterns = [
  6. url(r'^admin/', admin.site.urls),
  7. url(r'^stark/', site.urls),
  8. ]

2、导入stark组件,配置

  将组件直接可以复制到CRM系统中,然后在app的setting中进行注册。

3、CRM的Stark配置

  1. from .models import *
  2. from stark.service.stark import site
  3.  
  4. site.register(Department)
  5. site.register(UserInfo)
  6. site.register(Course)
  7. site.register(School)
  8. site.register(ClassList)
  9. site.register(Customer)
  10. site.register(ConsultRecord)
  11. site.register(Student)
  12. site.register(CourseRecord)
  13. site.register(StudyRecord)
 

将CRM的所有表全部注册到Stark组件中。

4、录入数据

注册完成开始录入一些测试数据,注册成功会出现下面的URL。

1、School表

2、员工表UserInfo

3、班级表Classlist

4、客户表Customer

5、部门表Department

6、课程表Course

5、知识点

1、limit_choices_to

model与form组件的多对多关系如何渲染的

limit_choices_to

  1. teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo', related_name="abc",
  2. limit_choices_to={"depart__in": [1002, 1005]})
  3. tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes',
  4. limit_choices_to={"depart": 1001}, on_delete=True)

    #首先有一个limit_choice_to字段:
    teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo',related_name="abc",limit_choices_to={"depart__in":[1002,1005]})
    #这样限定他的部门只能是linux和python学部的员工为任课老师,可以把班主任限制在销售部的员工。

如下图:

2、自定制 display_classname

  1. class ClassConfig(ModelStark):
  2.  
  3. def display_classname(self,obj=None,header=False):
  4. if header:
  5. return "班级名称"
  6. class_name="%s(%s)"%(obj.course.name,str(obj.semester))
  7. return class_name
  8.  
  9. list_display = [display_classname,"tutor","teachers"]
  10.  
  11. site.register(ClassList,ClassConfig)

3、obj.get_gender_display()  显示option的值(Django自带方法)

  1. #通过这个方法,可以取到对应的choice的值,而不是序号1或者2
    class CusotmerConfig(ModelStark):
  2.  
  3. def display_gender(self,obj=None,header=False):
  4. if header:
  5. return "性别"
  6. return obj.get_gender_display()
  7.  
  8. # list_display = ["name",'gender',"consultant",]
  9. list_display = ["name",display_gender,"consultant",]

对比:

                                          

4.mark_safe()

通过mark_safe()可以让模板不进行转义,渲染成想要的标签等。

  1. from django.utils.safestring import mark_safe
  2.  
  3. class CusotmerConfig(ModelStark):
  4. def display_course(self,obj=None,header=False):
  5. #通过这个方法,可以取到对应的choice的值,而不是序号1或者2
  6. if header:
  7. return "咨询课程"
  8. else:
  9. temp=[]
  10. #这里的obj是customer表的实例对象,也就是当前记录的对象
  11. #对象正向查询按字段,直接.字段名.all()取出所有课程对象。
  12. #然后course.name取到所有的课程。
  13. for course in obj.course.all():
  14. #在这里拼一个a标签,url是取消课程用的,应该是某一个人取消某一门课程,
  15. 所以取到谁取消的,(obj.pk),取消哪一个课程(course.pk ,课程名字(用于展示)
  16. s = "<a href='/stark/crm/customer/cancel_course/%s/%s' style='border:1px solid #369;padding:3px 6px'><span>%s</span></a>&nbsp;" %(obj.pk, course.pk, course.name)
  17. temp.append(s)
  18.  
  19. return mark_safe("".join(temp))
  20. # list_display = ["name",'course',"consultant"]
  21. list_display = ["name",display_course,"consultant"]

5、extra_url()  额外的url接口

  1. #在stark组件中构建一个空的列表,别的表没有额外URL就是空列表,如果前面定义了extra_url方法,那么就可以添加额外的URL接口。
    def extra_url(self):
  2. return []

  3. def get_urls_2(self):
  4. temp = []
  5. model_name=self.model._meta.model_name
  6. app_label=self.model._meta.app_label
  7.  
  8. temp.append(url(r"^add/", self.add_view,name="%s_%s_add"%(app_label,model_name)))
  9. temp.append(url(r"^(\d+)/delete/", self.delete_view,name="%s_%s_delete"%(app_label,model_name)))
  10. temp.append(url(r"^(\d+)/change/", self.change_view,name="%s_%s_change"%(app_label,model_name)))
  11. temp.append(url(r"^$", self.list_view,name="%s_%s_list"%(app_label,model_name)))
  12.      #在这里调用这个方法,直接加继承一个额外的URL。
  13. temp.extend(self.extra_url())
  14.  
  15. return temp
  16.  
  17. #在CRM的Stark里面,这样调用方法,可以额外添加一个URL。

  def extra_url(self):
    temp=[]
    temp.append(url(r'record_score/(\d+)',self.score))
    return temp

  1.  

6、课程点击取消

  1. class CusotmerConfig(ModelStark):
  2.  
  3. def display_course(self,obj=None,header=False):
  4. if header:
  5. return "咨询课程"
  6. temp=[]
  7. for course in obj.course.all():
           #这里渲染出来课程的a标签。
  8. s="<a href='/stark/crm/customer/cancel_course/%s/%s' style='border:1px solid #369;padding:3px 6px'><span>%s</span></a>&nbsp;"%(obj.pk,course.pk,course.name,)
  9. temp.append(s)
  10. return mark_safe("".join(temp))
  11.  
  12. # list_display = ["name",'gender','course',"consultant",]
  13. list_display = ["name",display_gender,display_course,"consultant",]

   #点击a标签,从那个接口的url发一个请求,
   def cancel_course(self,request,customer_id,course_id):
     # print(customer_id,course_id)
       obj=self.model.objects.filter(pk=customer_id).first() #取到这个取消课程的客户对象。
      obj.course.remove(course_id) #这个客户把课程ID为某一个的课程取消掉。 #把对象通过字段找到的课程remove掉,传入remove的课程ID
      #最后重新定向回来展示页面,这样可以做出一个点击课程就可取消的效果。
      return redirect(self.get_list_url())

  1. def extra_url(self):
  2. temp=[]
  3. temp.append(url(r"cancel_course/(\d+)/(\d+)",self.cancel_course))
  4. return temp
  5.  
  6. site.register(Customer,CusotmerConfig)

6、代码

1、starkadmin代码

  1. from stark.service.stark import site,ModelStark
  2.  
  3. from .models import *
  4. site.register(School)
  5.  
  6. class UserConfig(ModelStark):
  7. list_display = ["name","email","depart"]
  8.  
  9. site.register(UserInfo,UserConfig)
  10.  
  11. class ClassConfig(ModelStark):
  12.  
  13. def display_classname(self,obj=None,header=False):
  14. if header:
  15. return "班级名称"
  16. class_name="%s(%s)"%(obj.course.name,str(obj.semester))
  17. return class_name
  18.  
  19. list_display = [display_classname,"tutor","teachers"]
  20.  
  21. site.register(ClassList,ClassConfig)
  22.  
  23. from django.utils.safestring import mark_safe
  24. from django.conf.urls import url
  25.  
  26. from django.shortcuts import HttpResponse,redirect
  27.  
  28. class CusotmerConfig(ModelStark):
  29.  
  30. def display_gender(self,obj=None,header=False):
  31. if header:
  32. return "性别"
  33. return obj.get_gender_display()
  34.  
  35. def display_course(self,obj=None,header=False):
  36. if header:
  37. return "咨询课程"
  38. temp=[]
  39. for course in obj.course.all():
  40. s="<a href='/stark/crm/customer/cancel_course/%s/%s' style='border:1px solid #369;padding:3px 6px'><span>%s</span></a>&nbsp;"%(obj.pk,course.pk,course.name,)
  41. temp.append(s)
  42. return mark_safe("".join(temp))
  43.  
  44. # list_display = ["name",'gender','course',"consultant",]
  45. list_display = ["name",display_gender,display_course,"consultant",]
  46.  
  47. def cancel_course(self,request,customer_id,course_id):
  48. print(customer_id,course_id)
  49.  
  50. obj=Customer.objects.filter(pk=customer_id).first()
  51. obj.course.remove(course_id)
  52. return redirect(self.get_list_url())
  53.  
  54. def extra_url(self):
  55.  
  56. temp=[]
  57.  
  58. temp.append(url(r"cancel_course/(\d+)/(\d+)",self.cancel_course))
  59.  
  60. return temp
  61.  
  62. site.register(Customer,CusotmerConfig)
  63. site.register(Department)
  64. site.register(Course)
  65. site.register(ConsultRecord)
  66. site.register(CourseRecord)
  67. site.register(StudyRecord)
  68. site.register(Student)
 

2、stark组件代码:

  1. from django.conf.urls import url
  2.  
  3. from django.shortcuts import HttpResponse,render,redirect
  4. from django.urls import reverse
  5. from django.db.models import Q
  6. from django.utils.safestring import mark_safe
  7.  
  8. from stark.utils.page import Pagination
  9. from django.db.models.fields.related import ManyToManyField,ForeignKey
  10.  
  11. class ShowList(object):
  12. def __init__(self,config,data_list,request):
  13. self.config=config
  14. self.data_list=data_list
  15. self.request=request
  16. #分页
  17. data_count=self.data_list.count()
  18. current_page=int(self.request.GET.get("page",1))
  19. base_path=self.request.path
  20.  
  21. self.pagination=Pagination(current_page,data_count,base_path,self.request.GET,per_page_num=10, pager_count=11, )
  22. self.page_data=self.data_list[self.pagination.start:self.pagination.end]
  23.  
  24. # actions
  25. self.actions=self.config.new_actions() # [patch_init,]
  26.  
  27. def get_filter_linktags(self):
  28. print("list_filter:",self.config.list_filter)
  29. link_dic={}
  30. import copy
  31.  
  32. for filter_field in self.config.list_filter: # ["title","publish","authors",]
  33. params = copy.deepcopy(self.request.GET)
  34.  
  35. cid=self.request.GET.get(filter_field,0)
  36.  
  37. print("filter_field",filter_field) # "publish"
  38. filter_field_obj=self.config.model._meta.get_field(filter_field)
  39. print("filter_field_obj",filter_field_obj)
  40. print(type(filter_field_obj))
  41. from django.db.models.fields.related import ForeignKey
  42. from django.db.models.fields.related import ManyToManyField
  43. print("rel======...",filter_field_obj.rel)
  44.  
  45. if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
  46. data_list=filter_field_obj.rel.to.objects.all()# publish1,publish2...】
  47. else:
  48. data_list=self.config.model.objects.all().values("pk",filter_field)
  49. print("data_list",data_list)
  50.  
  51. temp=[]
  52. # 处理 全部标签
  53. if params.get(filter_field):
  54. del params[filter_field]
  55. temp.append("<a href='?%s'>全部</a>"%params.urlencode())
  56. else:
  57. temp.append("<a class='active' href='#'>全部</a>")
  58.  
  59. # 处理 数据标签
  60. for obj in data_list:
  61. if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
  62. pk=obj.pk
  63. text=str(obj)
  64. params[filter_field] = pk
  65. else: # data_list= [{"pk":1,"title":"go"},....]
  66. print("========")
  67. pk=obj.get("pk")
  68. text=obj.get(filter_field)
  69. params[filter_field] =text
  70.  
  71. _url=params.urlencode()
  72. if cid==str(pk) or cid==text:
  73. link_tag="<a class='active' href='?%s'>%s</a>"%(_url,text)
  74. else:
  75. link_tag = "<a href='?%s'>%s</a>" % (_url, text)
  76. temp.append(link_tag)
  77.  
  78. link_dic[filter_field]=temp
  79.  
  80. return link_dic
  81.  
  82. def get_action_list(self):
  83. temp=[]
  84. for action in self.actions:
  85. temp.append({
  86. "name":action.__name__,
  87. "desc":action.short_description
  88. }) # [{"name":""patch_init,"desc":"批量初始化"}]
  89.  
  90. return temp
  91.  
  92. def get_header(self):
  93. # 构建表头
  94. header_list = []
  95. print("header",
  96. self.config.new_list_play()) # [checkbox,"pk","name","age",edit ,deletes] 【checkbox ,"__str__", edit ,deletes】
  97.  
  98. for field in self.config.new_list_play():
  99.  
  100. if callable(field):
  101. # header_list.append(field.__name__)
  102. val = field(self.config, header=True)
  103. header_list.append(val)
  104.  
  105. else:
  106. if field == "__str__":
  107. header_list.append(self.config.model._meta.model_name.upper())
  108. else:
  109. # header_list.append(field)
  110. val = self.config.model._meta.get_field(field).verbose_name
  111. header_list.append(val)
  112. return header_list
  113.  
  114. def get_body(self):
  115. # 构建表单数据
  116. new_data_list = []
  117. for obj in self.page_data:
  118. temp = []
  119.  
  120. for filed in self.config.new_list_play(): # ["__str__",] ["pk","name","age",edit]
  121.  
  122. if callable(filed):
  123. print("obj-----:",obj)
  124. val = filed(self.config, obj)
  125. else:
  126. try:
  127. field_obj=self.config.model._meta.get_field(filed)
  128. if isinstance(field_obj,ManyToManyField):
  129. ret = getattr(obj,filed).all()
  130. t=[]
  131. for mobj in ret:
  132. t.append(str(mobj))
  133. val=",".join(t)
  134. else:
  135.  
  136. val = getattr(obj, filed)
  137. if filed in self.config.list_display_links:
  138. # "app01/userinfo/(\d+)/change"
  139. _url = self.config.get_change_url(obj)
  140.  
  141. val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
  142.  
  143. except Exception as e:
  144. val = getattr(obj, filed)
  145.  
  146. temp.append(val)
  147.  
  148. new_data_list.append(temp)
  149. return new_data_list
  150.  
  151. '''
  152. [
  153. [1,"alex",12],
  154. [1,"alex",12],
  155. [1,"alex",12],
  156. [1,"alex",12],
  157.  
  158. ]
  159.  
  160. '''
  161.  
  162. class ModelStark(object):
  163.  
  164. list_display=["__str__",]
  165. list_display_links=[]
  166. modelform_class=None
  167. search_fields=[]
  168. actions = []
  169. list_filter=[]
  170.  
  171. def patch_delete(self, request, queryset):
  172.  
  173. queryset.delete()
  174.  
  175. patch_delete.short_description = "批量删除"
  176.  
  177. def __init__(self,model,site):
  178. self.model=model
  179. self.site=site
  180.  
  181. # 删除 编辑,复选框
  182. def edit(self,obj=None,header=False):
  183. if header:
  184. return "操作"
  185. #return mark_safe("<a href='%s/change'>编辑</a>"%obj.pk)
  186. _url=self.get_change_url(obj)
  187.  
  188. return mark_safe("<a href='%s'>编辑</a>"%_url)
  189.  
  190. def deletes(self,obj=None,header=False):
  191. if header:
  192. return "操作"
  193. # return mark_safe("<a href='%s/change'>编辑</a>"%obj.pk)
  194.  
  195. _url=self.get_delete_url(obj)
  196.  
  197. return mark_safe("<a href='%s'>删除</a>" % _url)
  198.  
  199. def checkbox(self,obj=None,header=False):
  200. if header:
  201. return mark_safe('<input id="choice" type="checkbox">')
  202.  
  203. return mark_safe('<input class="choice_item" type="checkbox" name="selected_pk" value="%s">'%obj.pk)
  204.  
  205. def get_modelform_class(self):
  206.  
  207. if not self.modelform_class:
  208. from django.forms import ModelForm
  209. from django.forms import widgets as wid
  210. class ModelFormDemo(ModelForm):
  211. class Meta:
  212. model = self.model
  213. fields = "__all__"
  214. labels={
  215. ""
  216. }
  217. return ModelFormDemo
  218. else:
  219. return self.modelform_class
  220.  
  221. def get_new_form(self,form):
  222.  
  223. for bfield in form:
  224. from django.forms.boundfield import BoundField
  225. print(bfield.field) # 字段对象
  226. print("name",bfield.name) # 字段名(字符串)
  227. print(type(bfield.field)) # 字段类型
  228. from django.forms.models import ModelChoiceField
  229. if isinstance(bfield.field,ModelChoiceField):
  230. bfield.is_pop=True
  231.  
  232. print("=======>",bfield.field.queryset.model) # 一对多或者多对多字段的关联模型表
  233.  
  234. related_model_name=bfield.field.queryset.model._meta.model_name
  235. related_app_label=bfield.field.queryset.model._meta.app_label
  236.  
  237. _url=reverse("%s_%s_add"%(related_app_label,related_model_name))
  238. bfield.url=_url+"?pop_res_id=id_%s"%bfield.name
  239.  
  240. return form
  241.  
  242. def add_view(self, request):
  243. ModelFormDemo = self.get_modelform_class()
  244. form = ModelFormDemo()
  245.  
  246. form=self.get_new_form(form)
  247.  
  248. if request.method=="POST":
  249. form = ModelFormDemo(request.POST)
  250. if form.is_valid():
  251. obj=form.save()
  252.  
  253. pop_res_id=request.GET.get("pop_res_id")
  254.  
  255. if pop_res_id:
  256. res ={"pk":obj.pk,"text":str(obj),"pop_res_id":pop_res_id}
  257. import json
  258. return render(request, "pop.html", {"res":res})
  259.  
  260. else:
  261. return redirect(self.get_list_url())
  262.  
  263. return render(request, "add_view.html", locals())
  264.  
  265. def delete_view(self, request, id):
  266. url = self.get_list_url()
  267. if request.method=="POST":
  268. self.model.objects.filter(pk=id).delete()
  269. return redirect(url)
  270.  
  271. return render(request, "delete_view.html", locals())
  272.  
  273. def change_view(self, request, id):
  274. ModelFormDemo = self.get_modelform_class()
  275. print("=====id",id)
  276. edit_obj = self.model.objects.filter(pk=id).first()
  277.  
  278. if request.method=="POST":
  279. form = ModelFormDemo(request.POST,instance=edit_obj)
  280. if form.is_valid():
  281. form.save()
  282. return redirect(self.get_list_url())
  283.  
  284. return render(request, "add_view.html", locals())
  285.  
  286. print("***********",edit_obj)
  287. form = ModelFormDemo(instance=edit_obj)
  288. form = self.get_new_form(form)
  289.  
  290. return render(request, "change_view.html", locals())
  291.  
  292. def new_list_play(self):
  293. temp=[]
  294. temp.append(ModelStark.checkbox)
  295. temp.extend(self.list_display)
  296. if not self.list_display_links:
  297. temp.append(ModelStark.edit)
  298. temp.append(ModelStark.deletes)
  299. return temp
  300.  
  301. def new_actions(self):
  302. temp=[]
  303. temp.append(ModelStark.patch_delete)
  304. temp.extend(self.actions)
  305.  
  306. return temp
  307.  
  308. def get_change_url(self,obj):
  309. model_name = self.model._meta.model_name
  310. app_label = self.model._meta.app_label
  311. print("obj===========",obj)
  312. _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,))
  313.  
  314. return _url
  315.  
  316. def get_delete_url(self, obj):
  317. model_name = self.model._meta.model_name
  318. app_label = self.model._meta.app_label
  319.  
  320. _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))
  321.  
  322. return _url
  323.  
  324. def get_add_url(self):
  325.  
  326. model_name = self.model._meta.model_name
  327. app_label = self.model._meta.app_label
  328.  
  329. _url = reverse("%s_%s_add" % (app_label, model_name))
  330.  
  331. return _url
  332.  
  333. def get_list_url(self):
  334.  
  335. model_name = self.model._meta.model_name
  336. app_label = self.model._meta.app_label
  337.  
  338. _url = reverse("%s_%s_list" % (app_label, model_name))
  339.  
  340. return _url
  341.  
  342. def get_serach_conditon(self,request):
  343. key_word = request.GET.get("q","")
  344. self.key_word=key_word
  345.  
  346. search_connection = Q()
  347. if key_word:
  348. # self.search_fields # ["title","price"]
  349. search_connection.connector = "or"
  350. for search_field in self.search_fields:
  351. search_connection.children.append((search_field + "__contains", key_word))
  352. return search_connection
  353.  
  354. def get_filter_condition(self,request):
  355. filter_condition=Q()
  356.  
  357. for filter_field,val in request.GET.items():
  358. if filter_field in self.list_filter:
  359. filter_condition.children.append((filter_field,val))
  360.  
  361. return filter_condition
  362.  
  363. def list_view(self, request):
  364. if request.method=="POST": # action
  365. print("POST:",request.POST)
  366. action=request.POST.get("action") # patch_init
  367. selected_pk=request.POST.getlist("selected_pk")
  368. action_func=getattr(self,action)
  369. queryset=self.model.objects.filter(pk__in=selected_pk)
  370. ret=action_func(request,queryset)
  371.  
  372. #return ret
  373.  
  374. # 获取serach的Q对象
  375. search_connection=self.get_serach_conditon(request)
  376.  
  377. # 获取filter构建Q对象
  378.  
  379. filter_condition=self.get_filter_condition(request)
  380.  
  381. # 筛选获取当前表所有数据
  382. data_list=self.model.objects.all().filter(search_connection).filter(filter_condition) # 【obj1,obj2,....】
  383.  
  384. # 按这ShowList展示页面
  385. showlist=ShowList(self,data_list,request)
  386.  
  387. # 构建一个查看URL
  388. add_url=self.get_add_url()
  389. return render(request, "list_view.html", locals())
  390.  
  391. def extra_url(self):
  392.  
  393. return []
  394.  
  395. def get_urls_2(self):
  396.  
  397. temp = []
  398.  
  399. model_name=self.model._meta.model_name
  400. app_label=self.model._meta.app_label
  401.  
  402. temp.append(url(r"^add/", self.add_view,name="%s_%s_add"%(app_label,model_name)))
  403. temp.append(url(r"^(\d+)/delete/", self.delete_view,name="%s_%s_delete"%(app_label,model_name)))
  404. temp.append(url(r"^(\d+)/change/", self.change_view,name="%s_%s_change"%(app_label,model_name)))
  405. temp.append(url(r"^$", self.list_view,name="%s_%s_list"%(app_label,model_name)))
  406.  
  407. temp.extend(self.extra_url())
  408.  
  409. return temp
  410.  
  411. @property
  412. def urls_2(self):
  413. print(self.model)
  414. return self.get_urls_2(), None, None
  415.  
  416. class StarkSite(object):
  417. def __init__(self):
  418. self._registry={}
  419.  
  420. def register(self,model,stark_class=None):
  421. if not stark_class:
  422. stark_class=ModelStark
  423.  
  424. self._registry[model] = stark_class(model, self)
  425.  
  426. def get_urls(self):
  427. temp=[]
  428. for model,stark_class_obj in self._registry.items():
  429. model_name=model._meta.model_name
  430. app_label=model._meta.app_label
  431. # 分发增删改查
  432. temp.append(url(r"^%s/%s/"%(app_label,model_name),stark_class_obj.urls_2))
  433.  
  434. '''
  435. url(r"^app01/userinfo/",UserConfig(Userinfo).urls_2),
  436. url(r"^app01/book/",ModelStark(Book).urls_2),
  437.  
  438. '''
  439. return temp
  440.  
  441. @property
  442. def urls(self):
  443.  
  444. return self.get_urls(),None,None
  445.  
  446. site=StarkSite()

CRM系统(第一部分)的更多相关文章

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

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

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

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

  3. CRM系统新思维

    客户关系管理系统(CRM系统)是管理公司当前以及未来潜在客户的系统,其主要目的是通过优化客户关系实现公司销售业绩的长期增长,它是企业信息系统的核心之一.目前,移动互联网.大数据以及人工智能技术发展日新 ...

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

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

  5. Day4作业:蛋疼CRM系统

    先上流程图,还得27寸4K显示器,画图各种爽: ReadMe: 运行程序前的提示: 1.抱歉,你得装prettytable模块...... 2.还得抱歉,如果shell中运行,最好把字体调得小点,表格 ...

  6. SaaS模式的CRM系统有哪些优势?

    早在10年前(2010年),就出现了SaaS模式的CRM系统.SaaS CRM一经面世,便迅速受到广大企业的青睐. SaaS CRM是指CRM厂家把CRM软件部署在自己的服务器上,有需要的客户能够根据 ...

  7. CRM系统简析

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

  8. 高校手机签到系统——第一部分Authority权限系统(下)

    很抱歉,之前寝室光纤断了,所以到现在才更新这个系列的第二篇博客.点击访问高校手机签到系统——第一部分Authority权限系统(上) 这几天我反思了一下上一篇写博上的方式,一味的贴代码式的,是否应该更 ...

  9. 报表平台对CRM系统价值几何

    CRM系统即客户关系管理系统,其利用信息科学技术实现市场营销.销售.服务等活动自动化,使企业能高效地为客户提供周到的服务,以提升客户满意度与忠诚度为目的的一种管理经营方式.而CRM报表平台作为一个枢纽 ...

随机推荐

  1. LeetCode算法题-First Unique Character in a String(Java实现)

    这是悦乐书的第213次更新,第226篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第81题(顺位题号是387).给定一个字符串,找到它中的第一个非重复字符并返回它的索引. ...

  2. 创建两个SAP系统之间的RFC信任关系

    一种常见的场景是企业运行着多个SAP系统(ERP/SRM/CRM),用户希望在AA1系统中使用BB1系统的事务.如果直接使用RFC调用另一系统的事务的话,则会弹出登陆框,让用户再次输入帐号密码... ...

  3. 简单理解Vue中的nextTick

    Vue中的nextTick涉及到Vue中DOM的异步更新,感觉很有意思,特意了解了一下.其中关于nextTick的源码涉及到不少知识,很多不太理解,暂且根据自己的一些感悟介绍下nextTick. 一. ...

  4. vue源码分析—数据绑定

    放进沙里附件拉萨就发牢骚:剑飞:啊撒

  5. vi/vim tab键空格数修改

    更改Tap键单位 vi/vim编辑器默认情况下,每按一次Tap相对于8个空格. (1)临时性更改 使用vi打开文件后,输入如下命令: :set tabstop=4 命令释义:更改为相当于四个空格. ( ...

  6. Tomcat配置(部分知识点)

    1.<Server>元素,shutdown属性表示关闭Server的指令:port属性表示Server接收shutdown指令的端口号,设为-1可以禁掉该端口 2.Connector的主要 ...

  7. UVA11212-Editing a Book(迭代加深搜索)

    Problem UVA11212-Editing a Book Accept:572  Submit:4428 Time Limit: 10000 mSec  Problem Description ...

  8. @property和@score.setter的用法

    @property 把属性装饰成get方法 给属性赋值时,会自动调用@property装饰的方法 只设置属性的@property 时,属性为只读 @score.setter 把属性装饰成set方法 给 ...

  9. php 10进制转62进制,可用于短网址生成

    <?php /** * 十进制数转换成62进制 * * @param integer $num * @return string */ function from10_to62($num) { ...

  10. SQLNET.AUTHENTICATION_SERVICES操作系统认证登录的设定

    $ORACLE_HOME/network/admin/sqlnet.ora 如果使用了SQLNET.AUTHENTICATION_SERVICES=(NTS)则说明可以使用OS认证就,只要conn / ...