stark組件開發之關鍵搜索
阿新 • • 發佈:2019-04-20
能夠 調用 http 後綴 查詢 接口 初始化方式 class類 模糊
- 模糊搜索:
在頁面生成一個表單。 以get 方式, 將數據提交到。當前查看頁面。 後臺接收數據,然後進行篩選過濾。
著個也需要,用戶自定制!
定義一個 search_list 這個值,默認為空。 頁面進行判斷,為空就不顯示。 搜索框!
如果,用戶 定義了這個列表, 那麽就顯示!
SharkHandler:
search_list = [] # 方便,用戶自己定制 def get_search_list(self): return self.search_lis
子類中, 由用戶自己定制:
class UserInfoHandler(StartHandler): list_display= ["name", "age", "depart", get_choice_txt("性別", "gender"), StartHandler.display_edit, StartHandler.display_del] per_page = 10 # 重訂 每頁顯示 多少 數據 has_add_btn = True ordered_list = ["-id"] # 排序的規則! 默認是 id 自己制定 -id 表示。反向 id 排序 # 模糊查詢使用,包含。 "name_contains" 表示name字段包含。。。 filter(name_contains="alex") 過濾名字中包含alex的名字search_list = ["name_contains"] # 如果這裏不設置的話, 就不顯示搜索框
接下來就是一個 ,模糊搜索了: 先看一了 例子!:
from django.db.models import Q, F # 用於構造復雜的 搜索條件 conn = Q() conn.connector = "OR" # 讓添加進來的條件, 做 or 判斷 conn.children.append(("name__contains", "lijie")) conn.children.append(("email", "lijie")) conn.children.append(("id__gt", 5)) self.model_class.objects.filter(conn) # 這樣就可以根據添加進 Q 的條件, 按照 or 的方式。在數據庫查詢!
ORM 無法進行,復雜的搜索條件。 所以使用 django 內置的 Q 方法來做這件事!
所以重點還是 子類中需要自定制的 search_list = ["name_contains"] 這裏才是決定了。 前端更夠查詢到的東西。
但是,這裏還是, 定死了的。 如果想要更加的靈活。 以後再說:
先看看。 整體代碼吧!
#! /usr/bin/env python # -*- coding: utf-8 -*- # __author__ = "nothing" # Date: 2019/4/18 import functools from types import FunctionType from django.urls import path, re_path, reverse from django.http import HttpResponse, JsonResponse, QueryDict from django.shortcuts import render, redirect, reverse from django.utils.safestring import mark_safe from django import forms from app01 import models from stark.utils.pagination import Pagination from django.db.models import Q, F # 用於構造復雜的 搜索條件 class StarkModelForm(forms.ModelForm): ‘‘‘ 因為,太多的地方需要使用, __init__ 初始化方式。來對每個標簽添加 class="form-control" 所以搞個基類讓 要進行, 這部操作的 類去繼承, ‘‘‘ def __init__(self, *args, **kwargs): super(StarkModelForm, self).__init__(*args, **kwargs) for name, field in self.fields.items(): field.widget.attrs["class"] = "form-control" class StartHandler(object): list_display = [] def __init__(self, site, model_class, prve): self.site = site self.model_class = model_class self.prev = prve self.request = None has_add_btn = True # 指定配置,默認顯示。 用戶在子類中,自定制是否顯示,添加按鈕 def get_add_btn(self): ‘‘‘預留鉤子,子類中重寫該方法。 根據權限的判斷是否顯示添加按鈕‘‘‘ if self.has_add_btn: # 根據別名反向生成, URL add_url = self.memory_url(self.get_add_url_name) return "<a class=‘btn btn-primary‘ href=‘%s‘>添加</a>" % add_url return None # 用於在 a 標簽。 攜帶本次GET 請求參數 def memory_url(self, get_url_name, *args, **kwargs): ‘‘‘用於反向生成url, 並且攜帶,get請求的參數,跳轉到下一個網頁‘‘‘ name = "%s:%s" % (self.site.namespace, get_url_name) base_url = reverse(name, args=args, kwargs=kwargs) # 記錄原搜索條件 if not self.request.GET: add_url = base_url else: param = self.request.GET.urlencode() # 獲取到GET請求的,所有的參數。 ?page=1&age=20 new_query_dict = QueryDict(mutable=True) new_query_dict["_filter"] = param add_url = "%s?%s" % (base_url, new_query_dict.urlencode()) return add_url # 用於 跳轉會原頁面時,解析出 GET 請求的參數。並拼接 def memory_reverse(self, get_url_name, *args, **kwargs): name = "%s:%s" % (self.site.namespace, get_url_name) url = reverse(name, args=args, kwargs=kwargs) origin_params = self.request.GET.get("_filter") if origin_params: url = "%s?%s" % (url, origin_params) return url # 用於用戶自定制, 是否顯示編輯按鈕, 和顯示的樣式 def display_edit(self, obj=None, is_header=None): ‘‘‘ 自定義頁面,顯示的列,(表頭和內容) :param obj: 數據庫中每一行記錄的 model對象 :param is_header: 判斷是否為表頭 :return: ‘‘‘ if is_header: return "編輯表頭" # name = "%s:%s" % (self.site.namespace, self.get_edit_url_name) # 拼接 stark:app01_userinfo_change return mark_safe("<a href=‘%s‘>編輯</a>" % self.memory_url(get_url_name=self.get_edit_url_name, pk=obj.pk)) # 用於用戶自定制, 是否顯示刪除按鈕 def display_del(self, obj=None, is_header=None): if is_header: return "刪除表頭" # name = "%s:%s" % (self.site.namespace, self.get_del_url_name) return mark_safe("<a href=‘%s‘>刪除</a>" % self.memory_url(get_url_name=self.get_del_url_name, pk=obj.pk)) def get_list_display(self): ‘‘‘ 獲取不同用戶登錄時, 頁面應該顯示的列. 使用時在子類中,重寫該方法,指定list_display 要包含哪些值 :return: ‘‘‘ value = [] value.extend(self.list_display) return value ordered_list = [] # 排序規則由 用戶指定。 def get_ordered_list(self): return self.ordered_list or ["id", ] # 默認使用 id 進行排序 search_list = [] # 方便,用戶自己定制 def get_search_list(self): return self.search_list per_page = 10 # 默認每頁顯示,多少數據。 也可在子類中,自行定制 def check_list_view(self, request): ‘‘‘ 列表查看頁面 :param request: :return: ‘‘‘ # self.request = request # 進入查看頁面,為request賦值! 使其他地方可以用到! list_display = self.get_list_display() # 頁面要顯示的列 self.list_display 示例:[‘name‘, ‘age‘, ‘depart‘] # 1. 制作表頭, 就是每張表中,每個字段寫的 verbose_name.。 如何獲取到這個值呢? # self.model_class._meta.get_field(‘name‘).verbose_name header_list = [] # 表頭 if list_display: for key_or_func in list_display: if isinstance(key_or_func, FunctionType): # 判斷當前參數, 是一個字符串還是一個函數。 verbose_name = key_or_func(self, obj=None, is_header=True) else: verbose_name = self.model_class._meta.get_field(key_or_func).verbose_name header_list.append(verbose_name) else: header_list.append(self.model_class._meta.model_name) # ##################獲取排序###################### order_list = self.get_ordered_list() # ##################搜索條件###################### search_list = self.get_search_list() # 搜索的條件 ["name_contains", "email"] ‘‘‘ 1. 如果 search_list 為空, 則不顯示 搜索框 2. 獲取用戶輸入的 關鍵字 3. 構造搜索條件 ‘‘‘ search_value = self.request.GET.get("q", None) # 獲取用戶發送過來的關鍵字,如果沒有 q 這個參數。 就返回 None conn = Q() conn.connector = "OR" # 讓添加進來的條件, 做 or 判斷 if search_value: # 接收到了, 用戶的搜索才, 進行 模糊查詢。 否則 啥都不幹 for item in search_list: conn.children.append((item, search_value)) # 2. 處理 從數據庫 取到的數據 # 用戶訪問的表 self.model_class query_set = self.model_class.objects.filter(conn).order_by(*order_list) # 計算總數量,和 表格顯示內容時,都需要,就提取出來了 # 2.1 ###############處理分頁################# ‘‘‘1.根據用戶訪問頁面,計算出索引的位置, 比如 page=3 2. 生成html頁碼 ‘‘‘ all_count = query_set.count() query_params = request.GET.copy() # page=1&level=2 query_params._mutable = True # request.get中的值默認是不能被修改的。加上這句代碼就可以修改了 pager = Pagination( current_page=request.GET.get("page"), # 用戶訪問的當前葉 all_count=all_count, # 數據庫一共有多少數據 base_url=request.path_info, # 所在的url 就是 ?page=1 之前的URL # 用於保留,用戶的請求信息,比如 level=2 被用戶先選中。 那麽分頁後。因為查詢的東西少了,分頁也應該想要的減少, # 但是level=2這個, 請求的信息!不能因為。分頁的原因。而減少。 query_params=query_params, per_page=self.per_page, # 每頁顯示多少數據。 ) # 2.1 ###############處理表格################# data_list = query_set[pager.start:pager.end] body_list = [] for row in data_list: row_list = [] if list_display: for key_or_func in list_display: if isinstance(key_or_func, FunctionType): # 這裏is_header=False obj=row(數據庫中循環的每一行的對象) row_list.append(key_or_func(self, obj=row, is_header=False)) else: row_list.append(getattr(row, key_or_func)) else: row_list.append(row) body_list.append(row_list) # 3 ############# 處理添加按鈕#################### add_btn = self.get_add_btn() return render(request, "stark/changelist.html", {"header_list": header_list, "data_list": data_list, "body_list": body_list, "pager": pager, "add_btn": add_btn, "search_list": search_list, "search_value": search_value}) model_form_class = None # 預留自定義form對象的接口 def get_model_form_class(self): if self.model_form_class: return self.model_form_class class DynamicModelForm(StarkModelForm): class Meta: model = self.model_class fields = "__all__" return DynamicModelForm # 預留的 form 保存。自定制接口 def save(self, form, is_update=False): ‘‘‘ 在使用 ModelForm 保存數據之前,預留的鉤子方法 :param form: 每一個 model_class 自己的 form 對象 :param is_update: :return: ‘‘‘ form.save() def add_view(self, request): ‘‘‘ 添加頁面 :param request: :return: ‘‘‘ model_form_class = self.get_model_form_class() if request.method == "GET": form = model_form_class() return render(request, "stark/change.html", {"form": form}) form = model_form_class(data=request.POST) if request.method == "POST": if form.is_valid(): self.save(form, is_update=False) return redirect(self.memory_reverse(self.get_list_url_name)) return render(request, "stark/change.html", {"form": form}) def change_view(self, request, pk): ‘‘‘ 編輯頁面 :param request: :return: ‘‘‘ current_change_obj = self.model_class.objects.filter(pk=pk).first() if not current_change_obj: return HttpResponse("要修改的頁面不存在,請重新選擇") model_form_class = self.get_model_form_class() if request.method == "GET": form = model_form_class(instance=current_change_obj) return render(request, "stark/change.html", {"form": form}) form = model_form_class(data=request.POST, instance=current_change_obj) if request.method == "POST": if form.is_valid(): self.save(form, is_update=False) return redirect(self.memory_reverse(self.get_list_url_name)) return render(request, "stark/change.html", {"form": form}) return HttpResponse("編輯頁面") def delete_view(self, request, pk): ‘‘‘ 刪除頁面 :param request: :return: ‘‘‘ origin_url = self.memory_reverse(get_url_name=self.get_list_url_name) current_model_obj = self.model_class.objects.filter(pk=pk) if not current_model_obj: return HttpResponse("要修改的記錄不存在,請重新選擇") if request.method == "POST": current_model_obj.delete() return redirect(origin_url) return render(request, "stark/delete.html", {"cancel": origin_url}) def get_url_name(self, param): ‘‘‘ 判斷是否有後綴 prev。 進行拼接URL的別名 :param param: 頁面後綴(list, add, change, del) :return: ‘‘‘ # 獲取每個model_class類。所在的app_name 和 他自己的 表名稱model_name app_label, model_name = self.model_class._meta.app_label, self.model_class._meta.model_name if self.prev: return "%s_%s_%s_%s" % (app_label, model_name, self.prev, param) # app01/userinfo/prev/list/ return "%s_%s_%s" % (app_label, model_name, param) # app01/userinfo/list/ @property def get_list_url_name(self): ‘‘‘獲取列表頁面URL 的name‘‘‘ return self.get_url_name("list") @property def get_add_url_name(self): ‘‘‘獲取添加頁面URL 的name‘‘‘ return self.get_url_name("add") @property def get_edit_url_name(self): ‘‘‘獲取修改頁面URL 的name‘‘‘ return self.get_url_name("change") # app01_userinfo_change @property def get_del_url_name(self): ‘‘‘獲取刪除頁面URL 的name‘‘‘ return self.get_url_name("del") def wrapper(self, func): @functools.wraps(func) # 保留原函數的 原信息 def inner(request, *args, **kwargs): # 這個inner 就是,我的每一個視圖函數了! self.request = request return func(request, *args, **kwargs) return inner def get_urls(self): partterns = [ re_path(r"list/$", self.wrapper(self.check_list_view), name=self.get_list_url_name), re_path(r"add/$", self.wrapper(self.add_view), name=self.get_add_url_name), re_path(r"change/(?P<pk>\d+)/$", self.wrapper(self.change_view), name=self.get_edit_url_name), re_path(r"del/(?P<pk>\d+)/$", self.wrapper(self.delete_view), name=self.get_del_url_name), ] partterns.extend(self.extra_url()) return partterns def extra_url(self): return [] class StartSite(object): def __init__(self): self._registry = [] self.app_name = "stark" self.namespace = "stark" def register(self, model_class, handler_class=None, prev=None): ‘‘‘ :param model_class: 是model中數據庫相關類。 接受一個類而不是對象 :param handler_class: 處理請求的視圖函數,所在的類 :param prev: 生成url 的前綴 :return: ‘‘‘ if handler_class is None: handler_class = StartHandler # 做個默認的Handler self._registry.append( {‘model_class‘: model_class, "handler": handler_class(self, model_class, prev), "prev": prev}) ‘‘‘ [ {‘model_class‘:models.Depart, "handler":DepartHandler(models.Depart, prev),"prev": prev}, {‘model_class‘:models.UserInfo, "handler":UserInfoHandler(models.UserInfo, prev),"prev": prev}, {‘model_class‘:models.Host, "handler":HostHandler(models.Host, prev),"prev": prev}, ] ‘‘‘ def get_urls(self): partterns = [] for item in self._registry: model_class = item["model_class"] handler = item["handler"] prev = item["prev"] # 獲取當前model_class所在的app名字 # 獲取當前model_class的類名,小寫 app_label, model_name = model_class._meta.app_label, model_class._meta.model_name if prev: partterns.append( re_path(r"%s/%s/%s/" % (app_label, model_name, prev), (handler.get_urls(), None, None))) else: partterns.append(re_path(r"%s/%s/" % (app_label, model_name), (handler.get_urls(), None, None))) return partterns @property def urls(self): ‘‘‘模擬include的返回值‘‘‘ return (self.get_urls(), self.app_name, self.namespace) def get_choice_txt(title, field): ‘‘‘ 對於 Stark組件中定義列時, choice如果想要顯示中文信息,調用此方法即可。 :param title: 希望頁面上顯示的表頭 :param field: 字段名稱 :return: ‘‘‘ def inner(self, obj=None, is_header=None): ‘‘‘ :param self: :param obj: StarkHandler 裏面列表視圖函數 中 循環出的每一個 model對象 :param is_header: :return: ‘‘‘ if is_header: return title method = "get_%s_display" % field return getattr(obj, method)() # 從model對象中,根據這個字符串。找到這個方法。 並執行。 拿到中文結果後, 返回 return inner site = StartSite()\stark\servers\start_v1.py
#! /usr/bin/env python # -*- coding: utf-8 -*- # __author__ = "nothing" # Date: 2019/4/18 from django.urls import re_path from stark.servers.start_v1 import site, StartHandler, get_choice_txt, StarkModelForm from django.http import HttpResponse, JsonResponse from django.utils.safestring import mark_safe from django.urls import reverse from app01 import models from django import forms class DepartHandler(StartHandler): ‘‘‘定義增刪改查‘‘‘ list_display = ["id", "title", StartHandler.display_edit, StartHandler.display_del] # def extra_url(self): # 自定制,新增URL # return [ # re_path("detail/(\d+)/$", self.detail_view) # ] # # def detail_view(self): # return HttpResponse("詳情頁") site.register(models.Depart, DepartHandler) class UserInfoModelForm(StarkModelForm): class Meta: model = models.UserInfo fields = ["name", "gender", "age", "depart"] class UserInfoHandler(StartHandler): list_display = ["name", "age", "depart", get_choice_txt("性別", "gender"), StartHandler.display_edit, StartHandler.display_del] per_page = 10 # 重訂 每頁顯示 多少 數據 has_add_btn = True ordered_list = ["-id"] # 模糊查詢使用,包含。 "name_contains" 表示name字段包含。。。 filter(name_contains="alex") 過濾名字中包含alex的名字 search_list = ["name__contains"] # 如果這裏不設置的話, 就不顯示搜索框 # model_form_class = UserInfoModelForm # # def save(self, form, is_update=False): # form.instance.pwd = 123 # form.save() # def get_add_btn(self): # pass # def get_list_display(self): # ‘‘‘預留的自定義擴展。調用父類方法,返回一個包含 模型表字段的 列表。 並且可以根據用戶的不同,顯示不同的列 # 此方法,不能夠和 list_display 列表共同使用。 此方法會覆蓋list_display‘‘‘ # return ["depart", self.display_edit] site.register(models.UserInfo, UserInfoHandler)\app01\stark.py
{% extends "layout.html" %} {% block content %} <div class="luffy-container"> <h3>數據列表</h3> {% if add_btn %} <div style="float: left">{{ add_btn|safe }}</div> {% endif %} {% if search_list %} <div style="float: right; margin-bottom: 5px"> <form method="get" class="form-inline"> <div class="form-group"> <input type="text" name="q" value="{{ search_value }}" placeholder="關鍵字搜索" class="form-control"> <button type="submit" class="btn-primary btn"> <i class="fa fa-search" aria-hidden="true"></i> </button> </div> </form> </div> {% endif %} <table class="table table-bordered"> <thead> <tr> {% for head in header_list %} <th>{{ head }}</th> {% endfor %} </tr> </thead> <tbody> {% for row in body_list %} <tr> {% for ele in row %} <td>{{ ele }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> </div> <nav> <ul class="pagination"> {{ pager.page_html|safe }} </ul> </nav> {% endblock %}\stark\changelist.html
stark組件開發之關鍵搜索