Day1:項目分析
一:需求分析
二:CRM角色功能介紹
三:業務場景分析
銷售: 1.銷售A 從百度推廣獲取了一個客戶,錄入了CRM系統,咨詢了Python課程,但是沒有報名 2.銷售B 從qq群獲取一個客戶,成功使他報名Python班,然后給他發送了報名連接,等待用戶填寫完畢后,將他添加到Python具體的學習班級中 3.銷售C 打電話給之前的一個客戶,說服他報名Python課程,但是沒有成功,更新了跟蹤記錄 4.銷售D 獲取了一個客戶,錄入信息時,發現此客戶已經存在,不允許重復錄入,隨后通知相應的原負責人跟進 5.銷售E 從客戶庫中獲取了,超過一個月未跟進的客戶,進行再次跟進 6.銷售主管 查看了部門本月的銷售報表,包括來源分析,成單率分析,班級報名數量分析,銷售額環比,同比
學員: 1.客戶A 填寫了銷售發來的報名連接,上傳了個人的證件信息,提交,之后收到郵件,告知報名成功,並為他開通了學員賬號,升級為學員A 2.學員A 登錄學員系統,看到自己的合同,報名的班級,課程大綱 3.學員A 提交了Python課程當時課時作業 4.學員A 查看自己的Python課程成績,排名 5.學員A 搜索問題,未找到答案,錄入一條問題 6.學員A 轉介紹學員,錄入其信息
講師: 1.講師A 登錄CRM系統,查看自己管理的班級列表 2.講師A 進入Python 5期課程,創建第3節的上課記錄,填寫了本節課內容,作業要求 3.講師A 在課程中點名,對點名情況進行錄入,標記相關狀態 4.講師A 批量下載所有學員的課時作業,給每個學員在線批注了成績+狀態
管理員: 1.創建課程 C++,Python.. 2.創建校區 上海,北京.. 3.創建班級 C++35期,Python27期 4.創建賬號 ABCD 5.創建了銷售,講師,學員角色 6.為賬號分配到對應的角色,將ABCD分配給銷售 7.創建相關權限 8.為銷售角色分配了相關權限
四:表結構設計
數據庫關聯模型
Django表結構實現

from django.db import models from django.contrib.auth.models import User # Create your models here. class UserProfile(models.Model): ''' 用戶信息表: 含有講師,銷售,管理員這些正式人員 ''' user = models.OneToOneField(User) #使用的是Django自帶的用戶驗證Username, password and email are required. Other fields are optional. name = models.CharField(max_length=64,verbose_name="姓名") role = models.ManyToManyField("Role",blank=True) #,null=Truenull has no effect on ManyToManyField.,null對於manytomanyfield無作用,會報警 def __str__(self): return self.name class Role(models.Model): ''' 角色表:學員,講師,銷售,管理員 ''' name = models.CharField(max_length=64,unique=True) menus = models.ManyToManyField("Menu",blank=True) def __str__(self): return self.name class Menu(models.Model): ''' 動態菜單 ''' name = models.CharField("菜單名",max_length=64) url_type_choices = ( (0,"absolute"), #絕對路徑/Sale/index.html (1,"dynamic"), #動態url,根據url()方法中的name獲取 ) url_type = models.SmallIntegerField(choices=url_type_choices) url_name = models.CharField("URL",max_length=128) def __str__(self): return self.name class CustumerInfo(models.Model): ''' 客戶信息表:聯系方式,姓名等 ''' name = models.CharField(max_length=64,null=True,blank=True) #開始咨詢的時候允許為空 contact_type_choices = ((0,'qq'),(1,"微信"),(2,'手機')) contact_type = models.SmallIntegerField(choices=contact_type_choices,default=0) contact = models.CharField(max_length=64,unique=True) source_choices = ( (0,'QQ群'), (1,"51CTO"), (2,"百度推廣"), (3,"知乎"), (4,"轉介紹"), (5,"其他") ) source = models.SmallIntegerField(choices=source_choices) referral_from = models.ForeignKey("self",blank=True,null=True,verbose_name="轉介紹人員") consult_courses = models.ManyToManyField("Course",verbose_name="咨詢課程") #咨詢的課程,允許咨詢多門 consult_content = models.TextField("咨詢內容",blank=True) status_choices = ((0,"未報名"),(1,"已報名"),(2,"已退學")) status = models.SmallIntegerField(choices=status_choices) consultant = models.ForeignKey("UserProfile",verbose_name="課程顧問") date = models.DateField(auto_now_add=True) def __str__(self): return self.name class CustumerFollowUp(models.Model): ''' 客戶跟蹤記錄表:跟蹤進度 ''' customer = models.ForeignKey("CustumerInfo") content = models.TextField(verbose_name="跟進內容") user = models.ForeignKey("UserProfile",verbose_name="跟進人員") status_choices = ( (0,"近期無報名計划"), (1,"一個月內報名"), (2,"2周內報名"), (3,"已報名"), ) status = models.SmallIntegerField(choices=status_choices) date = models.DateField(auto_now_add=True) def __str__(self): return self.content class Student(models.Model): ''' 學員信息表:(未報名的客戶在客戶表中),報名成功的在學員表 ''' customer = models.ForeignKey("CustumerInfo") class_grades = models.ManyToManyField("ClassList") #學員可以報多門課程 def __str__(self): return self.customer.name class Course(models.Model): ''' 課程表 ''' name = models.CharField(max_length=64,verbose_name="課程名稱",unique=True) price = models.PositiveSmallIntegerField() #必須為正 period = models.PositiveSmallIntegerField(verbose_name="課程周期(月)",default=5) outline = models.TextField(verbose_name="大綱") def __str__(self): return self.name class ClassList(models.Model): ''' 班級列表 ''' branch = models.ForeignKey("Branch") #校區關聯 couser = models.ForeignKey("Course") class_type_choices = ( (0,"脫產"), (1,"周末"), (2,"網絡班") ) class_type = models.SmallIntegerField(choices=class_type_choices,default=0) semester = models.SmallIntegerField(verbose_name="學期") teachers = models.ManyToManyField("UserProfile",verbose_name="講師") start_date = models.DateField("開班日期") graduate_date = models.DateField("畢業日期",blank=True,null=True) def __str__(self): return "%s (%s)期"%(self.couser,self.semester) class Meta: unique_together = ('branch','class_type',"couser","semester") #聯合唯一 class CourseRecord(models.Model): ''' 上課記錄:該節課程內容等 ''' class_grade = models.ForeignKey("ClassList",verbose_name="上課班級") day_num = models.PositiveSmallIntegerField(verbose_name="課程節次") teacher = models.ForeignKey("UserProfile") title = models.CharField("本節主題",max_length=64) content = models.TextField("本節內容") has_homework = models.BooleanField("本節是否有作業",default=True) homework = models.TextField("作業需求",blank=True,null=True) date = models.DateTimeField(auto_now_add=True,verbose_name="上課時間") def __str__(self): return "%s第(%s)節"%(self.class_grade,self.day_num ) class Meta: unique_together = ("class_grade","day_num") class StudyRecord(models.Model): ''' 學習記錄表:學員考勤,作業,成績,備注 ''' course_record = models.ForeignKey("CourseRecord") student = models.ForeignKey("Student") score_choices = ( (100,"A+"), (90,"A"), (85,"B+"), (80,"B"), (75,"B-"), (70,"C+"), (60,"C"), (40,"C-"), (0,"N/A"), #不可得not avaliable (-50, "D"), #未交作業 (-100,"COPY") #抄襲 ) score = models.SmallIntegerField(choices=score_choices) show_choices = ( (0,"缺勤"), (1,"已簽到"), (2,"遲到"), (3,"早退"), ) show_status = models.SmallIntegerField(choices=show_choices) note = models.CharField("情況備注",max_length=128,blank=True,null=True) date = models.DateTimeField(auto_now_add=True) def __str__(self): return "%s %s %s"%(self.course_record,self.student,self.score) class Branch(models.Model): ''' 校區 ''' name = models.CharField(max_length=64,unique=True) addr = models.CharField(max_length=128,blank=True,null=True) def __str__(self): return self.name
Day2:主要實現功能kingadmin為各個應用實現一個類似於Django自帶的數據庫管理功能
kingadmin目錄
銷售目錄
學員目錄
1.首先我們需要在項目啟動后(進入Kingadmin模塊中view視圖后,能夠自動采集所有的應用中需要我們采集的數據庫信息)
(1)先設置采集方法:在每個需要我們采集的應用模塊中添加上kingadmin.py文件(類似於后台admin會在應用模塊的admin.py中采集信息一樣)。如上面目錄結構,在其中添加了kingadmin.py

from kingadmin.sites import site #雖然說,每個APP:sale,student都去導入了一次site,但是在python項目中對於同一個模塊只會導入一次,所以這本身就是單例模式(使用的是內存中存在的那個) from kingadmin.admin_base import BaseKingAdmin from repository import models print("Sale.kingadmin") class CustomerAdmin(BaseKingAdmin): list_display = ['name','contact_type','contact','source','consult_content','consultant','status','date'] list_filter = ['source','consultant','status','date'] search_fields = ['contact','consultant__name'] site.register(models.CustumerInfo,CustomerAdmin) site.register(models.Role) site.register(models.Menu) site.register(models.UserProfile)

