kingadmin后台对象列表页功能开发
Posted fqh202
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kingadmin后台对象列表页功能开发相关的知识,希望对你有一定的参考价值。
目录
页面展示
对象列表
urls.py
from django.conf.urls import url
from king_admin import views
urlpatterns = [
# model对象列表页
url(r'^(w+)/(w+)/$', views.model_obj_list, name='model_obj_list')
]
views.py
from django.shortcuts import render, redirect
def model_obj_list(request, app_name, model_name):
"""生成对象列表页"""
if not request.user.is_authenticated:
return render(request, 'kingadmin/login.html')
# 取出当前的 admin_class 和 model 表对象
admin_class = site.enable_admins[app_name][model_name]
model_class = admin_class.model
from pure_pagination import Paginator, EmptyPage, PageNotAnInteger
try:
# 获取页码数
page = request.GET.get('page', 1)
except PageNotAnInteger:
page = 1
p = Paginator(query_set, request=request, per_page=10)
contacts = p.page(page)
return render(request, 'kingadmin/table_objects_list.html', locals())
table_objects_list.html
<table class="table table-striped">
<!--标题栏-->
<thead>
<tr>
<th width="50px"><input type="checkbox" id="action-toggle"></th>
<!-- 判断是否定制list_display -->
{% if admin_class.list_display %}
<!--循环遍历每个显示显示列名称-->
{% for field_name in admin_class.list_display %}
<th>
{{ field_name }}
</th>
{% endfor %}
{% else %}
<th>{{ model_name }}</th>
{% endif %}
</tr>
</thead>
<tbody>
<!--循环遍历记录对象-->
{% for obj in contacts.object_list %}
{% get_obj_rows obj admin_class %}
{% endfor %}
</tbody>
</table>
get_obj_rows.py
自定义标签渲染每行记录。
from django.template import Library
from django.utils.safestring import mark_safe
register = Library()
@register.simple_tag
def get_obj_rows(obj, admin_class):
"""根据传入的对象生成一行记录"""
ele = ''
if admin_class.list_display:
# 1、生成第一列的单选框,设置value为对象id,批量删除使用
ele += '<th><input type="checkbox" name="_selected_action" value="%s"></th>' % obj.id
for index, column_name in enumerate(admin_class.list_display):
# 2、根据设定的字段名从model中取出字段对象
column_obj = admin_class.model._meta.get_field(column_name) # 获取字段对象
# 3、取出当前列的显示信息
if column_obj.choices:
# 判断当前字段是否有choices选项,取出显示信息
column_data = getattr(obj, 'get_%s_display' % column_name)()
else:
column_data = getattr(obj, column_name)
# 4、首个字段列设置为超链接格式
if index == 0:
td_ele = "<td><a href='%d/change'>%s</a></td>" % (obj.id, column_data)
else:
td_ele = "<td>%s</td>" % column_data
ele += td_ele
# 5、生成当前对象的一行记录
ele = "<tr>%s</tr>" % ele
else:
# 2、若没有指定list_display,则默认显示对象str
ele = "<tr><td><input type='checkbox' name='_selected_action' " "value='%s'></th><td><a href='%s/change'>%s</a></td></tr>" % (obj.id, obj.id, obj)
return mark_safe(ele)
过滤功能
index.html
<!--自定义过滤器-->
<form id="filter_box">
<!--循环遍历自定义的过滤字段名称-->
{% for field_name in admin_class.list_filter %}
<!--先当前字段的名称-->
<span style="">{% get_label_name admin_class field_name %}</span>
<!--生成当前字段的select标签-->
{% bulid_filter_ele field_name admin_class %}
{% endfor %}
<input type="submit" value="过滤" class="btn btn-info button_control">
</form>
自定义标签
from django.template import Library
from django.utils.safestring import mark_safe
register = Library()
@register.simple_tag
def get_label_name(admin_class,field_name):
"""获取字段名称"""
column_obj = admin_class.model._meta.get_field(field_name)
return column_obj.verbose_name
@register.simple_tag
def bulid_filter_ele(field_name, admin_class):
"""根据传入的字段名生成过滤标签"""
# 1、
filter_ele = '<select name=%s class="form-control input_control" ' 'style="width:100px;display:inline;margin-right:10px">' % field_name
# 2、根据字段名取出当前字段对象
column_obj = admin_class.model._meta.get_field(field_name)
# 3、拼接options标签
try:
for choice in column_obj.get_choices():
# 若但字段没有choices选项会直接报错
selected = ''
# 3.1、筛选已过滤的字段,点击过滤后必须保存之前的过滤字段
if field_name in admin_class.filter_conditions: # 当前字段被过滤了
if str(choice[0]) == admin_class.filter_conditions.get(field_name): # 当前值被选中了
selected = 'selected'
# 3.2、若没有过滤内容,则显示为----
option = "<option value='%s' %s>%s</option>" % (choice[0], selected, choice[1])
filter_ele += option
except AttributeError as e:
# 关键点是这是name='%s__get'表示大于或等于
filter_ele = "<select name='%s__gte' class='form-control' " "style='width:100px;display:inline;margin-top:20px;margin-button:20px'>" % field_name
# 判断是否是日期字段
if column_obj.get_internal_type() in ('DateField', 'DateTimeField'):
# 自定义日期选项
import datetime
time_obj = datetime.datetime.today()
time_list = [
['', '-----------------'],
[time_obj, '今天'],
[time_obj - datetime.timedelta(7), '七天内'],
[time_obj.replace(day=1), '本月'],
[time_obj - datetime.timedelta(90), '三个月内'],
[time_obj.replace(month=1, day=1), '一年内'],
['', '所有'],
]
for i in time_list:
selected = ''
# 生成 value="2018-7-23"
time_to_str = '' if not i[0] else "%s-%s-%s" % (i[0].year, i[0].month, i[0].day)
if "%s__gte" % field_name in admin_class.filter_conditions: # 当前字段被过滤了
print('-------------gte')
if time_to_str == admin_class.filter_conditions.get("%s__gte" % field_name): # 当前值被选中了
selected = 'selected'
option = "<option value='%s' %s>%s</option>" % (time_to_str, selected, i[1])
filter_ele += option
filter_ele += '</select>'
return mark_safe(filter_ele)
urls.py
select标签在form表单中提交默认以GET方式提交,且携带选中的options。
/kingadmin/crm/customerinfo/?contact_type=0&consultant=2&status=&source=0&date__gte=
views.py
def model_obj_list(request, app_name, model_name):
"""生成对象列表页"""
if not request.user.is_authenticated:
return render(request, 'kingadmin/login.html')
admin_class = site.enable_admins[app_name][model_name]
model_class = admin_class.model
# 1、取出所有对象集合
query_set = model_class.objects.all()
filter_conditions = {}
# 2、取出url中携带的参数
for key, val in request.GET.items():
# 3、排除搜索、排序和分页等,只取过滤字段
if val and key not in ('page', 'order', 'search'):
# 4、生成过滤条件
filter_conditions[key] = val
# 5、开始过滤对象
if filter_conditions:
query_set = query_set.filter(**filter_conditions)
admin_class.filter_conditions = filter_conditions
....
搜索功能
前端
<form id="search_box">
<input type="search" name="search" class="form-control input_control"
value="{% show_search_str admin_class search_str %}" id="search_input">
<input type="submit" value="搜索" style="display: inline" class="btn btn-success button_control">
</form>
自定义标签
生成可搜索字段信息
@register.simple_tag
def show_search_str(admin_class, search_str):
"""显示可以搜索的字段"""
str = ''
if not search_str:
# 上一次进入后台没有进行搜索操作
search_fields = admin_class.search_fields
for f in search_fields:
str += f + ', '
return str
return search_str # 返回上一次搜索的数据
views.py
逻辑位于过滤功能后,因为要实现在过滤的基础上进行搜索功能。
def model_obj_list(request, app_name, model_name):
"""生成对象列表页"""
if not request.user.is_authenticated:
return render(request, 'kingadmin/login.html')
admin_class = site.enable_admins[app_name][model_name]
model_class = admin_class.model
...
# 1、获取用户输入的要搜索的字符串
search_str = request.GET.get('search')
if search_str:
search_fields = admin_class.search_fields # ['name','consultant__username']
from django.db.models import Q
# 2、创建q对象
q = Q()
# 3、增加连接符
q.connector = 'OR'
# 4、拼接q搜索条件
for field_name in search_fields:
q.children.append(('%s__contains' % field_name, search_str))
# 5、开始过滤并取出结果
query_set = query_set.filter(q)
...
action功能
前端
此处表单指定了post提交方式。
<form method="post" id="changelist-form" novalidate onsubmit="collect_id_befor_submit(this)">
<!--1、要提交的action功能-->
<select class="form-control" style="width: 200px;display: inline;height: 30px;
line-height: 30px;margin-top: 20px" name="action">
<option value="">-----------</option>
<!--取出admin_class中的actions列表信息-->
{% for action in admin_class.actions %}
<option value="{{ action }}">{{ action }}</option>
{% endfor %}
</select>
<!--2、选中的id列表,通过js动态赋值-->
<input hidden name="obj_id_list" id="obj_id_list">
<button type="submit" class="btn btn-danger button_control">Go</button>
{% csrf_token %}
</form>
<script>
$('#search_input').click(function () {
$('#search_input').val('')
});
// 1、监听标题栏的checkbox的click事件
$('.table').on('click', '#action-toggle', function () {
// 2、判断属性是否被选中
if ($(this).prop('checked')) {
// 3、若标题的chekbox被选中,则执行以下操作
$('[name=_selected_action]').prop('checked', true)
}
else {
// 3.1、反之
$('[name=_selected_action]').prop('checked', false)
}
});
// 提取选中的对象的id列表
function collect_id_befor_submit(data) {
var obj_id_lst = [];
// 4、过滤出选中的checkbox
checked_input_lst = $('[name=_selected_action]').filter(':checked');
// 5、拼接删除对象的id列表
$.each(checked_input_lst, function (i, ele) {
obj_id_lst.push(ele.value); // push方法
console.log(ele.value)
});
// 6、js动态将id列表赋值到要提交的标签中
$('#obj_id_list').val(JSON.stringify(obj_id_lst))
}
</script>
BaseKingAdmin
# kingadmin.py
from king_admin.base_king_admin import BaseKingAdmin
class CustomerInfoAdmin(BaseKingAdmin):
"""默认继承BaseKingAdmin"""
def __init__(self):
"""此处加入默认的action的删除功能"""
self.actions.extend(self.default_actions)
list_display = ['name','contact_type', 'status', 'source','consultant','date']
list_filter = ['contact_type','consultant','status','source','date']
search_fields = ['name','consultant__username']
filter_horizontal = ['consult_course']
若注册没有指定自定义的admin类,那么会默认使用BaseKingAdmin
类。
# kingadmin/base_king_admin.py
class BaseKingAdmin(object):
list_display = []
list_filter = []
search_fields=[]
readonly_fields=[] # 只读字段
filter_horizontal =[] #
default_actions = ['delete_selected'] # 默认action下拉框
actions=[]
def delete_selected(self,request,query_set):
"""删除选中的对象set"""
query_set.delete()
批量删除
views.py
def model_obj_list(request, app_name, model_name):
"""生成对象列表页"""
if not request.user.is_authenticated:
return render(request, 'kingadmin/login.html')
admin_class = site.enable_admins[app_name][model_name]
model_class = admin_class.model
# 自定义过滤
if request.method == "POST":
# 1、取出action名称
action = request.POST.get('action')
# 2、获取对象id列表
select_id_lst = json.loads(request.POST.get('obj_id_list'))
if select_id_lst:
# 根据id筛选出对应的对象,转换成queryset对象,方便后续操作
obj_lst = model_class.objects.filter(id__in=[int(i) for i in select_id_lst ])
# 3、获取对应的action函数
action_func = getattr(admin_class,action)
# 4、调用action函数
action_func(request,obj_lst)
# 取出所有对象集合
query_set = model_class.objects.all()
排序
功能:
- 1、点击标题栏则降序排列
- 2、二次点击则升序
- 3、第三次以再降序
- 以此类推
前端页面
要考虑三个功能:
- 1、判断当前字段是否是之前的排序字段,若是则取反;
- 2、
- 3、箭头更换
<!-- 判断是否定制list_display -->
{% if admin_class.list_display %}
<!--1、循环遍历每个显示列的名称-->
{% for field_name in admin_class.list_display %}
<th>
<!--2、点击当前字段排序功能-->
<a href="?order={% get_sorted_column order_column forloop.counter field_name %}
{% get_filter_args admin_class %}">{{ field_name }}<a>
{% arrow field_name order_column %}<!--排序箭头指示-->
</th>
{% endfor %}
{% else %}
<th>{{ model_name }}</th>
{% endif %}
views.py
逻辑写在在过滤功能之后。
def model_obj_list(request, app_name, model_name):
"""生成对象列表页"""
if not request.user.is_authenticated:
return render(request, 'kingadmin/login.html')
admin_class = site.enable_admins[app_name][model_name]
model_class = admin_class.model
...
order_column = {}
# 1、获取排序数据
order_index = request.GET.get('order')
if order_index:
# 2、取出排序的字段名
order_field = admin_class.list_display[abs(int(order_index)) - 1] # 取出排序的字段名
# 3、判断order_index的正负,其余交给前端处理!
if str(order_index).startswith('-'):
# 带负号则降序
query_set = query_set.order_by('-' + order_field)
else:
query_set = query_set.order_by(order_field)
order_column[order_field] = order_index # {name:1}
自定义标签
1、确定order值,判断当前字段是否是之前的排序字段,若是则取反;若之前没有排序,则返回循环计数。
@register.simple_tag
def get_sorted_column(order_column, counter, field_name):
"""
:param order_column: 传入之前的排序数据,例如 {field:index+1}
:param counter:循环计数,也是当前field在list_display中的位置+1
:param field_name:字段名
:return:
"""
# 1、判断加载页面前是否有排序
if field_name in order_column:
# 2、取出 索引信息
last_sort_index = order_column[field_name]
# 3、判断索引信息的正负号,并取反
if str(last_sort_index).startswith('-'):
this_time_sort_index = str(last_sort_index).strip('-')
else:
this_time_sort_index = '-%s' % last_sort_index
return this_time_sort_index
else:
# 2、没有排序则返回循环计数,就是order的值
return counter
2、考虑到在过滤的基础上进行排序:
@register.simple_tag
def get_filter_args(admin_class):
"""
:param admin_class:
:return:
"""
# 1、从admin_class中取出过滤字段信息
if admin_class and admin_class.filter_conditions:
ele = ''
# 2、将过滤字段也拼接到url中,否则排序后后端就直接将之前的过滤信息直接清空了
for k, v in admin_class.filter_conditions.items():
ele += '&%s=%s' % (k, v)
print(ele)
return mark_safe(ele)
return ''
3、根据排序情况动态改变图形
@register.simple_tag
def arrow(field_name, order_column):
"""
:param field_name: 当前字段
:param order_column: 排序字典
:return:
"""
symbol = ''
# 1、判断当前字段是否有排序
if field_name in order_column:
# 2、根据排序值的正负号 赋值不同的 图形 的class属性
if str(order_column[field_name]).startswith('-'):
symbol = 'glyphicon glyphicon-arrow-down'
else:
symbol = 'glyphicon glyphicon-arrow-up'
# 3、完成标签并返回到前端
arrow_str = '<span class="%s"></span>' % symbol
return mark_safe(arrow_str)
以上是关于kingadmin后台对象列表页功能开发的主要内容,如果未能解决你的问题,请参考以下文章