对于stark(curd)插件的使用简单介绍
一、创建表
1 from django.db import models 2 3 from django.db import models 4 5 class Department(models.Model): 6 """ 7 部门表 8 市场部 1000 9 销售 1001 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 18 class UserInfo(models.Model): 19 """ 20 员工表 21 """ 22 # auth = models.OneToOneField(verbose_name='用户权限', to=rbac_model.User) 23 name = models.CharField(verbose_name='员工姓名', max_length=16) 24 username = models.CharField(verbose_name='用户名', max_length=32) 25 password = models.CharField(verbose_name='密码', max_length=64) 26 email = models.EmailField(verbose_name='邮箱', max_length=64) 27 28 depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code") 29 30 def __str__(self): 31 return self.name 32 33 34 class Course(models.Model): 35 """ 36 课程表 37 如: 38 Linux基础 39 Linux架构师 40 Python自动化开发精英班 41 Python自动化开发架构师班 42 """ 43 name = models.CharField(verbose_name='课程名称', max_length=32) 44 45 def __str__(self): 46 return self.name 47 48 49 class School(models.Model): 50 """ 51 校区表 52 如: 53 北京海淀校区 54 北京昌平校区 55 上海虹口校区 56 广州白云山校区 57 """ 58 title = models.CharField(verbose_name='校区名称', max_length=32) 59 60 def __str__(self): 61 return self.title 62 63 64 class ClassList(models.Model): 65 """ 66 班级表 67 如: 68 Python全栈 面授班 5期 10000 2017-11-11 2018-5-11 69 """ 70 school = models.ForeignKey(verbose_name='校区', to='School') 71 course = models.ForeignKey(verbose_name='课程名称', to='Course') 72 73 semester = models.IntegerField(verbose_name="班级(期)") 74 price = models.IntegerField(verbose_name="学费") 75 start_date = models.DateField(verbose_name="开班日期") 76 graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True) 77 memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True) 78 teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo', related_name='teach_classes') 79 tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes') 80 81 def __str__(self): 82 return "{0}({1}期)".format(self.course.name, self.semester) 83 84 85 class Customer(models.Model): 86 """ 87 客户表 88 """ 89 qq = models.CharField(verbose_name='qq', max_length=64, unique=True, help_text='QQ号必须唯一') 90 91 name = models.CharField(verbose_name='学生姓名', max_length=16) 92 gender_choices = ((1, '男'), (2, '女')) 93 gender = models.SmallIntegerField(verbose_name='性别', choices=gender_choices) 94 95 education_choices = ( 96 (1, '重点大学'), 97 (2, '普通本科'), 98 (3, '独立院校'), 99 (4, '民办本科'), 100 (5, '大专'), 101 (6, '民办专科'), 102 (7, '高中'), 103 (8, '其他') 104 ) 105 education = models.IntegerField(verbose_name='学历', choices=education_choices, blank=True, null=True, ) 106 graduation_school = models.CharField(verbose_name='毕业学校', max_length=64, blank=True, null=True) 107 major = models.CharField(verbose_name='所学专业', max_length=64, blank=True, null=True) 108 109 experience_choices = [ 110 (1, '在校生'), 111 (2, '应届毕业'), 112 (3, '半年以内'), 113 (4, '半年至一年'), 114 (5, '一年至三年'), 115 (6, '三年至五年'), 116 (7, '五年以上'), 117 ] 118 experience = models.IntegerField(verbose_name='工作经验', blank=True, null=True, choices=experience_choices) 119 work_status_choices = [ 120 (1, '在职'), 121 (2, '无业') 122 ] 123 work_status = models.IntegerField(verbose_name="职业状态", choices=work_status_choices, default=1, blank=True, 124 null=True) 125 company = models.CharField(verbose_name="目前就职公司", max_length=64, blank=True, null=True) 126 salary = models.CharField(verbose_name="当前薪资", max_length=64, blank=True, null=True) 127 128 source_choices = [ 129 (1, "qq群"), 130 (2, "内部转介绍"), 131 (3, "官方网站"), 132 (4, "百度推广"), 133 (5, "360推广"), 134 (6, "搜狗推广"), 135 (7, "腾讯课堂"), 136 (8, "广点通"), 137 (9, "高校宣讲"), 138 (10, "渠道代理"), 139 (11, "51cto"), 140 (12, "智汇推"), 141 (13, "网盟"), 142 (14, "DSP"), 143 (15, "SEO"), 144 (16, "其它"), 145 ] 146 source = models.SmallIntegerField('客户来源', choices=source_choices, default=1) 147 referral_from = models.ForeignKey( 148 'self', 149 blank=True, 150 null=True, 151 verbose_name="转介绍自学员", 152 help_text="若此客户是转介绍自内部学员,请在此处选择内部学员姓名", 153 related_name="internal_referral" 154 ) 155 course = models.ManyToManyField(verbose_name="咨询课程", to="Course") 156 157 status_choices = [ 158 (1, "已报名"), 159 (2, "未报名") 160 ] 161 status = models.IntegerField( 162 verbose_name="状态", 163 choices=status_choices, 164 default=2, 165 help_text=u"选择客户此时的状态" 166 ) 167 consultant = models.ForeignKey(verbose_name="课程顾问", to='UserInfo', related_name='consultant',limit_choices_to={'depart_id':1001}) 168 date = models.DateField(verbose_name="咨询日期", auto_now_add=True) 169 last_consult_date = models.DateField(verbose_name="最后跟进日期", auto_now_add=True) 170 171 def __str__(self): 172 return "姓名:{0},QQ:{1}".format(self.name, self.qq, ) 173 174 175 class ConsultRecord(models.Model): 176 """ 177 客户跟进记录 178 """ 179 customer = models.ForeignKey(verbose_name="所咨询客户", to='Customer') 180 consultant = models.ForeignKey(verbose_name="跟踪人", to='UserInfo') 181 date = models.DateField(verbose_name="跟进日期", auto_now_add=True) 182 note = models.TextField(verbose_name="跟进内容...") 183 184 185 class PaymentRecord(models.Model): 186 """ 187 缴费记录 188 """ 189 customer = models.ForeignKey(Customer, verbose_name="客户") 190 191 class_list = models.ForeignKey(verbose_name="班级", to="ClassList", blank=True, null=True) 192 193 pay_type_choices = [ 194 (1, "订金/报名费"), 195 (2, "学费"), 196 (3, "转班"), 197 (4, "退学"), 198 (5, "退款"), 199 ] 200 pay_type = models.IntegerField(verbose_name="费用类型", choices=pay_type_choices, default=1) 201 paid_fee = models.IntegerField(verbose_name="费用数额", default=0) 202 turnover = models.IntegerField(verbose_name="成交金额", blank=True, null=True) 203 quote = models.IntegerField(verbose_name="报价金额", blank=True, null=True) 204 note = models.TextField(verbose_name="备注", blank=True, null=True) 205 date = models.DateTimeField(verbose_name="交款日期", auto_now_add=True) 206 consultant = models.ForeignKey(verbose_name="负责老师", to='UserInfo', help_text="谁签的单就选谁") 207 208 209 class Student(models.Model): 210 """ 211 学生表(已报名) 212 """ 213 customer = models.OneToOneField(verbose_name='客户信息', to='Customer') 214 215 username = models.CharField(verbose_name='用户名', max_length=32) 216 password = models.CharField(verbose_name='密码', max_length=64) 217 emergency_contract = models.CharField(max_length=32, blank=True, null=True, verbose_name='紧急联系人') 218 class_list = models.ManyToManyField(verbose_name="已报班级", to='ClassList', blank=True) 219 220 company = models.CharField(verbose_name='公司', max_length=128, blank=True, null=True) 221 location = models.CharField(max_length=64, verbose_name='所在区域', blank=True, null=True) 222 position = models.CharField(verbose_name='岗位', max_length=64, blank=True, null=True) 223 salary = models.IntegerField(verbose_name='薪资', blank=True, null=True) 224 welfare = models.CharField(verbose_name='福利', max_length=256, blank=True, null=True) 225 date = models.DateField(verbose_name='入职时间', help_text='格式yyyy-mm-dd', blank=True, null=True) 226 memo = models.CharField(verbose_name='备注', max_length=256, blank=True, null=True) 227 228 def __str__(self): 229 return self.username 230 231 232 class CourseRecord(models.Model): 233 """ 234 上课记录表 235 """ 236 class_obj = models.ForeignKey(verbose_name="班级", to="ClassList") 237 day_num = models.IntegerField(verbose_name="节次", help_text=u"此处填写第几节课或第几天课程...,必须为数字") 238 teacher = models.ForeignKey(verbose_name="讲师", to='UserInfo') 239 date = models.DateField(verbose_name="上课日期", auto_now_add=True) 240 241 course_title = models.CharField(verbose_name='本节课程标题', max_length=64, blank=True, null=True) 242 course_memo = models.TextField(verbose_name='本节课程内容概要', blank=True, null=True) 243 has_homework = models.BooleanField(default=True, verbose_name="本节有作业") 244 homework_title = models.CharField(verbose_name='本节作业标题', max_length=64, blank=True, null=True) 245 homework_memo = models.TextField(verbose_name='作业描述', max_length=500, blank=True, null=True) 246 exam = models.TextField(verbose_name='踩分点', max_length=300, blank=True, null=True) 247 248 def __str__(self): 249 return "{0} day{1}".format(self.class_obj, self.day_num) 250 251 252 class StudyRecord(models.Model): 253 course_record = models.ForeignKey(verbose_name="第几天课程", to="CourseRecord") 254 student = models.ForeignKey(verbose_name="学员", to='Student') 255 record_choices = (('checked', "已签到"), 256 ('vacate', "请假"), 257 ('late', "迟到"), 258 ('noshow', "缺勤"), 259 ('leave_early', "早退"), 260 ) 261 record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64) 262 score_choices = ((100, 'A+'), 263 (90, 'A'), 264 (85, 'B+'), 265 (80, 'B'), 266 (70, 'B-'), 267 (60, 'C+'), 268 (50, 'C'), 269 (40, 'C-'), 270 (0, ' D'), 271 (-1, 'N/A'), 272 (-100, 'COPY'), 273 (-1000, 'FAIL'), 274 ) 275 score = models.IntegerField("本节成绩", choices=score_choices, default=-1) 276 homework_note = models.CharField(verbose_name='作业评语', max_length=255, blank=True, null=True) 277 note = models.CharField(verbose_name="备注", max_length=255, blank=True, null=True) 278 279 homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None) 280 stu_memo = models.TextField(verbose_name='学员备注', blank=True, null=True) 281 date = models.DateTimeField(verbose_name='提交作业日期', auto_now_add=True) 282 283 def __str__(self): 284 return "{0}-{1}".format(self.course_record, self.student)
二、注册表需要知道的知识点
1、部门表相关
需求:默认编辑不要了,通过字段变成可点击的,具体是哪个字段不知道,可以自定义
edit_link = ["title"]
方法:拿到字段后判断这个字段是都在edit_link里面,
- 值变成可点击的
- 反向生成url
- 获取url的参数,拼接成_listfilter
具体实现:
StarkConfig类 # =============编辑链接================ edit_link = [] def get_edit_link(self): result = [] if self.edit_link: result.extend(self.edit_link) return result ChangeList类 self.edit_link = config.get_edit_link() def edit_link_tag(self,pk,text): params = QueryDict(mutable=True) query_str = self.request.GET.urlencode() #page=1 params[self.config._query_param_key] = query_str #要跳转的路径http://127.0.0.1:8080/index/crm/department/1/change/?_listfilter=None%3D return mark_safe("<a href='%s?%s'>%s</a>"%(self.config.get_change_url(pk),params.urlencode(),text)) def body_list(): #用于定制编辑列 if field_name in self.edit_link: val = self.edit_link_tag(row.pk,val)
4、=============重写get_list_display()方法================= 实现: # 重写get_list_display(),因为父类有这个方法,如果这里不写就会继承父类的,写了就优先执行自己的 def get_list_display(self): result = [] result.extend(self.list_display) result.insert(0,v1.StarkConfig.checkbox) result.append(v1.StarkConfig.edit) result.append(v1.StarkConfig.delete) return result 重写构造方法需要注意的: 函数和方法的区别:由于我们 val = field_name(self.config, row) ,传了一个self, 所以用第二种方式。用方式一就报错了 方式一:self.edit自己没有就找父类的,自己有就用自己的 方式二;v1.StarkConofig.edit 如果是函数自己写一个self =========写代码的时候注意加注释========================== 对类的注释 对于文件的注释 对于函数的注释
2、员工表
1、错误信息的显示
add_view
给errors加上个样式
2、自定义ModelForm def get_model_form_class(self): '''自定义ModelForm''' class MyModelForm(ModelForm): class Meta: model = models.UserInfo fields = "__all__" error_messages = { "name":{"required":"姓名不能为空"}, "username":{"required":"用户名不能为空"}, "password":{"required":"密码不能为空"}, "email":{"required":"邮箱不能为空","invalid":"邮箱格式不正确"}, "depart":{"required":"用户名不能不选",}, } return MyModelForm
3、组合搜索的筛选条件出错了:
组合搜索的筛选条件出错了: 出的错误:当你看到明明有数据,但是却没有查到 出错原因:是由于关联的字段变了depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code") 以前关联的是pk,现在关联的是code。所以对应的值就不一样了。所以找不到了 解决办法: 在FilterOption又加了两个参数 text_func_name :组合搜素时,组合搜索时,页面上生成显示文本的函数 val_func_name :组合搜素时,页面上生成a标签的value值的函数,也就是pk class FilterOption: def __init__(self, field_name, is_multi=False, is_choice=False, condition=None,text_func_name=None,val_func_name=None): self.text_func_name = text_func_name self.val_func_name = val_func_name # ================普通按钮==================== for val in self.data: #单选 if self.option.is_choice: # ((1,'男'),(2,'女'),(1,'男')) pk, text = str(val[0]),val[1] else: # 外键或多对多 '''现在的做法,是由于关联的字段变了depart = models.ForeignKey(verbose_name='部门', to="Department",to_field="code")''' text = str(self.option.text_func_name(val)) if self.option.text_func_name else str(val) pk = str(self.option.val_func_name(val)) if self.option.val_func_name else str(val.pk) #原来的做法 pk, text = str(val.pk), str(val) stark.py comb_filter = [ v1.FilterOption("depart",val_func_name=lambda x: x.code,), ]
stark.py
comb_filter = [ v1.FilterOption("depart",val_func_name=lambda x: x.code,), ] #分组搜索
3、班级管理
4、学校
5、课程
6、客户以及跟进记录
客户信息; 需求: 咨询课程 吧咨询课程以标签的形式显示 并且加样式一点击X就给删除了, 额外的增加个url def extra_url(self): ... 客户的状态。对于未报名的学生,有个跟进记录 跟进记录:一点跳转到详细的根进记录,这个根进记录的url 加上是否显示组合搜索。。。。。 自己可以查看自己的根进记录,不可以查看别人的(自己写一个视图函数 继承父类的方法, )
代码实现
class ConsultRecordConfig(v1.StarkConfig): def customer_display(self,obj=None,is_header=False): if is_header: return "所咨询客户" return obj.customer.name list_display = [customer_display,"consultant","date","note"] comb_filter = [ v1.FilterOption("customer"), ] #组合搜索默认不显示,但是却又筛选的功能 def change_views(self, request,*args, **kwargs): customer = request.GET.get('customer') # session中获取当前用户ID current_login_user_id = 6 ct = models.Customer.objects.filter(consultant=current_login_user_id, id=customer).count() if not ct: return HttpResponse('无权访问.') return super(ConsultRecordConfig,self).change_views(request, *args, **kwargs) v1.site.register(models.ConsultRecord,ConsultRecordConfig) v1.py # =============组合搜索==================== comb_filter = [] def get_comb_filter(self): result = [] if self.comb_filter: result.extend(self.comb_filter) return result show_comb_filter = False def get_show_comb_filter(self): return self.show_comb_filter self.show_comb_filter = config.get_show_comb_filter() 在页面 {% if cl.show_comb_filter %} <div class="list-filter"> {% for item in cl.gen_comb_filter %} <div> {% for col in item %} {{ col }} {% endfor %} </div> {% endfor %} </div> {% endif %}
图示:
三、注册表的具体使用方法
1 #!usr/bin/env python 2 # -*- coding:utf-8 -*- 3 from django.forms import ModelForm 4 from django.shortcuts import render,HttpResponse,redirect 5 from django.utils.safestring import mark_safe 6 from stark.service import v1 7 from crm import models 8 from django.conf.urls import url 9 10 class DepartmentConfig(v1.StarkConfig): 11 list_display = ["title","code"] 12 edit_link = ["title"] #自定制链接,指定字段可编辑 13 14 # 重写get_list_display(),因为父类有这个方法,如果这里不写就会继承父类的,写了就优先执行自己的 15 def get_list_display(self): 16 result = [] 17 18 result.extend(self.list_display) 19 result.insert(0,v1.StarkConfig.checkbox) 20 result.append(v1.StarkConfig.edit) 21 result.append(v1.StarkConfig.delete) 22 23 return result 24 25 v1.site.register(models.Department,DepartmentConfig) 26 27 class UserInfoConfig(v1.StarkConfig): 28 edit_link = ["name"] 29 30 def depart_dispaly(self,obj=None,is_header=False): 31 if is_header: 32 return "所属部门" 33 return obj.depart.title 34 35 def get_model_form_class(self): 36 '''自定义ModelForm''' 37 class MyModelForm(ModelForm): 38 class Meta: 39 model = models.UserInfo 40 fields = "__all__" 41 error_messages = { 42 "name":{"required":"姓名不能为空"}, 43 "username":{"required":"用户名不能为空"}, 44 "password":{"required":"密码不能为空"}, 45 "email":{"required":"邮箱不能为空","invalid":"邮箱格式不正确"}, 46 "depart":{"required":"用户名不能不选",}, 47 } 48 return MyModelForm 49 50 list_display = ["name","username","email",depart_dispaly] 51 52 comb_filter = [ 53 v1.FilterOption("depart",val_func_name=lambda x: x.code,), 54 ] #分组搜索 55 56 def delete_view(self, request,nid, *args, **kwargs): 57 '''重写视图函数''' 58 if request.method=="GET": 59 return render(request,"stark/delete_view.html",{"quxiao_url":self.get_list_url()}) 60 else: 61 self.model_class.objects.filter(pk=nid).delete() 62 return redirect(self.get_list_url()) 63 64 v1.site.register(models.UserInfo,UserInfoConfig) 65 66 class CourseConfig(v1.StarkConfig): 67 list_display = ["name"] 68 edit_link = ["name"] 69 show_actions =True #显示actions 70 71 def mutil_delete(self,request): 72 if request.method =="POST": 73 pk_list = request.POST.getlist("pk") 74 print(pk_list,"000") 75 self.model_class.objects.filter(id__in=pk_list).delete() 76 77 mutil_delete.short_desc = "批量删除" 78 def init_func(self): 79 pass 80 init_func.short_desc = "初始化" 81 actions = [mutil_delete,init_func] #actios操作 82 83 84 search_fields = ["name__contains"] #按照name搜索 85 show_search_form = True 86 87 v1.site.register(models.Course,CourseConfig) 88 89 class SchoolConfig(v1.StarkConfig): 90 list_display = ["title"] 91 edit_link = ["title"] 92 93 v1.site.register(models.School,SchoolConfig) 94 95 class ClassListConfig(v1.StarkConfig): 96 def teachers_display(self,obj=None,is_header=False): 97 if is_header: 98 return "任教老师" 99 user_list = obj.teachers.all() 100 html = [] 101 for i in user_list: 102 html.append(i.name) 103 return ','.join(html) 104 105 def display_graduate_date(self,obj=None,is_header=False): 106 if is_header: 107 return "结业日期" 108 return '' if not obj.graduate_date else obj.graduate_date 109 110 def display_memo(self,obj=None,is_header=False): 111 if is_header: 112 return "说明" 113 return '' if not obj.memo else obj.memo 114 115 def course_semester(self,obj=None,is_header=False): 116 if is_header: 117 return "课程(班级)" 118 return "%s(%s期)"%(obj.course,obj.semester) 119 120 #列举这个班级的人数 121 def num(self,obj=None,is_header=False): 122 if is_header: 123 return "人数" 124 print(obj.student_set.all().count()) 125 return obj.student_set.all().count() 126 list_display = ["school",course_semester,num,"price","start_date",display_graduate_date,display_memo,teachers_display,"tutor"] 127 edit_link = ["school"] 128 v1.site.register(models.ClassList,ClassListConfig) 129 130 class CustomerConfig(v1.StarkConfig): 131 def display_gender(self,obj=None,is_header=False): 132 if is_header: 133 return "性别" 134 return obj.get_gender_display() 135 def display_education(self,obj=None,is_header=False): 136 if is_header: 137 return "学历" 138 return obj.get_education_display() 139 140 def display_status(self, obj=None, is_header=False): 141 if is_header: 142 return '状态' 143 return obj.get_status_display() 144 def recode(self, obj=None, is_header=False): 145 if is_header: 146 return "跟进记录" 147 return mark_safe("<a href='/index/crm/consultrecord/?customer=%s'>查看跟进记录</a>" %(obj.pk,)) 148 149 def display_course(self,obj=None, is_header=False): 150 if is_header: 151 return "咨询课程" 152 course_list = obj.course.all() 153 html = [] 154 for item in course_list: 155 temp = "<a style='display:inline-block;padding:3px 5px;border:1px solid blue;margin:2px;' href='/index/crm/customer/%s/%s/dc/'>%s X</a>" %(obj.pk,item.pk,item.name) 156 html.append(temp) 157 return "".join(html) 158 159 def extra_urls(self): 160 # 由于没有路径,我们可以额外的增加一个路径,重新走一个delete_course视图 161 app_model_name = (self.model_class._meta.app_label, self.model_class._meta.model_name) 162 urlpatterns =[ 163 url(r'^(\d+)/(\d+)/dc/$', self.wrap(self.delete_course), name="%s_%s_delete" % app_model_name) 164 165 ] 166 return urlpatterns 167 def delete_course(self, request,customer_id,course_id): 168 ''' 169 删除当前用户感兴趣的课程 170 :param request: 171 :param customer_id: 172 :param course_id: 173 :return: 174 ''' 175 customer_obj = self.model_class.objects.filter(pk=customer_id).first() 176 customer_obj.course.remove(course_id) 177 return redirect(self.get_list_url()) 178 179 list_display = ["qq","name","graduation_school",display_course,display_gender,display_status,display_education,recode] 180 edit_link = ["name","graduation_school"] 181 search_fields = ["name__contains"] 182 show_search_form = True 183 184 show_actions = True 185 comb_filter = [ 186 v1.FilterOption("gender",is_choice=True), 187 ] 188 189 v1.site.register(models.Customer, CustomerConfig) 190 191 class ConsultRecordConfig(v1.StarkConfig): 192 def customer_display(self,obj=None,is_header=False): 193 if is_header: 194 return "所咨询客户" 195 return obj.customer.name 196 list_display = [customer_display,"consultant","date","note"] 197 comb_filter = [ 198 v1.FilterOption("customer"), 199 ] #组合搜索默认不显示,但是却又筛选的功能 200 201 def change_views(self, request,*args, **kwargs): 202 customer = request.GET.get('customer') 203 # session中获取当前用户ID 204 current_login_user_id = 6 205 ct = models.Customer.objects.filter(consultant=current_login_user_id, id=customer).count() 206 if not ct: 207 return HttpResponse('无权访问.') 208 return super(ConsultRecordConfig,self).change_views(request, *args, **kwargs) 209 v1.site.register(models.ConsultRecord,ConsultRecordConfig) 210 v1.site.register(models.Student)