美文网首页Python Web开发学习
【CRM客户关系管理】11.为查询结果添加分页和排序功能

【CRM客户关系管理】11.为查询结果添加分页和排序功能

作者: 吾星喵 | 来源:发表于2018-12-04 09:35 被阅读0次

    为查询结果添加分页

    参考官方文档 https://docs.djangoproject.com/zh-hans/2.1/topics/pagination/

    添加分页

    from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
    
    
    @login_required
    def table_detail(request, app_name, model_name):
        """取出指定model里的数据返回到前端"""
        # 拿到admin_class后,通过它获取model
        admin_class = site.enable_admins[app_name][model_name]
        # print(admin_class)  # 执行djadmin.py定义的注册模型类
        queryset = admin_class.model.objects.all()
        # print(queryset)
    
        # 进行过滤
        queryset, filter_conditions = get_filter_result(request, queryset)
        # 将过滤字典保存到全局注册类中
        admin_class.filter_conditions = filter_conditions
    
        # 查询集结果分页
        paginator = Paginator(queryset, 1)  # Show 10 contacts per page
        page = request.GET.get('page')
        try:
            queryset = paginator.get_page(page)
        except PageNotAnInteger:
            queryset = paginator.get_page(1)
        except EmptyPage:
            queryset = paginator.get_page(paginator.num_pages)
    
        return render(request, 'djadmin/table_detail.html', locals())
    

    这时候访问 http://127.0.0.1:8000/djadmin/crm/customerinfo/ 就只会显示一个结果了

    image.png

    理论上访问 http://127.0.0.1:8000/djadmin/crm/customerinfo/?page=2 就会显示第二页,但实际报错

    image.png

    分页报错排除筛选条件

    因为后台在处理page=2时将这个值当作get_filter_result(request, queryset)的过滤条件了,需要排除键是page的结果

    def get_filter_result(request, queryset):
        """获取过滤的字段,并返回过滤后的查询集和过滤的字典"""
        filter_conditions = {}
        # 获取过滤的字段
        for k, v in request.GET.items():
            # 分页将不计入筛选值
            if k == 'page':
                continue
            if v:  # 所选的值不会空是保存到字典中
                filter_conditions[k] = v
        return queryset.filter(**filter_conditions), filter_conditions
    

    现在访问第二页 http://127.0.0.1:8000/djadmin/crm/customerinfo/?page=2 就可以得到正常的结果

    image.png

    分页按钮功能

    使用Django自带的分页,将以下代码添加到table_detail.html模板最下方

        <div class="pagination">
            <span class="step-links">
                {% if queryset.has_previous %}
                    <a href="?page=1">&laquo; first</a>
                    <a href="?page={{ queryset.previous_page_number }}">previous</a>
                {% endif %}
    
                <span class="current">
                    Page {{ queryset.number }} of {{ queryset.paginator.num_pages }}.
                </span>
    
                {% if queryset.has_next %}
                    <a href="?page={{ queryset.next_page_number }}">next</a>
                    <a href="?page={{ queryset.paginator.num_pages }}">last &raquo;</a>
                {% endif %}
            </span>
        </div>
    
    image.png

    使用bootstrap分页按钮

    在项目templates目录下,创建includes文件夹,然后创建pagination.html文件

    <ul class="pagination pagination-sm m-0 float-right">
        {% if page.number > 1 %}
            <li class="page-item"><a class="page-link" href="?page=1" aria-label="">首页</a></li>
        {% endif %}
    
        {% if page.has_previous %}
            <li class="page-item"><a class="page-link" href="?page={{ page.previous_page_number }}">&laquo;</a></li>
        {% endif %}
    
        {%  for pg in page.paginator.page_range %}
            {% if page.number == pg %}
                <li class="page-item active"><a class="page-link" href="?page={{ pg }}">{{ pg }}</a></li>
            {% elif pg > page.number|add:'-3' and pg < page.number|add:'3' %}
                <li class="page-item"><a class="page-link" href="?page={{ pg }}">{{ pg }}</a></li>
            {% endif %}
        {% endfor %}
    
        {% if page.has_next %}
                <li class="page-item"><a class="page-link" href="?page={{ page.next_page_number }}">&raquo;</a></li>
        {% endif %}
    
        {% if page.number != page.paginator.num_pages %}
            <li class="page-item"><a class="page-link" href="?page={{ page.paginator.num_pages }}" aria-label="">尾页</a></li>
        {% endif %}
    </ul>
    

    在详情中包含分页模块,下方为自带的分页,上部为bootstrap分页模块

    <div>
        {% include 'includes/pagination.html' with page=queryset %}
    </div>
    
    <div class="pagination">
        <span class="step-links">
            {% if queryset.has_previous %}
                <a href="?page=1">&laquo; first</a>
                <a href="?page={{ queryset.previous_page_number }}">previous</a>
            {% endif %}
    
            <span class="current">
                Page {{ queryset.number }} of {{ queryset.paginator.num_pages }}.
            </span>
    
            {% if queryset.has_next %}
                <a href="?page={{ queryset.next_page_number }}">next</a>
                <a href="?page={{ queryset.paginator.num_pages }}">last &raquo;</a>
            {% endif %}
        </span>
    </div>
    
    image.png

    测完完成后就可以在视图中把每页数量设置为10条了。

    排序功能

    处理排序的函数

    在djadmin应用下的视图中,增加处理排序功能的函数get_order_result(request, queryset, admin_class)

    def get_order_result(request, queryset, admin_class):
        """获取request中的排序字段,然后返回排序后的结果"""
        current_order_field = dict()
        # 通过获取前端传过来的排序的索引字符串
        order_value = request.GET.get('_order')  # _order的值为list_display列表的索引:-0, 0, 1, -3等
        if order_value:
            # 通过索引找到要排序的字段,因为索引可能是正数或负数,所以要用绝对值
            order_field = admin_class.list_display[abs(int(order_value))]
            # 记录当前的排序字段,以list_display列表的值为键,request的值为值保存到字典中
            current_order_field[order_field] = order_value
            # 判断是正序或者是倒序,如果是倒序,需要添加负号
            if order_value.startswith('-'):
                order_field = '-' + order_field
            return queryset.order_by(order_field), current_order_field
        else:
            return queryset, current_order_field
    

    详情视图调用排序函数

    修改详情视图table_detail(request, app_name, model_name),在过滤和分页的中间增加排序调用

    @login_required
    def table_detail(request, app_name, model_name):
        """取出指定model里的数据返回到前端"""
        # 拿到admin_class后,通过它获取model
        admin_class = site.enable_admins[app_name][model_name]
        # print(admin_class)  # 执行djadmin.py定义的注册模型类
        queryset = admin_class.model.objects.all()
        # print(queryset)
    
        # 进行过滤
        queryset, filter_conditions = get_filter_result(request, queryset)
        # 将过滤字典保存到全局注册类中
        admin_class.filter_conditions = filter_conditions
    
        # 排序,返回排序的结果和排序的字段字典
        queryset, current_order_field = get_order_result(request,queryset, admin_class)
    
        # 查询集结果分页
        paginator = Paginator(queryset, 10)  # Show 10 contacts per page
        page = request.GET.get('page')
        try:
            queryset = paginator.get_page(page)
        except PageNotAnInteger:
            queryset = paginator.get_page(1)
        except EmptyPage:
            queryset = paginator.get_page(paginator.num_pages)
    
        return render(request, 'djadmin/table_detail.html', locals())
    

    排序报错排除筛选条件

    修改djadmin应用视图的函数get_filter_result(request, queryset),排除排序字段,这儿就以如果键以_开头,则不进行筛选。

    def get_filter_result(request, queryset):
        """获取过滤的字段,并返回过滤后的查询集和过滤的字典"""
        filter_conditions = {}
        # 获取过滤的字段
        for k, v in request.GET.items():
            # 分页将不计入筛选值
            if k == 'page' or k.startswith('_'):
                continue
            if v:  # 所选的值不会空是保存到字典中
                filter_conditions[k] = v
        return queryset.filter(**filter_conditions), filter_conditions
    

    这时候如果访问 http://127.0.0.1:8000/djadmin/crm/customerinfo/?_order=5 就会按客户状态进行排序

    image.png

    访问 http://127.0.0.1:8000/djadmin/crm/customerinfo/?_order=-5 按客户状态倒序

    image.png

    设计排序点击链接

    观察模板标签中的build_table_head_name(admin_class)函数,假如我们要实现点击表格头部进行排序,实现起来不太方便

    # 显示模型表的中文名称
    @register.simple_tag
    def build_table_head_name(admin_class):
        th = ''
        if admin_class.list_display:
            for display_field in admin_class.list_display:
                # 获取列中的字段对象
                display_field_obj = admin_class.model._meta.get_field(display_field)
                # print(display_field_obj.verbose_name)
                tmp = "<th>{}</th>".format(display_field_obj.verbose_name)
                th += tmp
        else:
            th += "<th>{}</th>".format(admin_class.model._meta.verbose_name)  # 如果没有自定义注册,则表格标题就显示模型的别名verbose_name
        return mark_safe(th)
    

    所以修改表格的头部,暂时在模板中处理显示表头的功能

    <thead>
    <tr>
        {% if admin_class.list_display %}
            {% for display_field in admin_class.list_display %}
                <th><a href="?_order={% get_sorted_data display_field current_order_field forloop.counter0 %}">{{ display_field }} <span class="glyphicon glyphicon-triangle-bottom" aria-hidden="true"></span></a> </th>
            {% endfor %}
        {% else %}
            {% build_table_head_name admin_class %}
        {% endif %}
        <!--
        {% build_table_head_name admin_class %}
        -->
    </tr>
    </thead>
    

    字体图标: https://v3.bootcss.com/components/

    排序样式如下

    image.png

    下面来处理这些箭头的链接功能:实现只显示当前排序字段的箭头,以及根据正倒序显示不同的箭头方向。

    处理箭头链接功能

    在djadmin应用的模板标签templatetags\djadmin_tags.py中,新建get_sorted_data(display_field, current_order_field, forloop_counter)函数,用于返回排序的链接和图标样式.

    current_order_field为当前排序的字典,字典键为list_display的值,字典值为list_display的索引正负值,用于判定正倒序

    @register.simple_tag
    def get_sorted_data(display_field, current_order_field, forloop_counter):
        print(current_order_field)  # {'status': '-5'}假如是按客户状态(列表索引为5)
        print(display_field)  # 遍历显示list_display列表值
        if display_field in current_order_field.keys():
            # 得到当前排序的字段
            if current_order_field[display_field].startswith('-'):
                order_data = current_order_field[display_field].replace('-', '')  # 去掉倒序
            else:
                order_data = '-' + current_order_field[display_field]
            return order_data
        return forloop_counter  # 如果不是当前排序,直接默认正序,也就是list_display的索引值
    

    现在点击表头的字段都可以按照该字段进行排序了

    image.png

    排序页面显示优化

    <span class="glyphicon glyphicon-triangle-bottom" aria-hidden="true"></span>

    如何实现上下箭头切换以及可以不显示箭头

    在模板标签中再增加一个函数,这个函数逻辑上和get_sorted_data函数差不多

    @register.simple_tag
    def get_sorted_arrow(display_field, current_order_field, forloop_counter):
        if display_field in current_order_field.keys():
            if current_order_field[display_field].startswith('-'):
                arrow_direction = 'bottom'
            else:
                arrow_direction = 'top'
            span = '<span class="glyphicon glyphicon-triangle-{}" aria-hidden="true"></span>'.format(arrow_direction)
            return mark_safe(span)
        return ''
    

    然后修改table_detail.html模板表头元素

    <thead>
    <tr>
        {% if admin_class.list_display %}
            {% for display_field in admin_class.list_display %}
                <th>
                    <a href="?_order={% get_sorted_data display_field current_order_field forloop.counter0 %}">
                        {{ display_field }} {% get_sorted_arrow display_field current_order_field forloop.counter0 %}
                    </a>
                </th>
            {% endfor %}
        {% else %}
            {% build_table_head_name admin_class %}
        {% endif %}
        <!--
        {% build_table_head_name admin_class %}
        -->
    </tr>
    </thead>
    
    image.png image.png

    相关文章

      网友评论

        本文标题:【CRM客户关系管理】11.为查询结果添加分页和排序功能

        本文链接:https://www.haomeiwen.com/subject/bfopcqtx.html