kingadmin後臺(2)、對象列表頁功能開發
阿新 • • 發佈:2018-08-11
自定義 stat () 計數 child render 圖片 object nth
目錄
- 頁面展示
- 對象列表
- 過濾功能
- 搜索功能
- action功能
- 排序
頁面展示
對象列表
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後臺(2)、對象列表頁功能開發