CRM项目(五)

文章目录

1. 添加页面统计功能

2. 添加搜索功能

  2.1. 原生admin搜索功能分析

  2.2. 添加搜索字段

  2.3. 编写搜索功能函数

  2.4. 编写视图函数

  2.5. 编写模板文件

  2.6. BUG解决

3. 添加排序功能

  3.1. 原生admin排序功能分析

  3.2. 编写排序功能函数

  3.3. 编写视图函数

  3.4. 编写模板文件

  3.5. BUG修改

4. 添加复选框

  4.1. 添加checkbox标签

  4.2. 添加事件和value

CRM项目开发(五)

 

简单基础的功能添加是比较漫长的道路,前面添加了分页和过滤功能,接下来添加的仍然会是一些琐碎而又常用的功能。

 

 

 

 

添加页面统计功能

这个功能实在是太简单了,只需要一行代码就能够搞定,当然是在这使用Django的情况,其他框架虽然没有使用过,但应该一行代码也能搞定,可以通过直接数据库查询将统计结果返回给模板文件进行渲染。我们在这里使用的方法很简单,不要更改其他任何文件的代码,只需要在table_objs.html文件中添加如下内容(位置自己定):

...
             </table>
              {# 添加下面的一行代码即可! #}
              <p>数量统计:<mark style="margin: auto 5px">{{ query_set.paginator.count }}</mark></p>
              <nav>
              {#分页处理#}
...

显示效果如下:

添加搜索功能

原生admin搜索功能分析

回来看看起初我们在admin.py中添加的功能字段:

...
#自定义操作
class CustomerAdmin(admin.ModelAdmin):
    list_display = ('name', 'id','qq','source','consultant','content','status','date')
    list_filter = ('source','consultant','date')
    #搜索功能,并指定搜索字段
    search_fields = ('qq','name')
    raw_id_fields = ('consult_course',)
    filter_horizontal = ('tags',)
    list_editable = ('status',)
...

添加了这么多的功能,大家应该早就发现了吧!Django为我们提供的套路都是一样的,我们进行模仿重构也是同样的套路。在此基础上,我们还可以进一步的扩展!

添加搜索字段

在我们自己的king_admin中添加,首先要在基类中添加,然后在独立的自定义子类中添加,具体如下:

from CRM import models
#创建基类
class BaseAdmin(object):
    list_display = []
    list_filter = []
    search_fields = []
    list_per_page = 10
...
#自定义类,显示特定字段
class CustomerAdmin(BaseAdmin):
    list_display = ['qq','name','source','consultant','consult_course','date','status']
    list_filters = ['source','consultant','consult_course','status']
    #添加如下代码,搜索字段自选
    search_fields = ['qq', 'name', 'consultant__name']
    list_per_page = 2
    #model = models.Customer
...

这里的consultant__name考虑到了跨表的问题,先这样写,后面在优化的时候集中处理!

编写搜索功能函数

这里,我们还要考虑一个问题:前面我们添加了过滤、分页、统计功能,那么搜索功能是该基于这些功能呢? 很显然,是必须的。也就是说,当我们将数据过滤后得到的结果,在通过搜索查询到所需的具体数据。

utils.py文件中,过滤功能函数下面继续编写搜索功能函数:

from django.db.models import Q
...
#-----------------------搜索功能-----------------------------------
def table_search(request,admin_class,object_list):
    """
    :param request: 封装的请求体
    :param admin_class: 自定义类
    :param object_list: 过滤后的数据
    :return:
    """
    #在请求中通过参数查询结果
    search_text = request.GET.get("_q","")
    #创建Q查询对象,组合搜索
    q_obj = Q()
    #设定连接方式
    q_obj.connector = "OR"
    #遍历搜索选项
    for search_words in admin_class.search_fields:
        q_obj.children.append(("{0}__contains".format(search_words), search_text))
    search_result = object_list.filter(q_obj)
    return search_result, search_text

编写视图函数

搜索功能已经写好,视图函数这里只需要引用添加就完美了。先找到过滤后的数据变量,将其作为参数传入即可:

def display_objects(request, app_name, table_name):
    #获取自定义的admin_class
    admin_class = enabled_admins[app_name][table_name]
    #数据查询
    #query_set = admin_class.model.objects.all()
    #分页处理
    #1.分页对象参数构建:对象列表,每页显示数量
    #query_set_list = admin_class.model.objects.all()
    #延伸===>添加过滤条件
    query_set_list, filter_conditions = table_filter(request, admin_class)
    #延伸===>添加搜索功能
    query_set_list, search_text = table_search(request, admin_class, query_set_list)
    #2.分页对象创建
    paginator = Paginator(query_set_list, admin_class.list_per_page)
    #3.获取前端点击的页面数值
    get_page = request.GET.get('page')
    #4.页面异常处理
    try:
        #直接获取该页内容
        query_set = paginator.page(get_page)
    except PageNotAnInteger:
        #不是整数值,跳转到首页
        query_set = paginator.page(1)
    except EmptyPage:
        #超出范围,跳转到最后一页
        query_set = paginator.page(paginator.num_pages)
    return render(request, 'king_admin/table_objs.html',
                                 {'admin_class': admin_class,
                                  'query_set': query_set,
                                  'filter_conditions': filter_conditions,
                                  'search_text': search_text})

上述文件中添加的内容并不是很多,添加内容如下:

 #延伸===>添加搜索功能
    query_set_list, search_text = table_search(request, admin_class, query_set_list)
	
#搜索结果作为参数返回
  'search_text': search_text

编写模板文件

这里就不需使用templatetags,直接在模板添加样式代码和后台数据参数,为了减少代码的重复,将搜索功能和过滤放在同一个表单里面:

...
                <form class="" method="get">
                    {#条件过滤#}
                    {% for condition in admin_class.list_filters %}
                        <div class="col-lg-2">
                                <span>{{ condition }}</span>
                                {% render_filter_element condition admin_class filter_conditions %}
                        </div>
                    {% endfor %}
                    <button type="SUBMIT" class="btn btn-success">检索</button>
                {# 添加搜索功能 #}
                <div class="row">
                  <div class="col-lg-2" style="margin: auto 30px">
                    <input type="search" name="_q" class="form-control" style="margin-left:15px" value="{{ search_text }}"
                           placeholder="{% for search_field in admin_class.search_fields %}{{ search_field }},{% endfor %} ">
                  </div>
                  <button type="SUBMIT" class="btn btn-success">search</button>
                  </div>
                </form>
...
{# 添加搜索到分页 #}
{% create_page_element query_set filter_conditions search_text %}
...

标签模板内的分页功能修改:

...
#----------------------------分页优化处理-----------------------------
@register.simple_tag
def create_page_element(query_set, filter_conditions, search_text):
    '''返回整个分页元素'''
    page_btns = ''
    filters = ''
    #过滤条件
    for k, v in filter_conditions.items():
        filters += '&{0}={1}'.format(k, v)
    added_dot_ele = False #标志符
    for page_num in query_set.paginator.page_range:
        if page_num < 3 or page_num > query_set.paginator.num_pages -2 \
                or abs(query_set.number - page_num) <= 2: #代表最前2页或最后2页 #abs判断前后1页
            element_class = ""
            if query_set.number == page_num:
                added_dot_ele = False
                element_class = "active"
            page_btns += '''<li class="%s"><a href="?page=%s%s&_q=%s">%s</a></li>''' % (element_class, page_num,
                                                                                        filters, search_text ,page_num)
        else: #显示...
            if added_dot_ele == False: #现在还没加...
                page_btns += '<li><a>...</a></li>'
                added_dot_ele = True
    return mark_safe(page_btns)

  添加搜索参数和拼接url

渲染后的页面效果:

虽然丑,但功能基本实现,后期会美化处理。

BUG解决

搜索QQ号,居然出现这个的页面:不能搜索

这个问题在前面已经叙述过了,这个_q在数据中是没有的,我可以将其作为保留字!

还记得之前写了一个专门用来存储保留字的列表吗?我们只需要将_q添加到列表即可,在utils.py文件中的过滤功能函数里面:

----------------过滤功能------------------------------
def table_filter(request, admin_class):
    """条件过滤,并构造滤后的数据结构"""
    filter_conditions = {}
    keywords = ['page', '_q'] #保留关键字
    for k, v in request.GET.items():
        if k in keywords:
            continue
        if v:
            filter_conditions[k] = v
    return admin_class.model.objects.filter(**filter_conditions), filter_conditions

完没解决问题!

添加排序功能

原生admin排序功能分析

如图:

看出默认情况下,Django是以二者进行降序排列的。当点击时,进行反序排列,还有点击取消功能。

编写排序功能函数

在编写之前我们还是同样的要在king_admin中添加排序字段:

#创建基类
class BaseAdmin(object):
    list_display = []
    list_filter = []
    search_fields = []
    ordering = None  #添加此字段
    list_per_page = 10
...
#自定义类,显示特定字段
class CustomerAdmin(BaseAdmin):
    list_display = ['id', 'qq','name','source','consultant','consult_course','date','status']
    list_filters = ['source','consultant','consult_course','status']
    search_fields = ['qq', 'name', 'consultant__name', ]
    ordering = 'date'
    list_per_page = 2
...

utils.py文件中,添加排序函数:

...
#-----------------------排序功能-----------------------------------
def table_sort(request, admin_class, query_set_list):
    """
    默认情况下,Djngo中取出来的数据是无序的
    :param request:
    :param query_set_list: 过滤、搜索之后的数据
    :return:
    """
    #---------------------------初始化排序设定--------------------------------
    # 默认排序条件---降序
    king_admin_ordering = "-{0}".format(admin_class.ordering if admin_class.ordering else "-id")
    # 获取排序后结果
    order_by_init = query_set_list.order_by(king_admin_ordering)
    #-----------------------------排序判断------------------------------------
    #通过参数获取到结果,默认None,修改为空
    order_by_text = request.GET.get('o', '')
    #判断是否存在
    if order_by_text:
        #存在即根据获取字段排反序
        order_result = order_by_init.order_by(order_by_text)
        #下次获取到的数据排序,要取反结果即:添加或去除‘-’
        #判断是否存在‘-’,存在就去除
        if order_by_text.startswith('-'):
            order_by_text = order_by_text.strip('-')
        #没有就加上
        else:
            order_by_text = '-{0}'.format(order_by_text)
    #不存在返回数据
    else:
        order_result = order_by_init
    return  order_result, order_by_text

编写视图函数

添加前,需要考虑一些问题:如何结合前面添加的功能? 也就是说,考虑到过滤,分页,搜索,统计等。按照前面的方式,我们都是以前面为基础,分页为后盾进行组合的,这里同样是可以的。

...
def display_objects(request, app_name, table_name):
    #获取自定义的admin_class
    admin_class = enabled_admins[app_name][table_name]
    #数据查询
    #query_set = admin_class.model.objects.all()
    #分页处理
    #1.分页对象参数构建:对象列表,每页显示数量
    #query_set_list = admin_class.model.objects.all()
    #延伸===>添加过滤条件
    query_set_list, filter_conditions = table_filter(request, admin_class)
    #延伸===>添加搜索功能
    query_set_list, search_text = table_search(request, admin_class, query_set_list)
    #延伸===>添加排序功能
    query_set_list, order_by_text = table_sort(request, admin_class, query_set_list)
    #2.分页对象创建
    paginator = Paginator(query_set_list, admin_class.list_per_page)
    #3.获取前端点击的页面数值
    get_page = request.GET.get('page')
    #4.页面异常处理
    try:
        #直接获取该页内容
        query_set = paginator.page(get_page)
    except PageNotAnInteger:
        #不是整数值,跳转到首页
        query_set = paginator.page(1)
    except EmptyPage:
        #超出范围,跳转到最后一页
        query_set = paginator.page(paginator.num_pages)
    return render(request, 'king_admin/table_objs.html',
                                 {'admin_class': admin_class,
                                  'query_set': query_set,
                                  'filter_conditions': filter_conditions,
                                  'search_text': search_text,
                                  'order_by_text': order_by_text})
...

上述文件添加的主要内容是:

#延伸===>添加排序功能
  query_set_list, order_by_text = table_sort(request, admin_class, query_set_list)

返回数据的参数:'order_by_text': order_by_text

编写模板文件

由于是结合前面的功能,这里我们要继续优化分页功能的templatetags里面的标签函数,其实就是在拼接的url里面添加一个额外的参数,目的是传递数据。

table_objs.html文件的分页标签修改:

...
 {% create_page_element query_set filter_conditions search_text order_by_text %}
...

添加一个排序参数

标签内功能函数修改如下:

...
#----------------------------分页优化处理-----------------------------
@register.simple_tag
def create_page_element(query_set, filter_conditions, search_text, order_by_text):
    '''返回整个分页元素'''
    page_btns = ''
    filters = ''
    #过滤条件
    for k, v in filter_conditions.items():
        filters += '&{0}={1}'.format(k, v)
    added_dot_ele = False #标志符
    for page_num in query_set.paginator.page_range:
        if page_num < 3 or page_num > query_set.paginator.num_pages -2 \
                or abs(query_set.number - page_num) <= 2: #代表最前2页或最后2页 #abs判断前后1页
            element_class = ""
            if query_set.number == page_num:
                added_dot_ele = False
                element_class = "active"
            page_btns += '''<li class="%s"><a href="?page=%s%s&_q=%s&o=%s">%s</a></li>''' % (element_class, page_num,
                                                                                                filters, search_text,
                                                                                             order_by_text ,page_num)
        else: #显示...
            if added_dot_ele == False: #现在还没加...
                page_btns += '<li><a>...</a></li>'
                added_dot_ele = True
    return mark_safe(page_btns)

添加排序参数和拼接url

基本的功能添加完毕,接下来就是添加触发按钮:通过字段进行触发排功能。

对显示字段进行一定的修改:

...
<table class="table table-hover">
                  <thead>
                       <tr>
                            {% for title_name in admin_class.list_display %}
                                 {% create_table_title title_name order_by_text filter_conditions %}
                            {% endfor %}
                       </tr>
                  </thead>
                  <tbody>
...

标签功能函数的添加:

#------------------------创建表头--------------------------------
@register.simple_tag
def create_table_title(title_name, order_by_text, filter_conditions):
    #过滤条件
    filters = ''
    for k, v in filter_conditions.items():
        filters += "&{0}={1}".format(k, v)
    #标签元素
    element = '''<th><a href="?{filters}&o={order_by_text}">{title_name}</a>{sort_icon}</th>'''
    #判断排序数据
    if order_by_text:
        #升序箭头
        if order_by_text.startswith("-"):
            sort_icon = '''<span class="glyphicon glyphicon-chevron-up"></span>'''
        #降序箭头
        else:
            sort_icon = '''<span class="glyphicon glyphicon-chevron-down"></span>'''
        #对比字段
        if order_by_text.strip("-") == title_name:
            order_by_text = order_by_text
        else:
            order_by_text = title_name
            sort_icon = ''
    else:  # 没有排序
        order_by_text = title_name
        sort_icon = ''
    ele = element.format(order_by_text=order_by_text, title_name=title_name, sort_icon=sort_icon, filters=filters)
    return mark_safe(ele)

渲染效果如下:

在此 额外添加了一列 id列。

BUG修改

1.显示效果看着没有问题,当点击进行排序时,又出现之前的错误:

 

 解决:在过滤功能函数中添加为保留关键字

---------------过滤功能------------------------------
def table_filter(request, admin_class):
    """条件过滤,并构造滤后的数据结构"""
    filter_conditions = {}
    keywords = ['page', '_q', 'o'] #保留关键字
    for k, v in request.GET.items():
        if k in keywords:
            continue
        if v:
            filter_conditions[k] = v
    return admin_class.model.objects.filter(**filter_conditions), filter_conditions

如下图:

 

不错,能进行正常的排序啦!但还是有点小缺点:怎么取消排序?后面优化的时候在添加吧。

添加复选框

添加checkbox标签

为每行的开头添加一个checkbox,只需要在table_objs.html文件中的表格标签中添加:

...
            <table class="table table-hover">
                  <thead>
                       <tr>
                            {# 添加该行 #}
                            <th style="width: 35px"><input type="checkbox" ></th>
                            {% for title_name in admin_class.list_display %}
                                 {% create_table_title title_name order_by_text filter_conditions %}
                            {% endfor %}
                       </tr>
                  </thead>
                  <tbody>
                      {% for item in query_set %}
                        <tr>
                           {# 添加该行 #}  
                            <td ><input  type="checkbox" value=""></td>
                            {#创建列表行数据#}
                            {% create_row item admin_class %}
                        </tr>
                      {% endfor %}
                  </tbody>
              </table>
...

添加事件和value

对标题的checkbox添加全选和全取消点击事件,并将每行的数据id添加到value

...
                  <thead>
                       <tr>
                            <th style="width: 35px"><input type="checkbox" onclick="checkAllToggle(this);"></th>
                            {% for title_name in admin_class.list_display %}
                                 {% create_table_title title_name order_by_text filter_conditions %}
                            {% endfor %}
                       </tr>
                  </thead>
                  <tbody>
                      {% for item in query_set %}
                        <tr>
                            <td ><input tag="object_checkbox" type="checkbox" value="{{ item.id }}"></td>
                            {#创建列表行数据#}
                            {% create_row item admin_class %}
                        </tr>
                      {% endfor %}
                  </tbody>
				  
...			  
 <script>
        function checkAllToggle(self) {
            if($(self).prop('checked')){
                $('input[tag="object_checkbox"]').prop('checked', true);
            }else{
                $('input[tag="object_checkbox"]').prop('checked', false);
            }
        }
    </script>
{% endblock %}

  JS脚本放在该文件的最下面即可,后面统一放到文件中。

渲染后的效果:

 

posted @ 2017-01-16 18:42  runnering  阅读(168)  评论(0)    收藏  举报