为查询结果添加分页
参考官方文档 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">« 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 »</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 }}">«</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 }}">»</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">« 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 »</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
网友评论