stark
Ⅰ 排序
當數據量增多,對于數據 我們應該能夠指定如何排序的。且此功能應該是可以給用戶自定義進行配置的。
這是StarkHandler類的方法
1 order_list = [] 2 def get_order_list(self): 3 return self.order_list or ['id'] #默認是按照id排序
Combine▼
list_view下的排序功能:
1 order_list = self.get_order_list() 2 3 #組合搜索條件字典 4 search_dict = self.get_search_group_condition(request) 5 6 #自定義方法篩選后的結果 7 queryset = self.get_queryset(**kwargs) 8 9 queryset = queryset.filter(conn).filter(**search_dict).all().order_by(*order_list)
用戶使用配置只需在子類Handler寫上
1 order_list = ['-id','name'] #優先根據-id排序,完后再根據name排序
Ⅱ 模糊搜索
實現思路:
在頁面設置form表單,搜索:以Get形式提交到后臺。后臺獲取數據然后進行篩選過濾。
后端獲取關鍵字之后,根據定義的列進行查找(多列可以按照 "或" 進行查詢)
同樣,預留勾子,功能可以給用戶自定義進行配置的。
這是StarkHandler類的方法
1 search_list = [] #為空的話,模板默認不顯示搜索按鈕 2 def get_search_list(self): 3 return self.search_list
Combine▼
list_view下的模糊搜索功能:
1 search_list = self.get_search_list() 2 search_value = request.GET.get('q','') 3 4 from django.db.models import Q #實現“或”查詢 5 conn = Q() 6 conn.connector = 'OR' 7 8 if search_value: 9 #Q,用于構造復雜的ORM查詢條件 10 for item in search_list: 11 conn.children.append((item,search_value))
即為上面的 queryset = queryset.filter(conn).filter(**search_dict).all().order_by(*order_list)
用戶使用配置只需在子類Handler寫上
1 search_list = ['name'] 2 search_list = ['name__contains','email__contains']
Ⅲ 批量操作
首先,添加checkbox列
1 def display_checkbox(self,obj=None,is_header=None):
2 """
3 生成checkbox 批量操作
4 :param obj:
5 :param is_header:
6 :return:
7 """
8 if is_header:
9 return "選擇"
10 return mark_safe('<input type="checkbox" name="pk" value="%s">'%obj.pk)
#name屬性對表單數據進行標識,通過request.POST.getlist('pk')獲得選取值。
如圖 →
然后,生成批量操作按鈕
考慮到以后功能拓展,功能數量增加,生成過多按鈕而影響頁面的布局,
為此,這里以下拉框的形式展示這些功能。即<select><option>Function</option></select>
如圖 →
同樣,預留勾子,功能可以給用戶自定義進行配置的
這是StarkHandler類的方法
1 action_list = [] #默認是沒有批量操作的 2 def get_action_list(self): 3 """ 4 處理批量操作等自定義方法 5 :return: 6 """ 7 return self.action_list
下面是"一鍵刪除"的例子
這是StarkHandler類的方法
1 def multi_delete(self,request): 2 """ 3 批量刪除 4 :param request: 5 :return: 6 """ 7 pk_list = request.POST.getlist('pk') 8 self.model_class.objects.filter(id__in=pk_list).delete() 9 10 multi_delete.text = '一鍵刪除' #在python中,一切皆對象,都可以使用反射。
用戶使用配置只需在子類Handler寫上
1 action_list = [StarkHandler.multi_delete,] #用戶可以在子類自定義方法,添加到action_list
展示的形式 與 可拓展 解決了,下一步我們只要處理請求傳入過來的數據即可。
處理action_list & 獲取操作并執行
action_list不能直接放在模板進行循環展示,因為模板中 {{ func }}會自動執行,
因此我們得把獲得action_list做小小的處理,做成字典的形式{ 函數名 : 函數文本名稱 },再到模板中顯示。
list_view下處理action_list:
1 action_list = self.action_list 2 action_dict = {func.__name__:func.text for func in action_list} 3 4 if request.method == 'POST': 5 func_name = request.POST.get('action') 6 if func_name and func_name in action_dict: #邏輯優化,防止被 方法亂入 7 func = getattr(self,func_name) 8 func(request)
Ⅳ 組合搜索
根據字段找到關聯的數據choice、ForeignKey、ManyToMany
實現思路:
第一步:配置
預留勾子,功能可以給用戶自定義進行配置的
這是StarkHandler類的方法
1 search_group = [] 2 def get_search_group(self): 3 return self.search_group
第二部:根據配置獲取數據。根據用戶寫入的字符串,去自己對應的Model類中找到 字段對象。
但是怎么知道傳入進來的字符串對應的字段對象是 choice字段、FK還是M2M?
這里我們可以通過類型進行判斷一下 ▼
1 from django.db.models import ManyToManyField,ForeignKey 2 3 field_object = model_class._meta.get_field(self.field) 4 #choice_obj: app01.UserInfo.gender 5 #FK_oobj: app01.UserInfo.depart 6 if isinstance(field_object,ForeignKey) or isinstance(field_object,ManyToManyField):
7 #Fk和M2M,應該去獲取其相關表中的數據 8 print(field_object.remote_field.model) 獲得關聯表的類
9 else:
10 #獲取choice中的數據
11 print(field_object.choices)
第三部:根據配置獲取數據(含條件)。在實現 組合搜索按鈕 查找數據的時候,可以把關聯的數據全部獲取到,但是我們的代碼應該可以支持設置條件的,也就是可以添加篩選條件等。比如想要生成一個age>5的搜索按鈕。且可以給用戶自定義設置的。
不要用字典列表反復嵌套。縱觀寫得牛逼的源碼,封裝的時候很少使用dict,tuple,一般都是封裝成類,再去類中獲取。
為了把上面想到的功能優雅地寫入serch_group,我們進行簡單的代碼拆分,封裝到一個類中。
1 class Option(object):
2 def __init__(self,field,is_multi=False,db_condition=None,text_func=None,value_func=None):
3 """
4 :param field: 組合搜索關聯的字段
5 :param is_multi: 是否支持多選
6 :param db_condition: 數據庫關聯查詢時的條件
7 :param text_func: 此函數用于組合搜索按鈕 展示 文本或者圖案
8 :param value_func: 此函數用于顯示組合搜索按鈕值
9 """
10 self.field = field
11 self.is_multi = is_multi
12 if not db_condition:
13 db_condition = {} #默認是為空的,即無查詢條件
14 self.db_condition = db_condition
15 self.text_func = text_func
16 self.value_func = value_func
17
18 self.is_choice = False
19
20 def get_db_condition(self):
21 return self.db_condition
22
23 def get_queryset_or_tuple(self,model_class,request,*args,**kwargs): #第二步的處理封裝在這里
24 """
25
26 :param model_class:
27 :param request:
28 :param args:
29 :param kwargs:
30 :return:
31 """
32 field_object = model_class._meta.get_field(self.field) #choice:app01.UserInfo.gender #FK:app01.UserInfo.depart
33
34 title = field_object.verbose_name
35 if isinstance(field_object,ForeignKey) or isinstance(field_object,ManyToManyField):
36 db_condition = self.get_db_condition()
37 #### 返回的是QuerySet類型 ####
38 #print(field_object.remote_field.model.objects.filter(**db_condition)) <QuerySet [<Depart: 技術部>, <Depart: 美術部>]>
39 return SearchGroupRow(title,field_object.remote_field.model.objects.filter(**db_condition),self,request.GET) #self是Option類對象
40 else:
41 #### 獲取choice中的數據 / 返回的是元祖類型 ####
42 self.is_choice = True
43 # print(field_object.choices) ((1, '男'), (2, '女'))
44 return SearchGroupRow(title,field_object.choices,self,request.GET)
45
46 def get_text(self,field_object):
47 if self.text_func: #如果自定義了函數,則用自己的
48 return self.text_func(field_object)
49
50 if self.is_choice:
51 return field_object[1]
52
53 return str(field_object)
54
55 def get_value(self,field_object):
56 if self.value_func: #自定制
57 return self.value_func(field_object)
58
59 if self.is_choice:
60 return field_object[0]
61
62 return field_object.pk
用戶使用配置只需在子類Handler寫上,
且用戶如果需要根據自己的情況處理db_condition,繼承該類,重寫方法,返回字典。
1 search_group = [Option('gender',is_multi=True),]
2 search_group = [Option('name',db_condition={"id__gt":5})]
第四步:在頁面上顯示組合搜索的按鈕內容(將QuerySet 和 tuple進行封裝)
上面,我們針對choice,FK,M2M 字段對象區分處理了,但是FK,M2M返回的的是QuerySet類型,而choice返回的是元祖。
如果把結果直接拿到模板渲染的話,模板上還需要 if...else 再次判斷,區分處理,循環數據,前端就會莫名復雜。
想法:
把代碼放到后端,讓后端通過python進行判斷,而模板只進行循環展示。
這里我們重新封裝一個類,把前面得到的結果傳入進行處理,返回統一的對象。
補充一個知識點
1 # 一般情況下實例化的對象是不可以被迭代的 2 # 如果一個類中定義了__iter__方法且該方法返回一個迭代器,那么就稱該實例化的對象為可迭代對象。即對象可以被循環。 3 4 class test(): 5 def __init__(self,name): 6 self.name = name 7 def __iter__(self): 8 return iter([1,2,3,4]) 9 10 # 生成器是一種特殊的迭代器。所以還可以這么寫, 11 def __iter__(self): 12 yield 1 13 yield 2 14 yield 3
View Code
在此基礎下,根據傳入不同結果進行判斷,再返回相應可迭代對象。
class SearchGroupRow(object):
def __init__(self,title,queryset_or_tuple, option_object,query_dict):
"""
:param title: 組合搜索的列名稱
:param queryset_or_tuple: 組合搜索關聯獲取到的數據
:param option_object: Option對象
:param query_dict: request.GET
"""
self.title = title
self.queryset_or_tuple = queryset_or_tuple
self.option_object = option_object
self.query_dict = query_dict
def __iter__(self):
yield mark_safe('<span class="search_group label label-default">%s</span>'%self.title)
for item in self.queryset_or_tuple:
text = self.option_object.get_text(item) #性別 /部門
value = self.option_object.get_value(item) #'1' /技術部
query_dict = self.query_dict.copy() #{'gender': ['1']} #生成下面URL需要(1)
query_dict._mutable = True
if not self.option_object.is_multi:
request_value_list = query_dict.getlist(self.option_object.field) #獲取GET數據 ['2']
query_dict[self.option_object.field] = value #生成下面URL需要(2)
# print(text,str(value),request_value_list)
if str(value) in request_value_list:
#通過去除 ?gender=2 重新賦值URL,實現再選取時 自動消失樣式
query_dict.pop(self.option_object.field)
yield mark_safe('<a class="btn btn-danger active" href="?%s">%s</a>'%(query_dict.urlencode(),text))
else:
yield mark_safe('<a class="btn btn-default" href="?%s">%s</a>'%(query_dict.urlencode(),text))
else:
multi_request_value_list =query_dict.getlist(self.option_object.field) #['1','2']
if str(value) in multi_request_value_list:
multi_request_value_list.remove(str(value))
query_dict.setlist(self.option_object.field,multi_request_value_list)
yield mark_safe('<a class="btn btn-danger active" href="?%s">%s</a>'%(query_dict.urlencode(),text))
else:
multi_request_value_list.append(str(value))
query_dict.setlist(self.option_object.field,multi_request_value_list)
yield mark_safe('<a class="btn btn-default" href="?%s">%s</a>'%(query_dict.urlencode(),text))
Combine above▼
1 #list_view下處理search_group: 2 search_group_row_list = [] 3 search_group = self.get_search_group() # ['gender', 'depart'] 4 for keyword_object in search_group: #keyword是一個Option類實例對象 5 row = keyword_object.get_queryset_or_tuple(self.model_class,request,*args,**kwargs) 6 #返回的是一個SearchGroupRow對象,且可迭代 7 search_group_row_list.append(row)
模板傳入seach_group_row_list,循環字段對象、再渲染 → 如圖:
第五步:為組合搜索按鈕生成URL
多組搜索按鈕,生成URL時,應該不影響其他組的條件。即 /?gender=1&depart=1。
1.根據 Option.get_text ,Option.get_value 返回字段對象 text值和所有value值
2.生成帶參數的URL,渲染模板
具體代碼紫色部分
第六步:獲取請求的數據進行篩選
獲取 多組搜索按鈕URL參數 后,需要根據條件篩選數據。
在篩選數據之前,如果我想實現支持單選和多選的多組搜索按鈕?進而獲取呢?
1 def get_search_group_condition(self,request):
2 condition = {}
3 for option_object in self.get_search_group():
4 if option_object.is_multi: 5 value_list = request.GET.getlist(option_object.field) #tags=[1,2]
6 if not value_list:
7 continue
8 condition['%s__in' %option_object.field] = value_list
9 else:
10 value = request.GET.get(option_object.field)
11 if not value:
12 continue
13 condition[option_object.field] = value
14
15 return condition
返回字典(篩選條件單選或多選),交給list_view處理。
1 queryset = queryset.filter(conn).filter(**search_dict).all().order_by(*order_list)
暫時想到這么多....
總結
- 上一篇: 交换机命令行配置与VLAN
- 下一篇: 怎么创建具有真实纹理的CG场景岩石?