from kingadmin.sites import site from kingadmin.admin_base import BaseKingAdmin from Student import models class TestAdmin(BaseKingAdmin): list_display = ['name'] site.register(models.TestAdmin,TestAdmin) ---------------------------------------------------------- student模塊中自定義一個表 class TestAdmin(models.Model): name = models.CharField("姓名",max_length=64) def __str__(self): return self.name
從中發現需要用到一個基類BaseKingAdmin來自於kingadmin模塊:是為了防止注冊事件時出現為空的現象,而且在基類中添加功能更加方便

class BaseKingAdmin(object): pass
還需要from kingadmin.sites import site,使用到site方法(類似於admin.site.register(模型,自定義模型顯示類)):功能是將各個模塊中的數據模型統一添加在一個數據結構中,方便調用

from kingadmin.admin_base import BaseKingAdmin class AdminSite(object): def __init__(self): self.enabled_admins = {} def register(self,model_class,admin_class=None): ''' 注冊admin表 :param model_class: :param admin_class: :return: ''' app_name = model_class._meta.app_label #app_label是當前應用的名字 一個應用可以注冊多個表 model_name = model_class._meta.model_name #model_name是表名 和app_lable連接就是數據表全名 if not admin_class: admin_class = BaseKingAdmin() else: admin_class = admin_class() if app_name not in self.enabled_admins: self.enabled_admins[app_name] = {} admin_class.model = model_class self.enabled_admins[app_name][model_name] = admin_class site = AdminSite()
將數據統一放入self.enabled_admins{}中,形式為self.enabled_admins[模塊][表名] = 自定義模型顯示類(默認BaseKingAdmin)
注意:雖然在每個模塊中都導入了一次sites模塊,使用一次site對象,實際上使用的是同一個site對象
可以使用id(site)查看內存,因為python機制中將一個模塊導入后,會將其保存在內存中,下次導入數據的時候,會直接從內存中獲取數據(所以大家使用的是一個site對象)
所以說:python模塊本身就是單例模式
(2)從settings.py中獲取各個模塊。創建app_setup.py文件,在項目進入view時去調用該文件,並執行,獲取到所有模塊的信息
進入views.py自動調用app_setup.kingadmin_auto_discover()方法

from django.shortcuts import render,redirect from django.contrib.auth import authenticate,login,logout #快捷操作 from kingadmin import app_setup app_setup.kingadmin_auto_discover() #用來導入所有含Kingadmin的模塊,模塊中會去調用相應的Kingadmin文件去注冊事件 from kingadmin.sites import site #發現只導入模塊一次,site對象只有一個 ---------------------下面實現的是將數據分發給前端------------------------------------- def get_filter_result(request,querysets): filter_conditions = {} for k,v in request.GET.items(): if v: filter_conditions[k] = v return querysets.filter(**filter_conditions),filter_conditions def table_obj_list(request,app_name,model_name): '''取出指定的數據返給前端''' admin_class = site.enabled_admins[app_name][model_name] model_class = admin_class.model querysets = model_class.objects.all() filter_data,filter_conditions = get_filter_result(request,querysets) print(filter_conditions) admin_class.filter_conditions = filter_conditions #也可以傳值給前端,但是這樣也不錯 return render(request,"kingadmin/table_obj_list.html",{"queryset":filter_data,'admin_class':admin_class})
看如何采集各個模塊信息:從配置文件中settings的INSTALLED_APPS中獲取所有模塊信息

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'repository.apps.RepositoryConfig', 'kingadmin', 'Student', 'Sale', ]
views調用了app_setup中的kingadmin_auto_discover()方法自動采集信息,下面看看app_setup文件:實現方法。反向查找
from django import conf #實現動態獲取配置文件,而不是以目錄形式 import importlib def kingadmin_auto_discover(): for module in conf.settings.INSTALLED_APPS: try: # md = importlib.import_module('.kingadmin',module) #這個也可以 md = __import__('%s.kingadmin'%module) #導入Kingadmin,然后回去執行該文件中的數據,去注冊事件(模塊導入后,會自動使用site.register方法注冊事件) except ImportError as e: pass
(3)上面將數據采集完畢,方法內存中site對象中,使用app_index視圖方法,可以實現后台管理admin首頁功能
def app_index(request):return render(request,"kingadmin/app_index.html",{'site':site})

<div> {% for app_name,app_tables in site.enabled_admins.items %} <table class="table table-striped"> <thead> <tr> <th>{{ app_name }}</th> </tr> </thead> <tbody> {% for model_name in app_tables %} <tr> <td> <a href="{% url 'table_obj_list' app_name model_name %}"> {{ model_name }} </a> </td> <td>ADD</td> <td>Change</td> </tr> {% endfor %} </tbody> </table> {% endfor %} </div>
(4)實現點擊表名,查看數據的功能

def get_filter_result(request,querysets): filter_conditions = {} for k,v in request.GET.items(): if v: filter_conditions[k] = v return querysets.filter(**filter_conditions),filter_conditions def table_obj_list(request,app_name,model_name): '''取出指定的數據返給前端''' admin_class = site.enabled_admins[app_name][model_name] model_class = admin_class.model querysets = model_class.objects.all() filter_data,filter_conditions = get_filter_result(request,querysets) print(filter_conditions) admin_class.filter_conditions = filter_conditions #也可以傳值給前端,但是這樣也不錯 return render(request,"kingadmin/table_obj_list.html",{"queryset":filter_data,'admin_class':admin_class})

{% extends "kingadmin/index.html" %} {% load my_func %} {% block right-content-container %} <h1 class="page-header">APP</h1> <form method="get"> {% for field in admin_class.list_filter %} {% build_filter_row field admin_class %} {% endfor %} <button type="submit" class="btn btn-primary">提交</button> </form> <div> <table class="table table-striped"> <thead> <tr> {% for field in admin_class.list_display %} <th>{{ field }}</th> {% endfor %} </tr> </thead> <tbody> {% for item in queryset %} <tr> {% build_table_row item admin_class %} </tr> {% endfor %} </tbody> </table> </div> {% endblock %}
前端使用了自定義模板函數

# coding:utf8 # __author: Administrator # date: 2018/5/26 0026 # /usr/bin/env python from django import template from django.utils.safestring import mark_safe from datetime import datetime,timedelta register = template.Library() @register.simple_tag def build_filter_row(field,admin_class): model = admin_class.model field_obj = model._meta.get_field(field) filter_conditions = admin_class.filter_conditions try: select = "<select name='%s'>"%field data_list = field_obj.get_choices(field) #可以獲取choices選項和外鍵 except AttributeError: if field_obj.get_internal_type() in ("DateField","DateTimeField"): field = "%s__gte"%field select = "<select name='%s'>"%field time_now = datetime.now() time_list = [ ["", "---------"], [time_now, "today"], [time_now - timedelta(7), "七天內"], [time_now.replace(day=1), "本月"], [time_now - timedelta(90), "三個月內"], [time_now.replace(month=1, day=1), "今年內"], ['', "ALL"] ] def turn_date(date_list): date_obj, date_str = date_list if type(date_obj) is datetime: date_obj = date_obj.strftime("%Y-%m-%d") return (date_obj,date_str) data_list = map(turn_date,time_list) else: select = "<select name='%s'>"%field data_list = list(model.objects.values_list("id",field)) for item in data_list: if str(item[0]) == filter_conditions.get(field,None): option = "<option value='%s' selected>" % str(item[0]) else: option = "<option value='%s'>"%str(item[0]) option += item[1] option += "</option>" select += option select += "</select>" return mark_safe(select) @register.simple_tag def build_table_row(obj,admin_class): '''生成一條HTML中tr元素''' tr = "" for field in admin_class.list_display: # column_obj = admin_class.model._meta.get_field(field) #model是獲取對應的模型對象 # if column_obj.choices: # column_data = getattr(obj,"get_%s_display"%field)() #使用方法,要加上() # else: # column_data = getattr(obj,field) #使用屬性不需要() func = "get_"+field+"_display" if hasattr(obj,func): column_data = getattr(obj,func)() else: column_data = getattr(obj,field) td = "<td>%s</td>"%column_data tr += td return mark_safe(tr)
Day3:對上面的功能添加分頁,篩選,排序,搜索功能(功能之間的url需要重組)
一:分頁實現(在Django自帶分頁組件下進行擴展)

from django.core.paginator import Paginator class CustomPagimator(Paginator): def __init__(self,current_page,max_page_num,*args,**kwargs): self.current_page = int(current_page) #當前頁 self.max_page_num = max_page_num #可以顯示多少頁 super(CustomPagimator,self).__init__(*args,**kwargs) def page_num_range(self): # self.num_pages 總頁數 part_num = int(self.max_page_num/2) if self.num_pages <= self.max_page_num: return range(1, self.num_pages + 1) if self.current_page <= part_num: return range(1,self.max_page_num+1) elif self.current_page+part_num>= self.num_pages: return range(self.num_pages-self.max_page_num,self.num_pages+1) else: return range(self.current_page - part_num, self.current_page + part_num + 1)

