- 模糊搜索:
在页面生成一个表单。 以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()
#! /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)
{% 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 %}
原文:https://www.cnblogs.com/chengege/p/10742395.html