strak组件(9):关键字搜索

效果图:

在列表视图函数增加搜索功能

一、strak组件

strak/service/core_func.py

import functools
from types import FunctionType

from django import forms
from django.db.models import Q
from django.http import QueryDict
from django.urls import re_path
from django.utils.safestring import mark_safe
from django.shortcuts import HttpResponse, render, reverse, redirect

from stark.utils.pagination import Pagination


class StarkModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(StarkModelForm, self).__init__(*args, **kwargs)
        # 统一给ModelForm生成字段添加样式
        for name, field in self.fields.items():
            field.widget.attrs['class'] = 'form-control'


def get_choice_text(title, field):
    """
    对于Stark组件中定义列时,choice如果想要显示中文信息,调用此方法即可。
    :param title: 希望页面显示的表头
    :param field:  字段名称
    :return:
    """

    def inner(self, obj=None, is_header=None):
        if is_header:
            return title
        method = "get_%s_display" % field
        return getattr(obj, method)()
        # GENDER_CHOICES = ((MALE, '男'),(FEMALE, '女'),)
        # 对于choice字段,如果想获取获取第二个值,可以通过:对象.get_字段名_display()

    return inner


class StarkHandler(object):
    list_display = []
    order_list = []
    search_list = []
    per_page_data = 10
    has_add_btn = True
    model_form_class = None
    list_template = None
    add_template = None
    edit_template = None
    delete_template = None

    def __init__(self, site, model_class, prev):
        self.site = site
        self.model_class = model_class
        self.prev = prev
        self.request = None

    def display_edit(self, obj=None, is_header=None):
        """
        自定义页面显示的列(表头和内容)
        :param obj:
        :param is_header:
        :return:
        """
        if is_header:
            return '编辑'
        name = '%s:%s' % (self.site.namespace, self.get_edit_url_name,)
        return mark_safe('<a href="%s">编辑</a>' % reverse(name, args=(obj.pk,)))

    def display_delete(self, obj=None, is_header=None):
        if is_header:
            return '删除'
        name = '%s:%s' % (self.site.namespace, self.get_delete_url_name,)
        return mark_safe('<a href="%s">删除</a>' % reverse(name, args=(obj.pk,)))

    def get_list_display(self):
        """
        获取页面上应该显示的列,预留的自定义扩展,例如:以后根据用户的不同显示不同的列
        :return:
        """
        value = []
        value.extend(self.list_display)
        return value

    def get_search_list(self):
        return self.search_list

    def get_add_btn(self, *args, **kwargs):
        if self.has_add_btn:
            return '<a class="btn btn-primary" href="%s">添加</a>' % self.reverse_add_url(*args, **kwargs)

    def get_model_form_class(self, request, *args, **kwargs):
        if self.model_form_class:
            return self.model_form_class

        class DynamicModelForm(StarkModelForm):
            class Meta:
                model = self.model_class
                fields = '__all__'

        return DynamicModelForm

    def get_order_list(self):
        return self.order_list or ['-id', ]

    def list_view(self, request, *args, **kwargs):
        """
        列表页面
        :param request:
        :return:
        """

        # 搜索列表写ORM语句,如:['name__contains','email__contains','id__gt','gender']
        search_list = self.get_search_list()
        search_value = request.GET.get('q', '')
        conn = Q()
        conn.connector = 'OR'  # 通过or链接
        if search_value:
            for item in search_list:
                conn.children.append((item, search_value))  # conn.children.append('name__contains','张三')

        # 1.获取排序
        order_list = self.get_order_list()
        queryset = self.model_class.objects.filter(conn).order_by(*order_list)

        # 2.分页处理
        all_count = queryset.count()
        query_params = request.GET.copy()  # 深copy
        query_params._mutable = True  # query_params默认不可修改

        pager = Pagination(
            current_page=request.GET.get('page'),
            all_count=all_count,
            base_url=request.path_info,
            query_params=query_params,
            per_page_data=self.per_page_data,
        )
        data_list = queryset[pager.start:pager.end]

        # 3 处理表格
        list_display = self.get_list_display()  # 会优先调用UserInfoHandler里的get_list_display()方法。
        # 3.1 处理表格的表头
        header_list = []
        if list_display:
            for field_or_func in list_display:
                if isinstance(field_or_func, FunctionType):
                    verbose_name = field_or_func(self, obj=None, is_header=True)
                else:
                    verbose_name = self.model_class._meta.get_field(field_or_func).verbose_name
                header_list.append(verbose_name)
        else:
            header_list.append(self.model_class._meta.model_name)  # 如果用户没有填写list_display,就显示表名

        # 3.2 处理表的内容
        body_list = []
        for obj in data_list:
            tr_list = []
            if list_display:
                for field_or_func in list_display:
                    if isinstance(field_or_func, FunctionType):
                        tr_list.append(field_or_func(self, obj, is_header=False, *args, **kwargs))
                    else:
                        tr_list.append(getattr(obj, field_or_func))
            else:
                tr_list.append(obj)  # 如果用户没有填写list_display,就显示表对象,所以表类要定义__str__方法
            body_list.append(tr_list)

        # 4 添加按钮
        add_btn = self.get_add_btn(*args, **kwargs)

        context = {
            'data_list': data_list,
            'header_list': header_list,
            'body_list': body_list,
            'pager': pager,
            'add_btn': add_btn,
            'search_list': search_list,
            'search_value': search_value,
        }

        return render(request, self.list_template or 'stark/data_list.html', context)

    def save(self, form, is_update=False, *args, **kwargs):
        """
        在使用ModelForm保存数据之前预留的钩子方法
        :param form:
        :param is_update:
        :return:
        """
        form.save()

    def add_view(self, request, *args, **kwargs):
        """
        添加页面
        :param request:
        :return:
        """
        model_form_class = self.get_model_form_class(request, *args, **kwargs)
        if request.method == 'GET':
            form = model_form_class()
            return render(request, 'stark/change.html', {'form': form})
        form = model_form_class(data=request.POST)
        if form.is_valid():
            self.save(form, False, *args, **kwargs)
            # 在数据库保存成功后,跳转回列表页面(携带原来的参数)。
            return redirect(self.reverse_list_url(*args, **kwargs))
        return render(request, self.add_template or 'stark/change.html', {'form': form})

    def edit_view(self, request, pk, *args, **kwargs):
        """
        编辑页面
        :param request:
        :return:
        """
        current_edit_object = self.model_class.objects.filter(pk=pk).first()
        if not current_edit_object:
            return HttpResponse('要修改的数据不存在,请重新选择')
        model_form_class = self.get_model_form_class(request, *args, **kwargs)
        if request.method == 'GET':
            form = model_form_class(instance=current_edit_object)
            return render(request, 'stark/change.html', {'form': form})
        form = self.model_form_class(data=request.POST, instance=current_edit_object)
        if form.is_valid:
            self.save(form, True, *args, **kwargs)
            # 在数据库保存成功后,跳转回列表页面(携带原来的参数)
            return redirect(self.reverse_list_url(*args, **kwargs))
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk, *args, **kwargs):
        """
        删除页面
        :param request:
        :param pk:
        :return:
        """
        original_list_url = self.reverse_list_url(*args, **kwargs)
        if request.method == 'GET':
            return render(request, 'stark/delete.html', {'cancel': original_list_url})
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(original_list_url)

    def get_url_name(self, params):
        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, params)
        return '%s_%s_%s' % (app_label, model_name, params)

    @property
    def get_list_url_name(self):
        """
        获取列表页面URL的name
        :return:
        """
        return self.get_url_name('list')

    @property
    def get_add_url_name(self):
        """
        获取添加页面URL的name
        :return:
        """
        return self.get_url_name('add')

    @property
    def get_edit_url_name(self):
        """
        获取编辑页面URL的name
        :return:
        """
        return self.get_url_name('edit')

    @property
    def get_delete_url_name(self):
        """
        获取删除页面URL的name
        :return:
        """
        return self.get_url_name('delete')

    def reverse_common_url(self, name, *args, **kwargs):
        """
        生成带有原搜索条件的URL
        :param name: url
        :param args:
        :param kwargs:
        :return:
        """
        name = '%s:%s' % (self.site.namespace, name)
        base_url = reverse(name, args=args, kwargs=kwargs)
        if not self.request.GET:
            reverse_url = base_url
        else:
            params = self.request.GET.urlencode()
            new_query_dict = QueryDict(mutable=True)
            new_query_dict['_filter'] = params
            reverse_url = '%s?%s' % (base_url, new_query_dict.urlencode())
        return reverse_url

    def reverse_add_url(self, *args, **kwargs):
        """
        带有原搜索条件的增加URL
        :param args:
        :param kwargs:
        :return:
        """
        return self.reverse_common_url(self.get_add_url_name, *args, **kwargs)

    def reverse_edit_url(self, *args, **kwargs):
        """
        带有原搜索条件的编辑URL
        :param args:
        :param kwargs:
        :return:
        """
        return self.reverse_common_url(self.get_edit_url_name, *args, **kwargs)

    def reverse_delete_url(self, *args, **kwargs):
        """
        带有原搜索条件的删除URL
        :param args:
        :param kwargs:
        :return:
        """
        return self.reverse_common_url(self.get_delete_url_name, *args, **kwargs)

    def reverse_list_url(self, *args, **kwargs):
        name = '%s:%s' % (self.site.namespace, self.get_list_url_name,)
        base_url = reverse(name, args=args, kwargs=kwargs)
        params = self.request.GET.get('_filter')
        if not params:
            return base_url
        return '%s?%s' % (base_url, params)

    def wrapper(self, func):
        """
        当每一个request请求进来的时候,把request赋值给类的数据属性self.request
        :param func: request请求对应的视图函数
        :return:
        """

        @functools.wraps(func)  # 保留原函数的原信息,写装饰器建议写上这个。
        def inner(request, *args, **kwargs):
            self.request = request
            return func(request, *args, **kwargs)

        return inner

    def get_urls(self):
        patterns = [
            re_path(r'^list/$', self.wrapper(self.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'^edit/(\d+)/$', self.wrapper(self.edit_view), name=self.get_edit_url_name),
            re_path(r'^delete/(\d+)/$', self.wrapper(self.delete_view), name=self.get_delete_url_name),
        ]

        patterns.extend(self.extra_urls())
        return patterns

    def extra_urls(self):
        return []


class StarkSite(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: 是models中的数据库表对应的类。
        :param handler_class: 处理请求的视图函数所在的类
        :param prev: 生成URL的前缀
        :return:
        """

        if not handler_class:
            handler_class = StarkHandler
        self._registry.append(
            {'model_class': model_class, 'handler': handler_class(self, model_class, prev), 'prev': prev})

    def get_urls(self):
        patterns = []
        for item in self._registry:
            model_class = item['model_class']
            handler = item['handler']
            prev = item['prev']
            app_name, model_name = model_class._meta.app_label, model_class._meta.model_name
            if prev:
                patterns.append(
                    re_path(r'^%s/%s/%s/' % (app_name, model_name, prev,), (handler.get_urls(), None, None)))
            else:
                patterns.append(re_path(r'^%s/%s/' % (app_name, model_name,), (handler.get_urls(), None, None)))

        return patterns

    @property
    def urls(self):
        return self.get_urls(), self.app_name, self.namespace


site = StarkSite()

二、业务代码

from stark.service.core_func import site, StarkHandler, StarkModelForm, get_choice_text

from web import models


class UserInfoModelForm(StarkModelForm):
    class Meta:
        model = models.UserInfo
        fields = ['name', 'gender', 'classes', 'age', 'email']


class DepartmentHandler(StarkHandler):
    list_display = ['title']


class UserInfoHandler(StarkHandler):
    per_page_data = 5
    order_list = ['gender']
    model_form_class = UserInfoModelForm
    search_list = ['name__contains', 'email__contains', ]
    list_display = [
        'name',
        get_choice_text('性别', 'gender'),
        get_choice_text('班级', 'classes'),
        'age', 'email', 'department',
        StarkHandler.display_edit,
        StarkHandler.display_delete,
    ]

    def save(self, form, is_update=False, *args, **kwargs):
        form.instance.department_id = 1
        form.save()


site.register(models.Department, DepartmentHandler)  # 给部门的url增加了前缀:/stark/web/department/private/
site.register(models.UserInfo, UserInfoHandler)

三、模板

{% extends 'layout.html' %}

{% block css %}
    <link rel="stylesheet" href="">
{% endblock css %}


{% block content %}
    <div class="custom-container">
        {% if add_btn %}
            <div class="up-down-space left">
                {{ add_btn|safe }}
            </div>
        {% endif %}

        <!-- 搜索 -->
        {% if search_list %}
            <div class="up-down-space right">
                <form method="get" class="form-inline">
                    <div class="form-group">
                        <input class="form-control" type="text" name="q" value="{{ search_value }}"
                               placeholder="关键字搜搜">
                        <button class="btn btn-primary" type="submit">
                            <i class="fa fa-search" aria-hidden="true"></i>
                        </button>
                    </div>
                </form>
            </div>
        {% endif %}
        <!-- 搜索结束 -->

        <table class="table table-bordered">
            <thead>
            <tr>
                {% for item in header_list %}
                    <th>{{ item }}</th>
                {% endfor %}
            </tr>
            </thead>
            <tbody>
            {% for row in body_list %}
                <tr>
                    {% for ele in row %}
                        <td>{{ ele }}</td>
                    {% endfor %}
                </tr>
            {% endfor %}
            </tbody>
        </table>
        <nav>
            <ul class="pagination">
                {{ pager.page_html|safe }}
            </ul>
        </nav>
    </div>
{% endblock content %}

猜你喜欢

转载自www.cnblogs.com/lshedward/p/10589527.html