current_page = request.GET.get('_p',1) paginator = CustomPagimator.CustomPagimator(current_page=current_page, max_page_num=3,object_list=querysets,per_page=2) # 傳入總數據和每頁顯示的數據 try: filter_data = paginator.page(current_page) except PageNotAnInteger: filter_data = paginator.page(1) except EmptyPage: filter_data = paginator.page(paginator.num_pages) # num_pages數總頁數,最后一頁 page_html = paginator.page_num_range()

<div> {% build_page_row queryset page_html admin_class %} </div>

@register.simple_tag def build_page_row(queryset,page_html,admin_class): #先生成條件過濾數據 filter_conditions = '' for k,v in admin_class.filter_conditions.items(): filter_conditions += "&"+k+'='+v; #再生成排序條件 if admin_class.sort_conditions: filter_conditions += "&o="+list(admin_class.sort_conditions.values())[0] #在生成搜索條件 if admin_class.search_conditions: filter_conditions += "&_q="+admin_class.search_conditions page_str = '<ul class="pagination">' if queryset.has_previous(): page_str += '<li><a href="?_p=%d%s">«</a></li>'%(queryset.previous_page_number(),filter_conditions) for i in page_html: if i == queryset.number: page_str += '<li class="active"><a href="?_p=%d%s">%d</a></li>'%(i,filter_conditions,i) else: page_str += '<li><a href="?_p=%d%s">%d</a></li>'%(i,filter_conditions,i) if queryset.has_next(): page_str += '<li><a href="?_p=%d%s">»</a></li>'%(queryset.next_page_number(),filter_conditions) return mark_safe(page_str)
二:對各個字段篩選(對kingadmin中list_filter字段進行篩選)
1:前端顯示

<div class="form-group"> <form method="get" class="form-inline"> {% for field in admin_class.list_filter %} {% build_filter_row field admin_class %} {% endfor %} {% build_order_filter admin_class %} <button type="submit" class="btn btn-primary">提交</button> </form> </div>
2.模板函數去定制標簽,在form表單中加入隱藏標簽(表示排序和搜索條件)

@register.simple_tag def build_filter_row(field,admin_class): model = admin_class.model field_obj = model._meta.get_field(field) filter_conditions = admin_class.filter_conditions label = """<label class="control-label">%s</label>"""%field try: select = "<div class='col-md-3'>%s:<select class='form-control' name='%s'>"%(label,field) data_list = field_obj.get_choices(field) #可以獲取choices選項和外鍵 except AttributeError: if field_obj.get_internal_type() in ("DateField","DateTimeField"): field = "%s__gte"%field select = "<div class='col-md-2'>%s:<select class='form-control' name='%s'>"%(label,field) time_now = datetime.now() time_list = [ ["", "---------"], [time_now, "today"], [time_now - timedelta(7), "七天內"], [time_now.replace(day=1), "本月"], [time_now - timedelta(90), "三個月內"], [time_now.replace(month=1, day=1), "今年內"], ['', "ALL"] ] def turn_date(date_list): date_obj, date_str = date_list if type(date_obj) is datetime: date_obj = date_obj.strftime("%Y-%m-%d") return (date_obj,date_str) data_list = map(turn_date,time_list) else: select = "<div class='col-md-3'>%s:<select class='form-control' name='%s'>"%(label,field) data_list = list(model.objects.values_list("id",field)) for item in data_list: if str(item[0]) == filter_conditions.get(field,None): option = "<option value='%s' selected>" % str(item[0]) else: option = "<option value='%s'>"%str(item[0]) option += item[1] option += "</option>" select += option select += "</select></div>" return mark_safe(select)
3.在views中將url中的各個條件,放置到admin_class中,方便模板標簽的使用

@login_required def table_obj_list(request,app_name,model_name): '''取出指定的數據返給前端''' admin_class = site.enabled_admins[app_name][model_name] model_class = admin_class.model querysets = model_class.objects.all() #所有數據 #搜索后的數據 querysets,search_conditions = get_search_result(request,querysets,admin_class) admin_class.search_conditions = search_conditions querysets,filter_conditions = get_filter_result(request,querysets) #過濾條件后的數據 admin_class.filter_conditions = filter_conditions #也可以傳值給前端,但是這樣也不錯 querysets, sort_conditions = get_order_result(request,querysets,admin_class) admin_class.sort_conditions = sort_conditions current_page = request.GET.get('_p',1) paginator = CustomPagimator.CustomPagimator(current_page=current_page, max_page_num=3,object_list=querysets,per_page=2) # 傳入總數據和每頁顯示的數據 try: filter_data = paginator.page(current_page) except PageNotAnInteger: filter_data = paginator.page(1) except EmptyPage: filter_data = paginator.page(paginator.num_pages) # num_pages數總頁數,最后一頁 page_html = paginator.page_num_range() return render(request,"kingadmin/table_obj_list.html",{"queryset":filter_data,'admin_class':admin_class,"page_html":page_html})
4.在views中的url數據獲取時將其他_q搜索,o排序,_p分頁數據過濾,獲取所有數據

def get_filter_result(request,querysets): filter_conditions = {} for k,v in request.GET.items(): if k in ("_p","o","_q"):continue if v: filter_conditions[k] = v return querysets.filter(**filter_conditions),filter_conditions
推文:python---Django中模型類中Meta元對象了解,可以知道數據模型中的字段對象或者其他所需要的內容
三:對各個字段進行排序(list_display)
1.前端傳遞排序數據,對於table中的th加上url

<thead> <tr> {% build_title_row admin_class %} </tr> </thead>
2.模板函數build_title_row 去生成標簽

@register.simple_tag def build_title_row(admin_class): #先生成過濾條件 filter_conditions = '' for k, v in admin_class.filter_conditions.items(): filter_conditions += "&" + k + '=' + v; #再生成搜索條件 if admin_class.search_conditions: filter_conditions += "&_q="+admin_class.search_conditions icon = """<span class="glyphicon glyphicon-triangle-%s" aria-hidden="true"></span>""" title = '' th = "<th><a href='?o=%s%s'>%s%s</a></th>" try: sort_cond = list(admin_class.sort_conditions.keys())[0] sort_val = list(admin_class.sort_conditions.values())[0] except IndexError: sort_cond = None sort_val = None for counter,field in enumerate(admin_class.list_display): if field == sort_cond: if sort_val.startswith("-"): title += th%(sort_val.strip("-"),filter_conditions,field,icon%"top") else: title += th%("-"+sort_val,filter_conditions,field,icon%"bottom") else: title += th%(counter,filter_conditions,field,"") return mark_safe(title)
3.views中對我們獲取的所有數據,根據前端傳遞的排序方法進行排序處理

def get_order_result(request,querysets,admin_class): order_index = request.GET.get("o") sort_conditions = {} if order_index: index = abs(int(order_index)) order_by = admin_class.list_display[index] if order_index.startswith("-"): querysets = querysets.order_by("-"+order_by) else: querysets = querysets.order_by(order_by) sort_conditions[order_by] = order_index return querysets,sort_conditions
四:對字段進行搜索(search_fields)
1.前端生成標簽時,form表單中需要一起傳遞其他條件的input隱藏框

<form class="form-horizontal" method="get" role="form"> <div class="form-group"> <div class="col-sm-8"> <input class="form-control" id="focusedInput" name="_q" placeholder="輸入數據開始搜索...." value="{{ admin_class.search_conditions }}" type="text"> {% build_search_filter admin_class %} </div> <div class="col-sm-2"> <input type="submit" class="btn btn-primary" value="Search"> </div> </div> </form>
2.使用模板函數生成標簽

@register.simple_tag def build_search_filter(admin_class): '''向搜索框中添加入過濾條件和排序條件''' # 先生成過濾條件 inp = "" for k, v in admin_class.filter_conditions.items(): inp += """<input type="hidden" name="%s" value="%s"/>"""%(k,v) # 再生成排序條件 if admin_class.sort_conditions: inp += """<input type="hidden" name="%s" value="%s"/>""" % ("o", list(admin_class.sort_conditions.values())[0]) return mark_safe(inp)
3.后端處理搜索條件,生成querysets數據

def get_search_result(request,querysets,admin_class): search_val = request.GET.get("_q") if search_val: q = Q() q.connector = "OR" for field in admin_class.search_fields: q.children.append(("%s__contains"%field,search_val)) querysets = querysets.filter(q) return querysets,search_val
Day4:動態生成任意表的CURD
1.如何在前端動態生成標簽?使用form驗證可以針對model生成所有的字段控件
推文:python---django中form組件(2)自定制屬性以及表單的各種驗證,以及數據源的實時更新,以及和數據庫關聯使用ModelForm和元類

from django.forms import ModelForm from repository import models class CustomerForm(ModelForm): class Meta: model = models.CustumerInfo #將表與元類中的數據關聯 fields = "__all__" def __new__(cls, *args, **kwargs): print(cls.base_fields) #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)]) #這張表中的所有字段對象 for field_name,field_obj in dict(cls.base_fields).items(): field_obj.widget.attrs.update({'class':"form-control"}) return ModelForm.__new__(cls)
2.如何針對每張表動態生成一個Form類?需要用到type方法去動態生成類

