批量操作与搜索

编辑本博客

批量操作

在页面上展示下拉列表供用户选择,而且该下拉选项是可定制的

service\stark.py 

StarkConfig()类

class StarkConfig(object):
    action_list = []  # 存放下拉菜单项
    # 定义方法返回下拉列表,这里的self对应的是某个类,即可实现自定义
    def get_action_list(self):
        val = []
        val.extend(self.action_list)
        return val
    #返回下拉列表信息,这里key用函数名,用于后面判断请求是否合法
    def get_action_dict(self):
        val = {}
        for item in self.get_action_list():
            val[item.__name__] = item
        return val
    action_list = self.get_action_list()
    #将函数以字典列表的方式进行处理,以供模板渲染
    action_list = [{"name": func.__name__, 'text': func.text} for func in action_list]
    
    #列表展示视图
    def changelist_view(self, request):
    """
    所有URL的查看页面
    :param request:
    :return:
    """
    if request.method == 'POST':
        action_name = request.POST.get('action')
        if action_name not in self.get_action_dict():
            return HttpResponse("非法请求")
        # 这个response可以自定义返回内容
        responst = getattr(self, action_name)(request)
        if responst:
            return responst
    return render(request, 'stark/changelist.html',
                      {'header_list': header_list, 'data_list': data_list, 'addbtn': self.get_add_btn(),
                       'action_list': action_list,'search_list':search_list}, )
View Code

app01\stark.py中自定义下拉选项

class UserinfoConf(StarkConfig):
    #自定义下拉列表函数进行初始化数据
    def multi_init(self,request):
        return HttpResponse("自定义初始化")
    #这个text是下拉列表中显示的名称
    multi_init.text = '批量初始化'
    #StarkConfig.multi_delete使用的父类中批量删除方法,也可以自定义。
    action_list =[StarkConfig.multi_delete,multi_init]
View Code

注册自定义配置类

site.register(models.UserInfo,UserinfoConf)
View Code

自定义搜索字段

根据自定义搜索字段进行搜索展示,这里使用到django的Q查询

service\stark.py

class StarkConfig(object):
    search_list = []  # 搜索字段
    #获取搜索字段,子类可以对search_list重写,重写后这里获取到重写内容
    def get_search_list(self):
        """
        :return:搜索字段列表
        """
        val=[]
        val.extend(self.search_list)
        return val
    # 数据展示视图
    def changelist_view(self, request):
        """
        所有URL的查看页面
        :param request:
        :return:
        """
        # 处理搜索
        search_list = self.get_search_list()
        q = request.GET.get('q')#这里的q是前台input传过来的参数
        conn=Q()
        #查询通过“或”条件进行连接
        conn.connector='OR'
        if q:#前台页面有查询条件
            #通过append方法构建查询条件
            for field in search_list:
                conn.children.append(('%s__contains' % field,q))
        #将查询条件添加到filter中进行过滤
        query_set = self.model_class.objects.filter(conn).order_by(*self.get_order_by())
    return render(request, 'stark/changelist.html',
                      {'header_list': header_list, 'data_list': data_list, 'addbtn': self.get_add_btn(),
                       'action_list': action_list,'search_list':search_list}, )
View Code

app01/stark.py

class DepartmentConfig(StarkConfig):
    #这里自定义查询参数,注意外键字段需要用“__”来连接,这样就是外键字段__被连接表字段
    search_list = ['name', 'tel', 'user__name']
View Code

前台页面代码,展示具体数据以及数据操作按钮

{% extends 'stark/layout.html' %}
{% block content %}
    <div style="margin: 8px 0">
        {% if addbtn %}
            {{ addbtn }}
        {% endif %}
    </div>
    {% if search_list %}
        <div style="float: right;padding: 5px">
            <form action="" method="get" class="form-inline">
                <div class="form-group">
                    <input type="text" name="q" class="form-control">
                    <button type="submit" class="btn btn-default">
                        <i class="fa fa-search" aria-hidden="true"></i>
                    </button>
                </div>
            </form>
        </div>
    {% endif %}
    <form action="" class="form-inline" method="post">
        {% csrf_token %}
        {% if action_list %}
            <div class="form-group" style="padding: 5px;">
                <select name="action" class="form-control">
                    <option>功能选择</option>
                    {% for item in action_list %}
                        <option value="{{ item.name }}">{{ item.text }}</option>
                    {% endfor %}
                </select>
                <button type="submit" class="btn btn-default">执行</button>
            </div>
        {% endif %}

        <table class="table table-bordered">
            <thead>
            <tr>
                {% for header in header_list %}
                    <th>{{ header }}</th>
                {% endfor %}
            </tr>
            </thead>
            <tbody>
            {% for row in data_list %}
                <tr>
                    {% for col in row %}
                        <td>{{ col }}</td>
                    {% endfor %}
                </tr>
            {% endfor %}
            </tbody>
        </table>
    </form>
{% endblock %}
View Code

