您好,登錄后才能下訂單哦!
因為stark用到了templates里面的html頁面文件,所以要整合在一個app里,在stark里面創建名字為templates的Python Package,將之前的html頁面拷貝在stark組件里面的templates里面,然后拷貝stark組件
二、實現crm邏輯
CRM即客戶關系管理,是指企業用CRM技術來管理與客戶之間的關系
1、創建數據庫
在app01應用下的models.py文件:
from django.db import models class Department(models.Model): """ 部門表 市場部 1000 銷售 1001 """ title = models.CharField(verbose_name='部門名稱', max_length=16) code = models.IntegerField(verbose_name='部門編號', unique=True, null=False) def __str__(self): return self.title class UserInfo(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): return self.name class Course(models.Model): """ 課程表 如: Linux基礎 Linux架構師 Python自動化開發精英班 Python自動化開發架構師班 Python基礎班 go基礎班 """ name = models.CharField(verbose_name='課程名稱', max_length=32) def __str__(self): return self.name class School(models.Model): """ 校區表 如: 北京沙河校區 上海校區 """ title = models.CharField(verbose_name='校區名稱', max_length=32) def __str__(self): return self.title class ClassList(models.Model): """ 班級表 如: Python全棧 面授班 5期 10000 2017-11-11 2018-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) class Customer(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): return self.name class ConsultRecord(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): return self.customer.name + ":" + self.consultant.name class Student(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): return str(self.customer) class ClassStudyRecord(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) class StudentStudyRecord(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) #數據庫實例化,使用下面的命令 #python3 manage.py makemigrations #python3 manage.py migrate
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文件:
from django.conf.urls import url from django.contrib import admin from stark.service.sites import site urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^stark/', site.urls), ]
stark組件service目錄下sites.py文件:
from django.conf.urls import url from django.shortcuts import HttpResponse, render, redirect from django.utils.safestring import mark_safe from django.urls import reverse class Show_List(object): def __init__(self, config, data_list): self.config = config self.data_list = data_list def get_header(self): # 處理表頭 # header_list=["ID","名稱","價格"] header_list = [] for field in self.config.new_list_display(): if isinstance(field, str): if field == "__str__": val = self.config.model._meta.model_name.upper() else: field_obj = self.config.model._meta.get_field(field) val = field_obj.verbose_name else: val = field(self.config, is_header=True) header_list.append(val) return header_list def get_body(self): # 處理表單數據 new_data_list = [] for obj in self.data_list: temp = [] for field in self.config.new_list_display(): # ["nid","title","price","authors",edit] ['__str__'] ["title","price"] if isinstance(field, str): try: from django.db.models.fields.related import ManyToManyField field_obj = self.config.model._meta.get_field(field) if isinstance(field_obj, ManyToManyField): l = [] for i in getattr(obj, field).all(): l.append(str(i)) val = ",".join(l) else: val = getattr(obj, field) print("val", val) except Exception as e: val = getattr(obj, field) else: val = field(self.config, obj) temp.append(val) new_data_list.append(temp) return new_data_list def get_new_actions(self): action_list = [] for i in self.config.actions: # [patch_init,] action_list.append({ "desc": i.desc, "name": i.__name__, }) return action_list class ModelStark(): list_display = ["__str__", ] search_fields = [] actions = [] def __init__(self, model, site): self.model = model self.site = site def edit(self, obj=None, is_header=False): if is_header: return "操作" return mark_safe("<a href='%s/change'>編輯</a>" % obj.pk) def delete(self, obj=None, is_header=False): if is_header: return "操作" return mark_safe("<a href='%s/delete'>刪除</a>" % obj.pk) def checkbox(self, obj=None, is_header=False): if is_header: return "選擇" return mark_safe("<input type='checkbox' name='selected_pk' value=%s>" % obj.pk) def get_list_url(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label _url = reverse("%s_%s_list" % (app_label, model_name)) return _url def new_list_display(self): temp = [] temp.append(ModelStark.checkbox) temp.extend(self.list_display) temp.append(ModelStark.edit) temp.append(ModelStark.delete) return temp def get_search_condition(self, request): from django.db.models import Q search_condition = Q() val = request.GET.get("q") if val: search_condition.connector = "or" for field in self.search_fields: search_condition.children.append((field + "__contains", val)) return search_condition def list_view(self, request): if request.method == "POST": action = request.POST.get("action") selected_pk = request.POST.getlist("selected_pk") action = getattr(self, action) action(selected_pk) # search search_condition = self.get_search_condition(request) # fliter from django.db.models import Q filter_condition = Q() for key, value in request.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) return render(request, "list_view.html", locals()) def get_mdoelForm(self): from django.forms import ModelForm class DemoModelForm(ModelForm): class Meta: model = self.model fields = "__all__" return DemoModelForm def add(self, request): if request.method == "POST": form = self.get_mdoelForm()(request.POST) if form.is_valid(): form.save() return redirect(self.get_list_url()) else: return render(request, "add.html", locals()) form = form = self.get_mdoelForm()() return render(request, "add.html", locals()) def change(self, request, id): obj = self.model.objects.filter(pk=id).first() if request.method == "POST": form = self.get_mdoelForm()(request.POST, instance=obj) if form.is_valid(): form.save() return redirect(self.get_list_url()) form = self.get_mdoelForm()(instance=obj) return render(request, "change.html", locals()) def delete_view(self, request, id): if request.method == "POST": self.model.objects.get(pk=id).delete() return redirect(self.get_list_url()) url = self.get_list_url() return render(request, "delete.html", locals()) def extra_url(self): return [] def get_urls2(self): model_name = self.model._meta.model_name app_label = self.model._meta.app_label temp = [ 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()) #額外添加一條url return temp @property def urls2(self): return self.get_urls2(), None, None class StarkSite(): def __init__(self, ): self._registry = {} # 一級分發 def get_urls(self): temp = [] for model, model_class_obj in self._registry.items(): # {Book:ModelAdmin(Book),Publish:ModelAdmn(Publish),....} app_name = model._meta.app_label model_name = model._meta.model_name temp.append(url(r"%s/%s/" % (app_name, model_name), model_class_obj.urls2)) return temp @property def urls(self): return self.get_urls(), None, None def register(self, model, admin_class=None, **options): if not admin_class: admin_class = ModelStark self._registry[model] = admin_class(model, self) site = StarkSite()
app01應用下的stark.py文件:
from stark.service.sites import site,ModelStark from django.utils.safestring import mark_safe from app01 import models from django.conf.urls import url from django.shortcuts import HttpResponse,redirect,render from django.http import JsonResponse site.register(models.Department) site.register(models.School) site.register(models.UserInfo) site.register(models.Course) site.register(models.ClassList) class Studentconfig(ModelStark): #學生表的配置類 def display_score(self,obj=None,is_header=False): if is_header: return "個人成績" return mark_safe("<a href='score/%s'>個人成績</a>"%obj.pk) list_display = ["customer","class_list",display_score] #定義顯示學生字段增加客戶信息,已報班級,個人成績三組字段 def score(self,request,sid): if request.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]] for i in ret] # print(ret) return JsonResponse(ret,safe=False) student=models.Student.objects.get(pk=sid) class_list=student.class_list.all() return render(request,"score.html",locals()) def extra_url(self): temp=[] temp.append(url("score/(\d+)",self.score)) return temp site.register(models.Student,Studentconfig) site.register(models.Customer) site.register(models.ConsultRecord) class ClassStudyRecordConfig(ModelStark): def detail(self,obj=None,is_header=False): if is_header: return "詳細信息" return mark_safe("<a href='/stark/app01/studentstudyrecord/?class_study_record=%s'>詳細信息</a>"%obj.pk) #url跳轉到當前班級的詳細信息 def record_score(self, obj=None, is_header=False): if is_header: return "錄入成績" return mark_safe("<a href='record_score/%s'>錄入成績</a>"%obj.pk) #url跳轉到當前班級的錄入成績 list_display = ["class_obj","day_num",detail,record_score] def patch_init(self,selected_pk): #定義批量初始化,對所有班級批量生成對應的學生記錄對象 classstudyrecord_list=self.model.objects.filter(pk__in=selected_pk) for classstudyrecord in classstudyrecord_list: student_list=models.Student.objects.filter(class_list=classstudyrecord.class_obj) for student in student_list: models.StudentStudyRecord.objects.create(class_study_record=classstudyrecord,student=student) patch_init.desc="批量初始化" actions = [patch_init] def record_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_choices update=False if request.method=="POST": #提交數據 print("POST",request.POST) for key,val in request.POST.items(): #頁面取到的鍵值進行處理 if key=="csrfmiddlewaretoken":continue field,pk=key.rsplit("_",1) #以下劃線作為分割標準,取右邊第一個 dic={field:val} models.StudentStudyRecord.objects.filter(pk=pk).update(**dic) #更新的字典,使用** update=True return render(request,"record_score.html",locals()) def extra_url(self): temp=[] temp.append(url("record_score/(\d+)",self.record_score)) return temp site.register(models.ClassStudyRecord,ClassStudyRecordConfig) class StudentStudyRecordConfig(ModelStark): def display_record(self,obj=None,is_header=False): if is_header: return "考勤" return obj.get_record_display() #返回需要的考勤狀態 def display_score(self,obj=None,is_header=False): if is_header: return "成績" return obj.get_score_display() #顯示對應的后面的字母表示的成績,而不是數字 list_display = ["student","class_study_record",display_record,display_score] def patch_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錄入成績頁面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.css"> </head> <body> <h4>錄入成績</h4> <div> <div> <div> <form action="" method="post"> {% csrf_token %} <table class="table table-bordered table-striped table-hover"> <thead> <tr> <th>編號</th> <th>姓名</th> <th>考勤</th> <th>成績</th> <th>批語</th> </tr> </thead> <tbody> {% for student_study_record in student_study_record_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ student_study_record.student }}</td> <td> {{ student_study_record.get_record_display }} </td> <td> <select name="score_{{ student_study_record.pk }}" id=""> {#下拉框顯示當前學生成績#} {% for foo in score_choices %} {% if student_study_record.score == foo.0 %} <option selected value="{{ foo.0 }}">{{ foo.1 }}</option> {#已經有成績的顯示器成績,沒有的顯示默認#} {% else %} <option value="{{ foo.0 }}">{{ foo.1 }}</option> {% endif %} {% endfor %} </select> </td> <td><textarea name="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> <input type="submit" class="btn btn-warning pull-right" value="保存"> {#保存按鈕#} {% if update %} <span style="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查看個人成績頁面:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.css"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script src="/static/Highcharts-6.1.0/code/highcharts.js"></script> </head> <body> <h3>查看{{ student }}成績</h3> <div> <div> <div class="col-md-8 col-md-offset-1"> <table class="table table-bordered table-striped table-hover"> <thead> <tr> <th>編號</th> <th>班級</th> <th>班主任</th> <th>成績柱狀圖</th> </tr> </thead> <tbody> {% for cls in class_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ cls.course }}({{ cls.semester }})</td> <td>{{ cls.tutor }}</td> <td><a cid="{{ cls.pk }}">成績柱狀圖</a></td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> <div id="container" style="width: 600px"></div> <script> $(".chart_score").click(function () { var cid=$(this).attr("cid"); $.ajax({ url:"", type:"get", data:{ sid:"{{ student.pk }}", cid:cid }, success:function (data) { var chart = Highcharts.chart('container', { chart: { type: 'column' }, title: { text: '個人成績分布圖' }, subtitle: { text: '數據截止 2017-03,來源: <a href="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>
頁面顯示效果圖如下:
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。