from django.forms import ModelForm from repository import models def create_dynamic_model_form(admin_class,form_add = False): '''動態生成modelform,form_add表示是添加數據生成form類。添加和編輯有所區別''' class Meta: model = admin_class.model # 將表與元類中的數據關聯 fields = "__all__" if not form_add: exclude = admin_class.readonly_fields admin_class.add_flag = False else: exclude = [] admin_class.add_flag = True def __new__(cls, *args, **kwargs): #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)]) #這張表中的所有字段對象 for field_name,field_obj in dict(cls.base_fields).items(): field_obj.widget.attrs.update({'class':"form-control"}) # if field_name in admin_class.readonly_fields: # field_obj.widget.attrs.update({'disabled':'true'}) return ModelForm.__new__(cls) dynamic_form = type("DynamicModelForm",(ModelForm,),{'Meta':Meta,"__new__":__new__}) return dynamic_form
3.在修改頁面中動態創建Form類(需要傳遞原來數據)

@login_required def table_obj_change(request,app_name,model_name,obj_id): '''kingadmin數據修改頁面''' admin_class = site.enabled_admins[app_name][model_name] #動態生成form表單 model_form = form_handle.create_dynamic_model_form(admin_class) obj = admin_class.model.objects.get(id=obj_id) if request.method == "GET": form_obj = model_form(instance=obj) elif request.method == "POST": form_obj = model_form(instance=obj,data=request.POST) if form_obj.is_valid(): form_obj.save() return redirect("/kingadmin/%s/%s"%(app_name,model_name)) return render(request,"kingadmin/table_obj_change.html",locals())

url(r"^(\w+)/(\w+)/(\d+)/change/$", views.table_obj_change, name="table_obj_change"),
4.在添加頁面動態創建Form類

@login_required def table_obj_add(request,app_name,model_name): admin_class = site.enabled_admins[app_name][model_name] model_form = form_handle.create_dynamic_model_form(admin_class,form_add = True) if request.method == "GET": form_obj = model_form() elif request.method == "POST": form_obj = model_form(data=request.POST) if form_obj.is_valid: form_obj.save() return redirect("/kingadmin/%s/%s" % (app_name, model_name)) return render(request,"kingadmin/table_obj_add.html",locals())

url(r"^(\w+)/(\w+)/add/$", views.table_obj_add, name="table_obj_add")
添加的url
5.修改和添加的HTML和公共部分

{% extends "kingadmin/index.html" %} {% load my_func %} {% block right-content-container %} <h2 class="page-header">{% get_model_name admin_class %}</h2> <h3 class="page-header">添加{% get_model_name admin_class %}</h3> <div> add {% include "kingadmin/table_obj_change_component.html" %} </div> {% endblock %}

{% extends "kingadmin/index.html" %} {% load my_func %} {% block right-content-container %} <h2 class="page-header">{% get_model_name admin_class %}</h2> <h3 class="page-header">修改{{ form_obj.instance }}</h3> <div> change {% include "kingadmin/table_obj_change_component.html" %} </div> {% endblock %}

{% load my_func %} <form class="form-horizontal" onsubmit="ChangeSelStatus(this);" method="post" role="form"> {% csrf_token %} {% for field in form_obj %} <div class="form-group"> <label class="col-sm-2 control-label">{{ field.label }}</label> {% if field.name in admin_class.filter_horizontal %} <div class="col-sm-5"> <select class="form-control" name="" id="id_{{ field.name }}_from" multiple> {% get_rel_m2m_val field.name admin_class as rel_querysets %} {% for rel_obj in rel_querysets %} {% get_rel_m2m_sel form_obj field.name rel_obj as sel_flag%} {% if not sel_flag %} <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option> {% endif %} {% endfor %} </select> </div> <div class="col-sm-5"> <select tag="submit" class="form-control" name="{{ field.name }}" id="id_{{ field.name }}_to" multiple> {% get_rel_m2m_val field.name admin_class as rel_querysets %} {% for rel_obj in rel_querysets %} {% get_rel_m2m_sel form_obj field.name rel_obj as sel_flag%} {% if sel_flag %} <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option> {% endif %} {% endfor %} </select> </div> {% else %} <div class="col-sm-10"> {{ field }} <span style="color: red;"> {{ field.errors.0 }} </span> </div> {% endif %} </div> {% endfor %} {% if not admin_class.add_flag %} {% for field_name in admin_class.readonly_fields %} <div class="form-group"> <label class="col-sm-2 control-label">{{ field_name }}</label> <div class="col-sm-10"> <p class="text-left">{% get_field_value_p field_name form_obj %}</p> </div> </div> {% endfor %} {% endif %} <div class="col-lg-offset-11 col-sm-1"> <input type="submit" class="btn btn-success" value="Save"> </div> </form> {% block extra-js %} <script> function MoveEleToOpp(ths,field_name) { if($(ths).parent().prop("id") == "id_"+field_name+"_from"){ var new_target = "id_"+field_name+"_to"; }else{ var new_target = "id_"+field_name+"_from"; } $("#"+new_target).append(ths); } function ChangeSelStatus(ths){ $("select[tag] option").prop("selected",true); } </script> {% endblock %}
6.處理在add和change中對於readonly_fileds字段的不同

{% if not admin_class.add_flag %} {% for field_name in admin_class.readonly_fields %} <div class="form-group"> <label class="col-sm-2 control-label">{{ field_name }}</label> <div class="col-sm-10"> <p class="text-left">{% get_field_value_p field_name form_obj %}</p> </div> </div> {% endfor %} {% endif %}
7.對於filter_horizontal字段我們在模板函數中進行獲取所有的值,並且判斷是否顯示在哪一個select標簽中

<div class="col-sm-5"> <select class="form-control" name="" id="id_{{ field.name }}_from" multiple> {% get_rel_m2m_val field.name admin_class as rel_querysets %} {% for rel_obj in rel_querysets %} {% get_rel_m2m_sel form_obj field.name rel_obj as sel_flag%} {% if not sel_flag %} <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option> {% endif %} {% endfor %} </select> </div> <div class="col-sm-5"> <select tag="submit" class="form-control" name="{{ field.name }}" id="id_{{ field.name }}_to" multiple> {% get_rel_m2m_val field.name admin_class as rel_querysets %} {% for rel_obj in rel_querysets %} {% get_rel_m2m_sel form_obj field.name rel_obj as sel_flag%} {% if sel_flag %} <option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option> {% endif %} {% endfor %} </select> </div>

@register.simple_tag def get_rel_m2m_val(field_name,admin_class): field_obj = admin_class.model._meta.get_field(field_name) rel_model = field_obj.related_model querysets = rel_model.objects.all() return querysets

@register.simple_tag def get_rel_m2m_sel(form_obj,field_name,rel_obj): try: querysets = getattr(form_obj.instance, field_name).all() if rel_obj in querysets: return True return False except TypeError: return False
8.實現js雙擊option,在兩個select之間跳轉

<option ondblclick="MoveEleToOpp(this,'{{ field.name }}');" value="{{ rel_obj.id }}">{{ rel_obj }}</option>

function MoveEleToOpp(ths,field_name) { if($(ths).parent().prop("id") == "id_"+field_name+"_from"){ var new_target = "id_"+field_name+"_to"; }else{ var new_target = "id_"+field_name+"_from"; } $("#"+new_target).append(ths); }
9.實現在點擊保存時,form表單自動將右側select中的數據全部選中。注意:加上name為select標簽,name="字段名"

<form class="form-horizontal" onsubmit="ChangeSelStatus(this);" method="post" role="form">

function ChangeSelStatus(ths){ $("select[tag] option").prop("selected",true); }
10.為filter_horizontal完善功能,添加全選,全部移除

<p><a onclick="ChooseAll(this,'{{ field.name }}')">ChooseAll</a></p> <p><a onclick="ChooseAll(this,'{{ field.name }}')">RemoveAll</a></p>

function ChooseAll(ths,field_name) { var sel_id = $(ths).parent().prev().prop("id") if(sel_id == "id_"+field_name+"_from"){ var new_target = "id_"+field_name+"_to"; }else{ var new_target = "id_"+field_name+"_from"; } $("#"+sel_id).find("option").each(function(){ $("#"+new_target).append(this); }) }
Day5:刪除功能開發和action方法實現
1.刪除功能開發

{% extends "kingadmin/index.html" %} {% load my_func %} {% block right-content-container %} <h2 class="page-header">{% get_model_name admin_class %}</h2> <h3 class="page-header alert-danger">注意:以下與{{ obj }}想關聯的數據都將被刪除</h3> <div> {% get_del_obj obj app_name model_name as res_del %} {{ res_del|safe }} </div> <form method="post"> {% csrf_token %} <input type="submit" class="btn btn-danger" value="確認刪除"> <a href="/kingadmin/{{ app_name }}/{{ model_name }}/{{ obj.id }}/change" class="btn btn-primary">返回</a> </form> {% endblock %}