扩展搜索功能:URL跳转保留搜索条件

 关键点:

  1. reversion函数中修改url地址,给添加上请求条件,通过http.QueryDict
    def reverse_change_url(self, row):
        info = (self.site.namespace, self.model_class._meta.app_label, self.model_class._meta.model_name)
        edit_url = reverse('%s:%s_%s_change' % info, args=(row.pk,))
        if not self.request.GET:
            return edit_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict['_filter'] = param_str
        edit_url = '%s?%s' % (edit_url, new_query_dict.urlencode(),)
        return edit_url
    View Code
  2. 利用内部全局请求钩子函数,将请求进来的request保存到类中,在其他函数中就可以调用request
    # 自定义内部钩子装饰器,让所有url进来都走这个函数,就可以批量添加功能
    def wrapper(self, func):
        @wraps(func)  # 保留原函数信息
        def inner(request,*args, **kwargs):
            self.request=request
            return func(request,*args, **kwargs)
        return inner
    View Code

service\stark.py

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


class StarkConfig(object):
    order_by = []  # 子类中重写该静态属性即可改变排序方法,默认按pk升序排列
    list_display = []  # 类的静态字段提供需要展示的列
    model_form_class = None
    action_list = []  # 存放下拉菜单项
    search_list = []  # 搜索字段

    def __init__(self, model_class):
        self.model_class = model_class
        self.site = site
        self.request=None
        self.back_condition_key='_filter'
    # 批量删除方法,可自定义
    def multi_delete(self, request):
        """
        批量删除
        :param request:
        :return:
        """
        pk_list = request.POST.get('pk')
        try:
            self.model_class.objects.filter(pk__in=pk_list).delete()
            return HttpResponse("删除成功!")
        except Exception as e:
            return HttpResponse(e.msg)

    multi_delete.text = '批量删除'

    # 批量初始化方法,可自定义
    def multi_init(self, request):
        print('批量初始化')

    multi_init.text = '批量初始化'

    # 显示行头的checkbox
    def display_checkbox(self, row=None, header=False):
        if header:
            # 表头行
            return mark_safe("<input type='checkbox' name='pk'")
        else:
            # 数据行
            return mark_safe("<input type='checkbox' name='pk' value='%s'>" % row.pk)

    # 显示行末的编辑按钮
    def display_edit(self, row=None, header=False):
        if header:
            return "编辑"
        else:
            return mark_safe(
                "<a href='%s'><i class='fa fa-edit' aria-hidden='true'></i></a>" % self.reverse_change_url(row))

    # 显示行末删除按钮
    def display_del(self, row=None, header=False):
        if header:
            return "删除"
        else:
            return mark_safe(
                "<a href='%s'><i class='fa fa-trash-o' aria-hidden='true'></i></a>" % self.reverse_del_url(row))

    # 行末显示删除和编辑按钮
    def display_edit_del(self, row=None, header=False):
        if header:
            return "操作"
        else:
            tpl = "<a href='%s'><i class='fa fa-edit' aria-hidden='true'></i></a> | <a href='%s'><i class='fa fa-trash-o' aria-hidden='true'></i></a>" % (
                self.reverse_change_url(row), self.reverse_del_url(row))
            return mark_safe(tpl)

    # 获取添加按钮,可定制
    def get_add_btn(self):
        # 有添加权限,返回按钮代码
        if True:
            return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())
        else:
            return None

    # 获取排序列表,可定制
    def get_order_by(self):
        # 用该方法来预留钩子
        return self.order_by

    # 获取需要展示的字段列表,可定制
    def get_list_display(self):
        # 用该方法来预留钩子,后期可添加功能,如根据角色来定义展示的内容
        return self.list_display

    # 获取ModelFormClass,可定制
    def get_model_form_class(self):
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    # 创建类的方法,获取该类相关urls,这里不同的app进来对应的self就不一样
    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            re_path(r'^list/$', self.wrapper(self.list_view), name='%s_%s_list' % info),
            re_path(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
            re_path(r'^(?P<pk>\d+)/change/$', self.wrapper(self.change_view), name='%s_%s_change' % info),
            re_path(r'^(?P<pk>\d+)/del/$', self.wrapper(self.delete_view), name='%s_%s_del' % info),
        ]
        extra = self.extra_url()
        if extra:
            # 扩展urls
            urlpatterns.append(extra)
        return urlpatterns

    # 返回下拉列表
    def get_action_list(self):
        val = []
        val.extend(self.action_list)
        return val

    def get_action_dict(self):
        val = {}
        for item in self.get_action_list():
            val[item.__name__] = item
        return val

    #获取搜索字段
    def get_search_list(self):
        """
        :return:搜索字段列表
        """
        val=[]
        val.extend(self.search_list)
        return val

    #
    def get_search_condition(self,request):
        search_list = self.get_search_list()
        q = request.GET.get('q',"")
        conn = Q()
        conn.connector = 'OR'
        if q:
            for field in search_list:
                conn.children.append(('%s__contains' % field, q))
        return search_list,q,conn
    # 生成删除链接
    def reverse_del_url(self, row):
        info = (self.site.namespace, self.model_class._meta.app_label, self.model_class._meta.model_name)
        del_url = reverse('%s:%s_%s_del' % info, args=(row.pk,))
        if not self.request.GET:
            return del_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        del_url = '%s?%s' % (del_url, new_query_dict.urlencode(),)
        return del_url
    # 生成添加连接
    def reverse_add_url(self):
        info = (self.site.namespace, self.model_class._meta.app_label, self.model_class._meta.model_name)
        add_url= reverse("%s:%s_%s_add" % info)

        if not self.request.GET:
            return add_url
        param_str=self.request.GET.urlencode()
        new_query_dict=QueryDict(mutable=True)
        new_query_dict[self.back_condition_key]=param_str
        add_url='%s?%s' % (add_url,new_query_dict.urlencode(),)

        return add_url
    # 生成列表展示连接
    def reverse_list_url(self):
        info = (self.site.namespace, self.model_class._meta.app_label, self.model_class._meta.model_name)
        list_url = reverse("%s:%s_%s_list" % info)
        origin_condition=self.request.GET.get(self.back_condition_key)
        if not origin_condition:
            return list_url
        list_url="%s?%s" % (list_url,origin_condition)
        return list_url
    # 生成修改连接
    def reverse_change_url(self, row):
        info = (self.site.namespace, self.model_class._meta.app_label, self.model_class._meta.model_name)
        edit_url = reverse('%s:%s_%s_change' % info, args=(row.pk,))
        if not self.request.GET:
            return edit_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        edit_url = '%s?%s' % (edit_url, new_query_dict.urlencode(),)
        return edit_url
    # 数据展示视图
    def list_view(self, request):
        """
        所有URL的查看页面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            if action_name not in self.get_action_dict():
                return HttpResponse("非法请求")
            # 这个response可以自定义返回内容
            responst = getattr(self, action_name)(request)
            if responst:
                return responst

        list_display = self.get_list_display()
        header_list = []
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func, FunctionType):
                    verbose_name = name_or_func(self, header=True)
                else:
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name
                header_list.append(verbose_name)
        else:
            header_list.append(self.model_class._meta.model_name)
        # 处理搜索

        search_list,q,conn=self.get_search_condition(request)
        query_set = self.model_class.objects.filter(conn).order_by(*self.get_order_by())
        ####这里表格数据处理应该用yield和inclusion_tag重写###
        data_list = []
        for row in query_set:
            row_list = []
            if not list_display:  # 用户没有指定需要展示的字段
                row_list.append(row)
                data_list.append(row_list)
                continue
            for name_or_func in list_display:
                if isinstance(name_or_func, FunctionType):
                    # 行头展示
                    col = name_or_func(self, row=row)
                else:
                    col = getattr(row, name_or_func)
                row_list.append(col)
            data_list.append(row_list)

        ###下拉选项框####
        action_list = self.get_action_list()
        action_list = [{"name": func.__name__, 'text': func.text} for func in action_list]

        return render(request, 'stark/changelist.html',
                      {'header_list': header_list, 'data_list': data_list, 'addbtn': self.get_add_btn(),
                       'action_list': action_list,'search_list':search_list,'q':q}, )

    # 数据添加视图
    def add_view(self, request):
        """
        所有添加页面都在此处理
        :param request:
        :return:
        """
        if request.method == 'GET':
            form = self.get_model_form_class()()
            return render(request, 'stark/add.html', {'form': form})
        form = self.get_model_form_class()(request.POST)
        if form.is_valid():
            form.save()
            return redirect(self.reverse_list_url())

    # 数据修改视图
    def change_view(self, request, pk):
        """
        所有页面编辑函数
        :param request:
        :param pk:
        :return:
        """
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse("数据不存在")
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            form = ModelFormClass(instance=obj)
            return render(request, 'stark/change.html', {'form': form})
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    # 数据删除视图
    def delete_view(self, request, pk):
        """
        所有删除页面
        :param request:
        :param pk:
        :return:
        """
        if request.method == 'GET':
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        if request.method == 'POST':
            obj = self.model_class.objects.filter(pk=pk).delete()
            return redirect(self.reverse_list_url())

    # 自定义内部钩子装饰器,让所有url进来都走这个函数,就可以批量添加功能
    def wrapper(self, func):
        @wraps(func)  # 保留原函数信息
        def inner(request,*args, **kwargs):
            self.request=request
            return func(request,*args, **kwargs)
        return inner

    # 钩子函数,可以在子类中自定义该方法,对urls进行扩展
    def extra_url(self):
        pass

    @property  # 该装饰器使调用该方法时直接用函数名即可,无需加括号
    def urls(self):
        return self.get_urls()


# 自定义注册类
class AdminSite(object):
    def __init__(self):
        self._registry = {}  # 用于存储注册进来的类,key是类,值是StarkConfig或自定义的StarkConfig的子类
        self.app_name = 'stark'
        self.namespace = 'stark'

    # 函数注册方法
    def register(self, models_class, stark_config=None):
        if not stark_config:
            stark_config = StarkConfig
        self._registry[models_class] = stark_config(models_class)

    # 动态创建urls
    def get_urls(self):
        urlpatterns = []
        for k, v in self._registry.items():
            # 对注册的类进行循环,创建urls
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            # v.urls是StarkConfig或自定义的StarkConfig的子类的urls,对路由进行再分发
            urlpatterns.append(re_path(r"^%s/%s/" % (app_label, model_name,), (v.urls, None, None)))
        return urlpatterns

    @property
    def urls(self):
        # 返回一个元祖,这里返回内容就和include()方法返回的内容一样
        return self.get_urls(), self.app_name, self.namespace


# 实例化注册类,需要注册的时候,导入该实例对象即可,大家操作的都是同一个实例对象
site = AdminSite()
View Code

app01\stark.py

from app01 import models
from django.urls import re_path
from django.shortcuts import HttpResponse,render,redirect
from stark.service.stark import site,StarkConfig

def test(request):
    return HttpResponse("test page")

class UserinfoConf(StarkConfig):
    order_by = ['-id']#按ID倒叙排列
    list_display = [StarkConfig.display_checkbox,'id','name','password','email',StarkConfig.display_edit_del]
    search_list = []
    #批量操作自定义
    def multi_init(self,request):
        return HttpResponse("自定义初始化")

    multi_init.text = '批量初始化'
    action_list =[StarkConfig.multi_delete,multi_init]
    #扩展URL
    def extra_url(self):
        return re_path(r'test/',test)

class DepartmentConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox,'id','name','tel','user',StarkConfig.display_edit_del]
    search_list = ['name', 'tel', 'user__name']
    action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
site.register(models.UserInfo,UserinfoConf)
site.register(models.Department,DepartmentConfig)
View Code

 

posted @ 2018-09-11 08:54  丫丫625202  阅读(119)  评论(0编辑  收藏  举报