1. 程式人生 > >stark組件開發之關鍵搜索

stark組件開發之關鍵搜索

能夠 調用 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組件開發之關鍵搜索