@register.simple_tag def get_del_obj(model_obj, app_name, model_name): all_rel = model_obj._meta.related_objects ul = "<ul>" ul += "<li>%s:<a href='/kingadmin/%s/%s/%s/change'>%s</a></li>"%(model_obj._meta.label.rsplit('.',maxsplit=1)[1],app_name,model_name,model_obj.id,model_obj) for rel_field in all_rel: sub_querysets = getattr(model_obj,rel_field.name+"_set").all() if not sub_querysets: #若是關聯但是沒有數據,則不顯示 continue if rel_field.get_internal_type() == "ManyToManyField": ul += "<li><ul>" ul += "<li>%s</li>"%rel_field.name for i in sub_querysets: ul += "<li><ul><li>%s</li></ul></li>" % i ul += "</ul></li>" else: for sub_item in sub_querysets: sub_res = get_del_obj(sub_item,sub_item._meta.app_label,sub_item._meta.model_name) ul += "<li>%s</li>"%(sub_res) ul += "</ul>" return ul

@login_required def table_obj_delete(request,app_name,model_name,obj_id): admin_class = site.enabled_admins[app_name][model_name] obj = admin_class.model.objects.get(id=obj_id) if request.method == "POST": obj.delete() return redirect("/kingadmin/{app_name}/{model_name}".format(app_name=app_name,model_name=model_name)) return render(request, "kingadmin/table_obj_delete.html", locals())
2.action字段功能完善

class CustomerAdmin(BaseKingAdmin): list_display = ['id','name','contact_type','contact','source','consult_content','consultant','status','date'] list_filter = ['source','consultant','status','date'] search_fields = ['contact','consultant__name','name'] readonly_fields = ['status','contact'] filter_horizontal = ['consult_courses',] action = ['change_status',] def change_status(self,request,querysets): querysets.update(status=1)

from django.shortcuts import render class BaseKingAdmin(object): list_display = [] list_filter = [] search_fields = [] readonly_fields = [] filter_horizontal = [] action = [] def_action = ['delete_selected_objs'] def delete_selected_objs(self,request,querysets): return render(request,'kingadmin/table_obj_delete.html') def __init__(self): self.action.extend(self.def_action)
(1)設置form表單布局

<div> <form action="" method="post" class="form-inline" onsubmit="return Raw_input_action(this);"> <div class="col-lg-3"> {% csrf_token %} <label class="control-label">Action:</label> <select name="action" id="action" class="form-control"> <option value="">---------</option> {% for action in admin_class.action %} <option value="{{ forloop.counter0 }}">{{ action }}</option> {% endfor %} </select> </div> <div class="col-sm-2"> <input type="submit" value="Go" class="btn btn-primary"> </div> </form> </div>
(2)設置復選框完成全選功能

<div> <table class="table table-striped"> <thead> <tr> <th><input type="checkbox" id="check_All" onclick="CheckAll(this)"/></th> {% build_title_row admin_class %} </tr> </thead> <tbody> {% for item in queryset %} <tr> <td><input type="checkbox" name="check_row" value="{{ item.id }}"></td> {% build_table_row item admin_class %} </tr> {% endfor %} </tbody> </table> </div>

function CheckAll(ths) { if($(ths).prop("checked")){ $("input[name=check_row]").prop("checked",true) }else{ $("input[name=check_row]").prop("checked",false) } }
(3)提交表單前先生成隱藏表單去獲取數據集

function Raw_input_action(ths) { if($("#action").val() == ""){ alert("請選擇正確的action"); return false; } var select_ids = []; $("input[name=check_row]").filter(":checked").each(function(){ select_ids.push($(this).val()); }) if(select_ids.length == 0){ alert("請選擇正確的項目"); return false; } new_ele = "<input type='hidden' name='select_ids' value='"+JSON.stringify(select_ids)+"'/>"; $(ths).append(new_ele); return true; }
(4)傳遞到后端進行處理

@login_required def table_obj_list(request,app_name,model_name): '''取出指定的數據返給前端''' admin_class = site.enabled_admins[app_name][model_name] model_class = admin_class.model querysets = model_class.objects.all() #所有數據 if request.method == "POST": selected_action = request.POST.get("action") selected_ids = request.POST.get("select_ids") getattr(admin_class,admin_class.action[int(selected_action)])(request,querysets.filter(id__in=json.loads(selected_ids)))
3.處理action中的默認行為delete批量刪除
(1)提交的url不是上面的table_obj_delete,而是本頁面和change_status一起作為action傳遞入當前url

def delete_selected_objs(self,request,querysets): return render(request,'kingadmin/table_obj_delete.html',{"admin_class":self,'obj':querysets})
(2)獲取delete_selected_objs在table_obj_list方法中返回

if request.method == "POST": if request.POST.get("delete_ids"): '''如果是刪除做post傳遞過來的話另外處理,否則就是action操作''' del_id = json.loads(request.POST.get("delete_ids")) admin_class.model.objects.filter(id__in=del_id).delete() return redirect("/kingadmin/%s/%s"%(app_name,model_name)) else: selected_action = request.POST.get("action") selected_ids = request.POST.get("select_ids") res = getattr(admin_class,admin_class.action[int(selected_action)])(request,querysets.filter(id__in=json.loads(selected_ids))) #如果返回值,代表是返回render指向delete頁面 if res: return res
若是執行完action方法后沒有返回值則是正常執行,如果有返回值,則是代表我們接下來是執行刪除操作。需要返回
(3)我們還是調用的上面的table_obj_delete.html頁面,但是其中的模板標簽函數,是針對一個數據對象,而現在是一個數據集,我們需要再次處理

@register.simple_tag def get_del_obj(model_objs, app_name, model_name): ul = "<ul>" try: iter(model_objs) except TypeError: model_objs = [model_objs,] for model_obj in model_objs: all_rel = model_obj._meta.related_objects ul += "<li>%s:<a href='/kingadmin/%s/%s/%s/change'>%s</a></li>"%(model_obj._meta.label.rsplit('.',maxsplit=1)[1],app_name,model_name,model_obj.id,model_obj) for rel_field in all_rel: sub_querysets = getattr(model_obj,rel_field.name+"_set").all() if not sub_querysets: #若是關聯但是沒有數據,則不顯示 continue if rel_field.get_internal_type() == "ManyToManyField": ul += "<li><ul>" ul += "<li>%s</li>"%rel_field.name for i in sub_querysets: ul += "<li><ul><li>%s</li></ul></li>" % i ul += "</ul></li>" else: for sub_item in sub_querysets: sub_res = get_del_obj(sub_item,sub_item._meta.app_label,sub_item._meta.model_name) ul += "<li>%s</li>"%(sub_res) ul += "</ul>" return ul
(4)我們提交數據,也不再是table_obj_delete方法,而是table_obj_list方法,所以我們需要傳遞一個數據代表要刪除的數據id集合,同時一個一個標識

<input type="hidden" name="delete_ids" value="{% get_del_objs_id obj %}">

@register.simple_tag def get_del_objs_id(objs): obj_ser = [] for obj in objs: obj_ser.append(obj.id) return json.dumps(obj_ser)
(5)views頁面根據post傳遞過來的隱藏標簽的name,判斷是不是執行刪除數據操作

if request.method == "POST": if request.POST.get("delete_ids"): '''如果是刪除做post傳遞過來的話另外處理,否則就是action操作''' del_id = json.loads(request.POST.get("delete_ids")) admin_class.model.objects.filter(id__in=del_id).delete() return redirect("/kingadmin/%s/%s"%(app_name,model_name))
4.實現面包屑導航

<ol class="breadcrumb"> <li><a href="/kingadmin">CRM</a></li> <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li> <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}">{{ model_name }}</a></li> <li class="active">{% get_nva_active admin_class %}</li> </ol>

<ol class="breadcrumb"> <li><a href="/kingadmin">CRM</a></li> <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li> <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}">{{ model_name }}</a></li> <li class="active">{% get_nva_active admin_class %}</li> </ol>

<ol class="breadcrumb"> <li><a href="/kingadmin">CRM</a></li> <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li> <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}">{{ model_name }}</a></li> <li class="active">{{ form_obj.instance }}</li> </ol>

@register.simple_tag def get_nva_active(admin_class): return admin_class.model._meta.verbose_name

<ol class="breadcrumb"> <li><a href="/kingadmin">CRM</a></li> <li><a href="/kingadmin/{{ app_name }}">{{ app_name }}</a></li> <li><a href="/kingadmin/{{ app_name }}/{{ model_name }}">{{ model_name }}</a></li> <li class="active">{% get_nav_del obj %}</li> </ol>

@register.simple_tag def get_nav_del(model_objs): obj_names = [] try: iter(model_objs) except TypeError: model_objs = [model_objs,] for model in model_objs: obj_names.append("%s"%model) return '|'.join(obj_names)
5.左側菜單狀態

{% for menu in role.menus.select_related %} {% if menu.url_type == 0 %} {% if menu.url_name == request.path %} <li class="active"><a href="{{ menu.url_name }}">{{ menu.name }}</a></li> {% else %} <li><a href="{{ menu.url_name }}">{{ menu.name }}</a></li> {% endif %} {% else %} {% url menu.url_name as url_name %} {% if url_name == request.path %} <li class="active"><a href="{{ url_name }}">{{ menu.name }}</a></li> {% else %} <li><a href="{{ url_name }}">{{ menu.name }}</a></li> {% endif %} {% endif %} {% endfor %}
Day6:學員報名流程開發

