一、創建表

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)