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后台对象列表页功能开发的主要内容,如果未能解决你的问题,请参考以下文章

5-crm项目-kingadmin,列表页---过滤

4-crm项目-kingadmin,列表页---表头和数据

kingadmin后台新增页面开发

kingadmin后台主页面开发

CRM项目实战-kingadmin对象删除功能开发

kingadmin后台怎么使用kingadmin模块