class ContractTemplate(models.Model): '''合同模板表''' name = models.CharField(max_length=64) content = models.TextField() date = models.DateField(auto_now_add=True) def __str__(self): return self.name class StudentEnrollment(models.Model): '''學員報名表:這里還沒有變成學員,適合客戶表相關聯''' customer = models.ForeignKey("CustumerInfo") class_grade = models.ForeignKey("ClassList") consultant = models.ForeignKey("UserProfile") #對應的銷售 contract_agreed = models.BooleanField(default=False) #是否同意合同 contract_signed_date = models.DateTimeField(blank=True,null=True) #同意合同未到時間 contract_approved = models.BooleanField(default=False) #審核是否完畢 contract_approved_date = models.DateTimeField(blank=True,null=True) class Meta: unique_together = ("customer","class_grade") def __str__(self): return "%s"%self.customer class PaymentRecord(models.Model): '''存儲學員繳費記錄''' enrollment = models.ForeignKey("StudentEnrollment") payment_type_choice = ( (0,"報名費"), (1,"學費"), (2,"退費"), ) payment_type = models.SmallIntegerField(choices=payment_type_choice) amount = models.IntegerField("費用",default=500) consultant = models.ForeignKey("UserProfile") #費用繳給誰 date = models.DateTimeField(auto_now_add=True) def __str__(self): return "%s"%self.enrollment
一:銷售為想報名的學員提供鏈接

@login_required def Student_encroll(request): ClassList = models.ClassList.objects.all() StuEncroll = models.CustumerInfo.objects.filter(consultant__user=request.user).all() if request.method == "POST": student = request.POST.get("student") classlist = request.POST.get("classlist") try: StuEncObj = models.StudentEnrollment.objects.create( customer_id=student, class_grade_id=classlist, consultant=request.user.userprofile ) except IntegrityError: StuEncObj = models.StudentEnrollment.objects.get( customer_id=student, class_grade_id=classlist, consultant=request.user.userprofile ) if StuEncObj.contract_agreed: return redirect("encrollment/%s/contract_audit.html"%StuEncObj.id) else: return HttpResponse("等待學員身份驗證") link = "http://127.0.0.1:8000/sale/encrollment/%s.html"%StuEncObj.id return render(request,"sale/stu_encroll.html",locals())
二:學員獲取鏈接,進行填寫信息,查閱合同,同意並上傳證件信息

def enrollment(request,id): '''學員在線報名''' enrollment_obj = models.StudentEnrollment.objects.get(id=id) if enrollment_obj.contract_agreed and not enrollment_obj.contract_approved: return HttpResponse("信息正在審核當中") if enrollment_obj.contract_approved: return HttpResponse("審核通過,去進行繳費操作") if request.method == "GET": forms = CustomerForm(instance=enrollment_obj.customer) elif request.method == "POST": if not request.POST.get("contract_agreed"): return HttpResponse("信息提交失敗,請先閱讀合同") cus_dir = os.path.join(conf.settings.SALE_FILE_UPLOAD_DIR, id) if len(os.listdir(cus_dir)) == 0: return HttpResponse("信息提交失敗,請先上傳證件信息") forms = CustomerForm(instance=enrollment_obj.customer,data=request.POST) if forms.is_valid(): forms.save() enrollment_obj.contract_agreed = True enrollment_obj.contract_signed_date = datetime.datetime.now() enrollment_obj.save() return HttpResponse("信息提交成功") file_dir = os.path.join(conf.settings.SALE_FILE_UPLOAD_DIR,id) if os.path.isdir(file_dir): file_info = os.listdir(file_dir) return render(request,"sale/enrollment.html",locals())

{% extends "index.html" %} {% block extra-link %} <link rel="stylesheet" href="/static/plugins/dropzone/dropzone.css"> {% endblock %} {% block body %} <h1 class="page-header">學員報名</h1> <div class="col-lg-offset-1 col-lg-10"> <div class="panel panel-info"> <div class="panel-heading"> <h3 class="panel-title">學員在線報名</h3> </div> <div class="panel-body"> <form class="form-horizontal" onsubmit="return PrevSubmit(this);" method="post" role="form"> {% csrf_token %} {% for field in forms %} <div class="form-group col-sm-6"> <label class="col-sm-4 control-label">{{ field.label }}</label> <div class="col-sm-8"> {{ field }} <span style="color: red;">{{ field.errors.0 }}</span> </div> </div> {% endfor %} <div class="form-group col-sm-6"> <label class="col-sm-4 control-label">報名班級</label> <div class="col-sm-8"> <p class="form-control-static">{{ enrollment_obj.class_grade }}</p> </div> </div> <div class="form-group col-sm-6"> <label class="col-sm-4 control-label">學費</label> <div class="col-sm-8"> <p class="form-control-static">{{ enrollment_obj.class_grade.couser.price }}</p> </div> </div> <div class="col-sm-12"> <pre style="height: 400px"> {{ enrollment_obj.class_grade.contract_template.content }} </pre> <div> <div class="form-inline"> <label class="col-sm-6 control-label" style="padding-top: 0px">是否同意以上合同</label> <input type="checkbox" value="1" name="contract_agreed"> </div> </div> </div> <div class="form-group col-sm-6"> <div class="col-sm-8"> <input type="submit" class="btn btn-success" value="提交"> </div> </div> </form> <div class="col-sm-12"> <ul id="file_ul" style="list-style: none"> <label class="control-label" style="padding-top: 0px;">已上傳的文件目錄</label> {% for file in file_info %} <li>{{ file }}</li> {% endfor %} </ul> <form action="{% url 'enrollment_fileupload' enrollment_obj.id %}" id="myAwesomeDropzone" class="dropzone"> <div class="fallback"> <input name="file" type="file" multiple /> </div> </form> </div> </div> </div> </div> {% endblock %} {% block extra-js %} <script src="/static/plugins/dropzone/dropzone.js"></script> <script> $(function () { Dropzone.options.myAwesomeDropzone = { paramName: "file", // The name that will be used to transfer the file maxFilesize: 2, // MB maxFiles:2, parallelChunkUploads:true, accept: function(file, done) { if (file.name == "justinbieber.jpg") { done("Naha, you don't."); } else { done(); } }, init: function() { this.on("success", function(file,respone) { /* Maybe display some more file information on your page */ var rep = JSON.parse(respone) if(!rep.status){ alert(rep.message); return; }else{ var li = "<li>"+file.name+"</li>"; $("#file_ul").append(li); } }); } }; }); function PrevSubmit(ths){ if(!$($(ths).find(":checkbox[name=contract_agreed]")[0]).prop("checked")){ alert("請先閱讀合同"); return false; } if($("#file_ul").find("li").length==0){ alert("請先上傳證件信息"); return false; } $(ths).find(":disabled").removeAttr("disabled") return true } </script> {% endblock %}

@csrf_exempt def enrollment_fileupload(request,encrollment_obj_id): cus_dir = os.path.join(conf.settings.SALE_FILE_UPLOAD_DIR,encrollment_obj_id) if not os.path.isdir(cus_dir): os.makedirs(cus_dir) status = { 'status':True, "message":None } print(request.FILES) #需要去接收文件,前端狀態才會是true if len(os.listdir(cus_dir)) >= 2: status['status'] = False status['message'] = "文件超出上傳個數" return HttpResponse(json.dumps(status)) file_obj = request.FILES.get("file") with open(os.path.join(cus_dir,file_obj.name),"wb") as fp: for chunks in file_obj.chunks(): fp.write(chunks) return HttpResponse(json.dumps(status))
三:銷售審核學員注冊信息,審核通過,為其生成賬號(密碼需要使用Django模塊加密),發送郵件
from django.contrib.auth.hashers import make_password #用於生成密碼

@login_required def contract_audit(request,id): '''合同審查''' enrollment_obj = models.StudentEnrollment.objects.get(id=id) if not enrollment_obj.contract_agreed: return redirect("/sale/EncrollLink.html") if enrollment_obj.contract_approved: return HttpResponse("審核通過,等待繳費") if request.method == "GET": forms = CustomerForm(instance=enrollment_obj.customer) contract_forms = ContractForm(instance=enrollment_obj) elif request.method == "POST": forms = CustomerForm(instance=enrollment_obj.customer,data=request.POST) contract_forms = ContractForm(instance=enrollment_obj,data=request.POST) if forms.is_valid() and contract_forms.is_valid(): forms.save() contract_forms.save() enrollment_obj.contract_approved_date = datetime.datetime.now() enrollment_obj.save() try: stu_obj = enrollment_obj.customer.student except Exception: pass else: return HttpResponse( "用戶%s已添加--->賬號為:%s" % (enrollment_obj.customer.name, enrollment_obj.customer.student.account.name)) #生成一個隨機字符串 account = ''.join(random.sample(string.digits,8)) pwd = ''.join(random.sample(string.ascii_letters+string.digits,8)) #創建一個賬號 user = models.User.objects.create( username=account, password=make_password(pwd), ) #創建一個用戶 userprofile = models.UserProfile.objects.create( user=user, name=enrollment_obj.customer.name, ) #為用戶綁定一個角色 userprofile.role.add(models.Role.objects.get(name="Student")) #保存到學員表 models.Student.objects.create( customer=enrollment_obj.customer, class_grades=enrollment_obj.class_grade, account=userprofile, ) send_info = "賬號:%s\n密碼:%s"%(account,pwd) send_mail( '成功成為正式學員', send_info, '18904190363@sina.cn', ["%s@qq.com"%enrollment_obj.customer.contact,], fail_silently=False, ) return HttpResponse("審核通過,等待繳費") print(forms.errors,contract_forms.errors) return render(request,"sale/contract_audit.html",locals())
Django自帶郵件發送模塊
(1)settings中配置
(2)導入模塊發送郵件
day7:實現講師和學員作業發布上傳功能(其中由於多使用table瀏覽數據,可以自定義一個類似於form的基類,去統一實現table顯示)
一:講師功能
(1)可以看出上面多是table顯示信息,下面自定義table_form類似於forms

import re class BaseForm(object): display_list = [] field_tag = {} attrs = {} extra_field = [] def __init__(self,model,querysets): self.instance = model self.querysets = querysets self.th = [] self.tr = [] def register(self): for field in self.display_list: if field == "self": self.th.append(self.instance._meta.verbose_name) continue field_obj = self.instance._meta.get_field(field) self.th.append(field_obj.verbose_name) #自定義額外字段 for item in self.extra_field: for k,v in item.items(): if v.get("verbose_name"): self.th.append(v.get("verbose_name")) else: self.th.append(k) for query in self.querysets: tds = [] for th in self.display_list: if th == "self": field_val = "%s" % query elif len(self.instance._meta.get_field(th).choices) > 0: field_val = "%s"%getattr(query,"get_%s_display"%th)() else: field_val = "%s"%getattr(query,th) if self.field_tag.get(th): # {"self": {"a": {"href": "127.0.0.1:8000","href": "127.0.0.1:8000"},"a": {"href": "127.0.0.1:8000"}}} tags = self.field_tag.get(th) # {"a": {"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"}, "a": {"href": "127.0.0.1:8000"}} #前面是內層,后面是外層 for k,v in tags.items(): # "a": {"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"} new_attr = [] for k1,v1 in v.items(): #{"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"} pat = re.compile("\{(.*?)\}") res = pat.search(v1) while res: v1 = pat.sub(str(getattr(query,res.group(1))),v1,1) res = pat.search(v1) new_attr.append("%s='%s'"%(k1,v1)) #獲取到所有的屬性放在列表中 field_val = "<%s %s>%s</%s>"%(k," ".join(new_attr),field_val,k) tds.append(field_val) for item in self.extra_field: for e_k,e_v in item.items(): if e_v.get("value"): field_val = "%s" % e_v.get("value") else: if hasattr(e_v.get("function"),"__call__"): field_val = e_v.get("function")(getattr(query,e_v.get("model_attr")),*e_v.get("args",()),**e_v.get("kwargs",{})) else: field_val = getattr(getattr(query,e_v.get("model_attr")),e_v.get("function"))(*e_v.get("args",()),**e_v.get("kwargs",{})) if self.field_tag.get(e_k): # {"self": {"a": {"href": "127.0.0.1:8000","href": "127.0.0.1:8000"},"a": {"href": "127.0.0.1:8000"}}} tags = self.field_tag.get(e_k) # {"a": {"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"}, "a": {"href": "127.0.0.1:8000"}} #前面是內層,后面是外層 for k,v in tags.items(): # "a": {"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"} new_attr = [] for k1,v1 in v.items(): #{"href": "127.0.0.1:8000", "href": "127.0.0.1:8000"} pat = re.compile("\{(.*?)\}") res = pat.search(v1) while res: v1 = pat.sub(str(getattr(query, res.group(1))), v1, 1) res = pat.search(v1) new_attr.append("%s='%s'"%(k1,v1)) #獲取到所有的屬性放在列表中 field_val = "<%s %s>%s</%s>"%(k," ".join(new_attr),field_val,k) tds.append(field_val) self.tr.append(tds) def __str__(self): cls_attr = [] if len(self.attrs): for item in self.attrs.items(): cls_attr.append("%s='%s'"%(item[0],item[1])) tb = "<table %s>"%(" ".join(cls_attr)) tr = "<thead><tr>" for th_data in self.th: th = "<th>%s</th>"%th_data tr += th tr += "</tr></thead><tbody>" tb += tr for tr_data in self.tr: tr = "<tr>" for td_data in tr_data: td = "<td>%s</td>"%td_data tr += td tr += "</tr>" tb += tr tb += "</tbody></table>" return tb
(2)使用方法
from Teacher.table_form import BaseForm class ClassListForm(BaseForm): display_list = ["self","branch","class_type","start_date","graduate_date"] #self代表直接顯示本條數據__str__ field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},"study_record":{"a":{"href":"/teacher/classlist/{id}/class_list.html"}},"student":{"a":{"href":"/teacher/classlist/{id}/student_list.html"}}} #在前面的標簽會顯示在內層,在顯示的數據外面加上標簽 attrs = {"class":"table table-hover"} #為table設置屬性 extra_field = [{"student":{'verbose_name':"學員數量","model_attr":"student_set","function":"count"}},{"study_record":{"verbose_name":"上課記錄","value":"上課記錄"}}] #額外自定義字段,若是有值value,會直接輸出,否則會去當前實例集self.querysets的每一個實例中去獲取相關的數據,使用函數去執行。字符串是調用自己的內置方法,function是調用自定義方法,同時可以使用"args"傳遞元組,"kwargs":傳遞字典作為參數 def __init__(self,model,querysets): super(ClassListForm, self).__init__(model,querysets)

class StudentForm(BaseForm): display_list = ["self"] #,"couser","semester" field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},} #在前面的標簽會顯示在內層 attrs = {"class":"table table-hover"} extra_field = [{"student_grade":{'verbose_name':"學員成績","value":"N/A"}},{"study_status":{"verbose_name":"出勤狀況","value":"N/A"}}] #額外自定義字段,若是有值value,會直接輸出,否則會去當前實例集self.querysets的每一個實例中去獲取相關的數據,使用函數去執行。字符串是調用自己的內置方法,function是調用自定義方法 def __init__(self,model,querysets): super(StudentForm, self).__init__(model,querysets)

class CourseForm(BaseForm): display_list = ["self","title","content","has_homework","homework","date"] #,"couser","semester" field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},} #在前面的標簽會顯示在內層 attrs = {"class":"table table-hover"} extra_field = [] #額外自定義字段,若是有值value,會直接輸出,否則會去當前實例集self.querysets的每一個實例中去獲取相關的數據,使用函數去執行。字符串是調用自己的內置方法,function是調用自定義方法 def __init__(self,model,querysets): super(CourseForm, self).__init__(model,querysets)
(3)在views中調用各個tableform

@login_required def course_list(request): course_querysets = models.ClassList.objects.filter( teachers=request.user.userprofile ) #自定義form,用於table forms = ClassListForm(models.ClassList,course_querysets) forms.register() return render(request,"teacher/class_list.html",locals()) @login_required def student_list(request,c_id): student_querysets = models.ClassList.objects.filter( teachers = request.user.userprofile, id = c_id ).get().student_set.all() forms = StudentForm(models.Student,student_querysets) forms.register() return render(request,"teacher/student_list.html",locals()) @login_required def classRec_list(request,c_id): course_querysets = models.ClassList.objects.filter( teachers = request.user.userprofile, id = c_id ).get().courserecord_set.all() forms = CourseForm(models.CourseRecord,course_querysets) forms.register() return render(request, "teacher/course_list.html", locals())
(4)前端調用{{ forms|safe }},form是定義的tableform變量,實現簡單顯示頁面

{% extends "index.html" %} {% load my_func %} {% block right-content-container %} <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">課程列表</h3> </div> <div class="panel-body"> {{ forms|safe }} </div> </div> {% endblock %}

{% extends "index.html" %} {% load my_func %} {% block right-content-container %} <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">課程記錄</h3> </div> <div class="panel-body"> {{ forms|safe }} <a href="{% url 'classRec_add' c_id %}" class="btn btn-primary">添加記錄</a> </div> </div> {% endblock %}

{% extends "index.html" %} {% load my_func %} {% block right-content-container %} <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">學員列表</h3> </div> <div class="panel-body"> {{ forms|safe }} </div> </div> {% endblock %}
(5)添加記錄

from django.forms import ModelForm,forms from repository import models class CustomerForm(ModelForm): class Meta: model = models.CustumerInfo #將表與元類中的數據關聯 fields = "__all__" exclude = ["consult_content","status","consult_courses"] readonly_fields = ['contact_type',"contact","consultant",'referral_from','source'] def __new__(cls, *args, **kwargs): #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)]) #這張表中的所有字段對象 for field_name,field_obj in dict(cls.base_fields).items(): field_obj.widget.attrs.update({'class':"form-control"}) if field_name in cls.Meta.readonly_fields: field_obj.widget.attrs.update({'disabled': "true"}) return ModelForm.__new__(cls) def clean(self): if self.errors: raise forms.ValidationError("Please fix errors before re-submit") if self.instance.id is not None: #這是一個修改的表單,而不是添加 for field_name in self.Meta.readonly_fields: new_val = self.cleaned_data[field_name] old_val = getattr(self.instance, field_name) if new_val != old_val: self.add_error(field_name, "ReadOnly fileds error: you cannot change %s"%old_val) class CourseRecordForm(ModelForm): class Meta: model = models.CourseRecord #將表與元類中的數據關聯 fields = "__all__" # exclude = ["teacher"] def __new__(cls, *args, **kwargs): #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)]) #這張表中的所有字段對象 for field_name,field_obj in dict(cls.base_fields).items(): field_obj.widget.attrs.update({'class':"form-control"}) return ModelForm.__new__(cls)

@login_required def classRec_add(request,c_id): if request.method == "GET": CRfm = forms.CourseRecordForm() else: CRfm = forms.CourseRecordForm(data=request.POST) CRfm.save() return redirect("/teacher/classlist/%s/course_list.html"%c_id) return render(request,"teacher/course_add.html",locals())

{% extends "index.html" %} {% load my_func %} {% block right-content-container %} <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">課程記錄添加</h3> </div> <div class="panel-body"> <form action="" method="post" class="form-group form-horizontal"> {% csrf_token %} {% for field in CRfm %} <div class="col-sm-6"> <div class="col-lg-4"> <label for="" class="control-label">{{ field.label }}</label> </div> <div class="col-lg-8"> {{ field }} </div> </div> {% endfor %} <div> <input type="submit" class="btn btn-primary" value="添加"> </div> </form> </div> </div> {% endblock %}
二:學員功能,實現課程顯示,作業提交
(一)根據郵件中的賬號密碼登錄
(二)實現查看班級,查看課程記錄,提交作業
(1)由於這里也是table多使用,可以繼續使用tableform
from Teacher.table_form import BaseForm class ClassListForm(BaseForm): display_list = ["self","class_type","start_date","graduate_date",] #,"couser","semester" field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},'score':{"a":{"href":"127.0.0.1:8080"},},'mng_homework':{"a":{"href":"/student/course.html"}}} #在前面的標簽會顯示在內層 attrs = {"class":"table table-hover"} extra_field = [{"score":{"verbose_name":"成績","value":"成績排名"}},{"mng_homework":{'verbose_name':"作業管理","value":"作業管理"}},] class ClassRecordForm(BaseForm): display_list = ["self","title","teacher","content","has_homework","homework"] #,"couser","semester" field_tag = {"self":{"a":{"href":"127.0.0.1:8000"},},'fin_homework':{"a":{"href":"/student/homework/{id}.html"}}} #在前面的標簽會顯示在內層 attrs = {"class":"table table-hover"} extra_field = [{"date":{"verbose_name":"日期","model_attr":"date","function":"strftime","args":("%Y-%m-%d %H:%M:%S",)}},{"fin_homework":{'verbose_name':"我的作業","value":"提交作業"}},] #額外自定義字段,若是有值value,會直接輸出,否則會去當前實例集self.querysets的每一個實例中去獲取相關的數據,使用函數去執行。字符串是調用自己的內置方法,function是調用自定義方法 def __init__(self,model,querysets): super(ClassRecordForm, self).__init__(model,querysets)
(2)view中調用,顯示班級和課程,前端也是{{forms|safe}}

# Create your views here. @login_required def couse_list(request): course_list = models.CourseRecord.objects.filter( class_grade__student=request.user.userprofile.student ).all() forms = ClassRecordForm(models.CourseRecord,course_list) forms.register() return render(request,"student/class_list.html",locals()) @login_required def AllClass(request): class_list = models.ClassList.objects.filter( student = request.user.userprofile.student ).all() forms = ClassListForm(models.ClassList,class_list) forms.register() return render(request,"student/class.html",locals())
(3)使用forms表單顯示數據,使用Dropzone添加作業

from django.forms import ModelForm,forms from repository import models class CourseRecordForm(ModelForm): class Meta: model = models.CourseRecord #將表與元類中的數據關聯 fields = "__all__" exclude = ["has_homework"] def __new__(cls, *args, **kwargs): #OrderedDict([('name', <django.forms.fields.CharField object at 0x00000000047FE9E8>), ('contact_type', <django.forms.fields.TypedChoiceField object at 0x00000000047FEBA8>), ('contact', <django.forms.fields.CharField object at 0x00000000047FECC0>), ('source', <django.forms.fields.TypedChoiceField object at 0x00000000047FEE10>), ('referral_from', <django.forms.models.ModelChoiceField object at 0x00000000047FEEF0>), ('consult_courses', <django.forms.models.ModelMultipleChoiceField object at 0x000000000480B048>), ('consult_content', <django.forms.fields.CharField object at 0x000000000480B0B8>), ('status', <django.forms.fields.TypedChoiceField object at 0x000000000480B208>), ('consultant', <django.forms.models.ModelChoiceField object at 0x000000000480B2E8>)]) #這張表中的所有字段對象 for field_name,field_obj in dict(cls.base_fields).items(): field_obj.widget.attrs.update({'class':"form-control","disabled":"true"}) return ModelForm.__new__(cls)

@login_required def homework(request,id): CRModel = models.CourseRecord.objects.filter(id=id,has_homework=True) if not CRModel.exists(): return HttpResponse("無作業") CRInfo = CRModel.get() cus_dir = os.path.join(conf.settings.STUDENT_HOMEWORK_DIR, str(id), str(request.user.userprofile.student.id)) if not os.path.isdir(cus_dir): os.makedirs(cus_dir) file_info = [] file_names = os.listdir(cus_dir) for filename in file_names: file_info.append(os.stat(os.path.join(cus_dir,filename))) if request.method == "GET": form = myforms.CourseRecordForm(instance=CRInfo) else: status = { 'status': True, "message": None } print(request.FILES) # 需要去接收文件,前端狀態才會是true if len(os.listdir(cus_dir)) >= 2: status['status'] = False status['message'] = "文件超出上傳個數" return HttpResponse(json.dumps(status)) file_obj = request.FILES.get("file") with open(os.path.join(cus_dir, file_obj.name), "wb") as fp: for chunks in file_obj.chunks(): fp.write(chunks) return HttpResponse(json.dumps(status)) return render(request,"student/homework.html",locals())
(4)前端代碼,使用Dropzone處理數據,以及ajax刪除數據

{% extends "index.html" %} {% load my_func %} {% block extra-link %} <link rel="stylesheet" href="/static/plugins/dropzone/dropzone.css"> {% endblock %} {% block right-content-container %} <div class="panel panel-primary"> <div class="panel-heading"> <h3 class="panel-title">作業提交</h3> </div> <div class="panel-body"> {{ form }} <div class="col-sm-12"> <table class="table table-hover" id="file_table"> <caption><label class="control-label" style="padding-top: 0px;">已上傳的文件目錄</label></caption> <thead> <tr> <th>文件名</th> <th>大小(KB)</th> <th>上傳時間</th> <th>刪除</th> </tr> </thead> <tbody> {% for file in file_info %} <tr> <td>{% get_list_value file_names forloop.counter0 %}</td> <td>{{ file.st_size }}</td> <td>{% get_date_str file.st_atime %}</td> <td><span style="color: red;" class="glyphicon glyphicon-remove" onclick="deleteFile(this);"></span></td> </tr> {% endfor %} </tbody> </table> <form action="{% url 'homework' CRInfo.id %}" id="myAwesomeDropzone" class="dropzone"> {% csrf_token %} <div class="fallback"> <input name="file" type="file" multiple /> </div> </form> </div> </div> </div> {% endblock %} {% block extra-js %} <script src="/static/plugins/dropzone/dropzone.js"></script> <script> $(function () { Dropzone.options.myAwesomeDropzone = { paramName: "file", // The name that will be used to transfer the file maxFilesize: 2, // MB maxFiles:2, parallelChunkUploads:true, accept: function(file, done) { if (file.name == "justinbieber.jpg") { done("Naha, you don't."); } else { done(); } }, init: function() { this.on("success", function(file,respone) { /* Maybe display some more file information on your page */ var rep = JSON.parse(respone) if(!rep.status){ alert(rep.message); return; }else{ var myDate = new Date(); var str_tm = myDate.toLocaleString(); str_tm = str_tm.replace(/\//g, "-"); str_tm = str_tm.replace(/[\u4e00-\u9fa5]+/g, ""); var tr = "<tr><td>"+file.name+"</td><td>"+file.size+"</td><td>"+str_tm+"</td><td>"+'<span style="color: red;" onclick="deleteFile(this);" class="glyphicon glyphicon-remove"></span></td></tr>' $("#file_table").append(tr); } }); } }; }); function deleteFile(ths){ var filename = $($(ths).parents("tr").children()[0]).text() $.ajax({ url:"/student/delete_file.html", data:{'c':'{{ id }}','f':filename,'csrfmiddlewaretoken':'{{ csrf_token }}'}, dataType:"json", type:"post", success:function(data){ if(data.status){ $(ths).parents("tr").remove() }else{ alert(data.message) } } }) } </script> {% endblock %}
(5)后台處理數據刪除

@login_required def delete_files(request): if request.method == "POST": status = { 'status':True, 'message':"" } c_id = request.POST['c'] filename = request.POST['f'] current_path = os.path.join(conf.settings.STUDENT_HOMEWORK_DIR, str(c_id), str(request.user.userprofile.student.id)) file_path = os.path.join(current_path,filename) print(file_path) if not os.path.isfile(file_path): status['status']=False status['message'] = "沒有權限" return HttpResponse(json.dumps(status)) else: os.remove(file_path) return HttpResponse(json.dumps(status))