项目一:CRM(客户关系管理系统)--5
Posted EagleSour
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目一:CRM(客户关系管理系统)--5相关的知识,希望对你有一定的参考价值。
简单基础的功能添加是比较漫长的道路,前面添加了分页和过滤功能,接下来添加的仍然会是一些琐碎而又常用的功能。
1. 添加页面统计功能
这个功能实在是太简单了,只需要一行代码就能够搞定,当然是在这使用Django
的情况,其他框架虽然没有使用过,但应该一行代码也能搞定,可以通过直接数据库查询将统计结果返回给模板文件进行渲染。我们在这里使用的方法很简单,不要更改其他任何文件的代码,只需要在table_objs.html
文件中添加如下内容(位置自己定):
1 ... 2 </table> 3 {# 添加下面的一行代码即可! #} 4 <p>数量统计:<mark style="margin: auto 5px">{{ query_set.paginator.count }}</mark></p> 5 <nav> 6 {#分页处理#} 7 ...
显示效果如下:
2. 添加搜索功能
外键查询和非外键查询方式不同:
非外键查询使用:
In [142]: models.CustomerInfo.objects.filter(name__contains=\'1\')
Out[142]: <QuerySet [<CustomerInfo: QQ:1 -- Name:test1>]>
外键查询使用:
In [143]: models.CustomerInfo.objects.filter(consult_course__name__contains=1)
Out[143]: <QuerySet [<CustomerInfo: QQ:1 -- Name:test1>]>
因此,在kingadmin.py中定义管理类中,如果要自定制search_fields时,一定要搞清楚field名称是否是外键,如果是外键的话,这里写的field名称应该是:field_name__外键field_name(中间使用双下换线连接),否则的话会报错:
1 In [144]: models.CustomerInfo.objects.filter(consult_course__contains=1) 2 --------------------------------------------------------------------------- 3 FieldError Traceback (most recent call last) 4 <ipython-input-144-f7156274ef81> in <module>() 5 ----> 1 models.CustomerInfo.objects.filter(consult_course__contains=1) 6 7 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/db/models/manager.py in manager_method(self, *args, **kwargs) 8 83 def create_method(name, method): 9 84 def manager_method(self, *args, **kwargs): 10 ---> 85 return getattr(self.get_queryset(), name)(*args, **kwargs) 11 86 manager_method.__name__ = method.__name__ 12 87 manager_method.__doc__ = method.__doc__ 13 14 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/db/models/query.py in filter(self, *args, **kwargs) 15 782 set. 16 783 """ 17 --> 784 return self._filter_or_exclude(False, *args, **kwargs) 18 785 19 786 def exclude(self, *args, **kwargs): 20 21 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/db/models/query.py in _filter_or_exclude(self, negate, *args, **kwargs) 22 800 clone.query.add_q(~Q(*args, **kwargs)) 23 801 else: 24 --> 802 clone.query.add_q(Q(*args, **kwargs)) 25 803 return clone 26 804 27 28 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/db/models/sql/query.py in add_q(self, q_object) 29 1248 existing_inner = set( 30 1249 (a for a in self.alias_map if self.alias_map[a].join_type == INNER)) 31 -> 1250 clause, _ = self._add_q(q_object, self.used_aliases) 32 1251 if clause: 33 1252 self.where.add(clause, AND) 34 35 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/db/models/sql/query.py in _add_q(self, q_object, used_aliases, branch_negated, current_negated, allow_joins, split_subq) 36 1274 child, can_reuse=used_aliases, branch_negated=branch_negated, 37 1275 current_negated=current_negated, connector=connector, 38 -> 1276 allow_joins=allow_joins, split_subq=split_subq, 39 1277 ) 40 1278 joinpromoter.add_votes(needed_inner) 41 42 /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/django/db/models/sql/query.py in build_filter(self, filter_expr, branch_negated, current_negated, can_reuse, connector, allow_joins, split_subq) 43 1199 lookup_class = field.get_lookup(lookups[0]) 44 1200 if lookup_class is None: 45 -> 1201 raise FieldError(\'Related Field got invalid lookup: {}\'.format(lookups[0])) 46 1202 if len(targets) == 1: 47 1203 lhs = targets[0].get_col(alias, field) 48 49 FieldError: Related Field got invalid lookup: contains
前端在渲染的时候也是报错:
2.1. 原生admin搜索功能分析
回来看看起初我们在admin.py
中添加的功能字段:
1 ... 2 #自定义操作 3 class CustomerAdmin(admin.ModelAdmin): 4 list_display = (\'name\', \'id\',\'qq\',\'source\',\'consultant\',\'content\',\'status\',\'date\') 5 list_filter = (\'source\',\'consultant\',\'date\') 6 #搜索功能,并指定搜索字段 7 search_fields = (\'qq\',\'name\',\'consult_course__name\') 8 raw_id_fields = (\'consult_course\',) 9 filter_horizontal = (\'tags\',) 10 list_editable = (\'status\',) 11 ...
添加了这么多的功能,大家应该早就发现了吧!Django
为我们提供的套路都是一样的,我们进行模仿重构也是同样的套路。在此基础上,我们还可以进一步的扩展!
2.2. 添加搜索字段
在我们自己的king_admin
中添加,首先要在基类中添加,然后在独立的自定义子类中添加,具体如下:
1 from CRM import models 2 #创建基类 3 class BaseAdmin(object): 4 list_display = [] 5 list_filter = [] 6 search_fields = [] 7 list_per_page = 10 8 ... 9 #自定义类,显示特定字段 10 class CustomerAdmin(BaseAdmin): 11 list_display = [\'qq\',\'name\',\'source\',\'consultant\',\'consult_course\',\'date\',\'status\'] 12 list_filters = [\'source\',\'consultant\',\'consult_course\',\'status\'] 13 #添加如下代码,搜索字段自选 14 search_fields = [\'qq\', \'name\', \'consultant__name\'] 15 list_per_page = 2 16 #model = models.Customer 17 ...
这里的consultant__name
考虑到了跨表的问题,先这样写,后面在优化的时候集中处理!
2.3. 编写搜索功能函数
这里,我们还要考虑一个问题:前面我们添加了过滤、分页、统计功能,那么搜索功能是该基于这些功能呢? 很显然,是必须的。也就是说,当我们将数据过滤后得到的结果,在通过搜索查询到所需的具体数据。
在utils.py
文件中,过滤功能函数下面继续编写搜索功能函数:
1 from django.db.models import Q 2 ... 3 #-----------------------搜索功能----------------------------------- 4 def table_search(request,admin_class,object_list): 5 """ 6 :param request: 封装的请求体 7 :param admin_class: 自定义类 8 :param object_list: 过滤后的数据 9 :return: 10 """ 11 #在请求中通过参数查询结果 12 search_text = request.GET.get("_q","") 13 #创建Q查询对象,组合搜索 14 q_obj = Q() 15 #设定连接方式 16 q_obj.connector = "OR" 17 #遍历搜索选项 18 for search_words in admin_class.search_fields: 19 q_obj.children.append(("{0}__contains".format(search_words), search_text)) 20 search_result = object_list.filter(q_obj) 21 return search_result, search_text
2.4. 编写视图函数
搜索功能已经写好,视图函数这里只需要引用添加就完美了。先找到过滤后的数据变量,将其作为参数传入即可:
1 def display_objects(request, app_name, table_name): 2 #获取自定义的admin_class 3 admin_class = enabled_admins[app_name][table_name] 4 #数据查询 5 #query_set = admin_class.model.objects.all() 6 #分页处理 7 #1.分页对象参数构建:对象列表,每页显示数量 8 #query_set_list = admin_class.model.objects.all() 9 #延伸===>添加过滤条件 10 query_set_list, filter_conditions = table_filter(request, admin_class) 11 #延伸===>添加搜索功能 12 query_set_list, search_text = table_search(request, admin_class, query_set_list) 13 #2.分页对象创建 14 paginator = Paginator(query_set_list, admin_class.list_per_page) 15 #3.获取前端点击的页面数值 16 get_page = request.GET.get(\'page\') 17 #4.页面异常处理 18 try: 19 #直接获取该页内容 20 query_set = paginator.page(get_page) 21 except PageNotAnInteger: 22 #不是整数值,跳转到首页 23 query_set = paginator.page(1) 24 except EmptyPage: 25 #超出范围,跳转到最后一页 26 query_set = paginator.page(paginator.num_pages) 27 return render(request, \'king_admin/table_objs.html\', 28 {\'admin_class\': admin_class, 29 \'query_set\': query_set, 30 \'filter_conditions\': filter_conditions, 31 \'search_text\': search_text})
上述文件中添加的内容并不是很多,添加内容如下:
1 #延伸===>添加搜索功能 2 query_set_list, search_text = table_search(request, admin_class, query_set_list) 3 4 #搜索结果作为参数返回 5 \'search_text\': search_text
2.5. 编写模板文件
这里就不需使用templatetags
,直接在模板添加样式代码和后台数据参数,为了减少代码的重复,将搜索功能和过滤放在同一个表单里面:
1 ... 2 <form class="" method="get"> 3 {#条件过滤#} 4 {% for condition in admin_class.list_filters %} 5 <div class="col-lg-2"> 6 <span>{{ condition }}</span> 7 {% render_filter_element condition admin_class filter_conditions %} 8 </div> 9 {% endfor %} 10 <button type="SUBMIT" class="btn btn-success">检索</button> 11 {# 添加搜索功能 #} 12 <div class="row"> 13 <div class="col-lg-2" style="margin: auto 30px"> 14 <input type="search" name="_q" class="form-control" style="margin-left:15px" value="{{ search_text }}" 15 placeholder="{% for search_field in admin_class.search_fields %}{{ search_field }},{% endfor %} "> 16 </div> 17 <button type="SUBMIT" class="btn btn-success">search</button> 18 </div> 19 </form> 20 ... 21 {# 添加搜索到分页 #} 22 {% create_page_element query_set filter_conditions search_text %} 23 ...
标签模板内的分页功能修改:
1 ... 2 #----------------------------分页优化处理----------------------------- 3 @register.simple_tag 4 def create_page_element(query_set, filter_conditions, search_text): 5 \'\'\'返回整个分页元素\'\'\' 6 page_btns = \'\' 7 filters = \'\' 8 #过滤条件 9 for k, v in filter_conditions.items(): 10 filters += \'&{0}={1}\'.format(k, v) 11 added_dot_ele = False #标志符 12 for page_num in query_set.paginator.page_range: 13 if page_num < 3 or page_num > query_set.paginator.num_pages -2 \\ 14 or abs(query_set.number - page_num) <= 2: #代表最前2页或最后2页 #abs判断前后1页 15 element_class = "" 16 if query_set.number == page_num: 17 added_dot_ele = False 18 element_class = "active" 19 page_btns += \'\'\'<li class="%s"><a href="?page=%s%s&_q=%s">%s</a></li>\'\'\' % (element_class, page_num, 20 filters, search_text ,page_num) 21 else: #显示... 22 if added_dot_ele == False: #现在还没加... 23 page_btns += \'<li><a>...</a></li>\' 24 added_dot_ele = True 25 return mark_safe(page_btns)
添加搜索参数和拼接url
渲染后的页面效果:
2.6. BUG解决
搜索QQ号,居然出现这个的页面:不能搜索
这个问题在前面已经叙述过了,这个_q
在数据中是没有的,我可以将其作为保留字!
还记得之前写了一个专门用来存储保留字的列表吗?我们只需要将_q
添加到列表即可,在utils.py
文件中的过滤功能函数里面:
1 ----------------过滤功能------------------------------ 2 def table_filter(request, admin_class): 3 """条件过滤,并构造滤后的数据结构""" 4 filter_conditions = {} 5 keywords = [\'page\', \'_q\'] #保留关键字 6 for k, v in request.GET.items(): 7 if k in keywords: 8 continue 9 if v: 10 filter_conditions[k] = v 11 return admin_class.model.objects.filter(**filter_conditions), filter_conditions
完美解决问题!
3. 添加排序功能
3.1. 原生admin排序功能分析
如图:
看出默认情况下,Django
是以二者进行降序排列的。当点击时,进行反序排列,还有点击取消功能。
3.2. 编写排序功能函数
在编写之前我们还是同样的要在king_admin
中添加排序字段:
1 #创建基类 2 class BaseAdmin(object): 3 list_display = [] 4 list_filter = [] 5 search_fields = [] 6 ordering = None #添加此字段 7 list_per_page = 10 8 ... 9 #自定义类,显示特定字段 10 class CustomerAdmin(BaseAdmin): 11 list_display = [\'id\', \'qq\',\'name\',\'source\',\'consultant\',\'consult_course\',\'date\',\'status\'] 12 list_filters = [\'source\',\'consultant\',\'consult_course\',\'status\'] 13 search_fields = [\'qq\', \'name\', \'consultant__name\', ] 14 ordering = \'date\' 15 list_per_page = 2 16 ...
在utils.py
文件中,添加排序函数:
1 ... 2 #-----------------------排序功能----------------------------------- 3 def table_sort(request, admin_class, query_set_list): 4 """ 5 默认情况下,Djngo中取出来的数据是无序的 6 :param request: 7 :param query_set_list: 过滤、搜索之后的数据 8 :return: 9 """ 10 #---------------------------初始化排序设定-------------------------------- 11 # 默认排序条件---降序 12 king_admin_ordering = "-{0}".format(admin_class.ordering if admin_class.ordering else "-id") 13 # 获取排序后结果 14 order_by_init = query_set_list.order_by(king_admin_ordering) 15 #-----------------------------排序判断------------------------------------ 16 #通过参数获取到结果,默认None,修改为空 17 order_by_text = request.GET.get(\'o\', \'\') 18 #判断是否存在 19 if order_by_text: 20 #存在即根据获取字段排反序 21 order_result = order_by_init.order_by(order_by_text) 22 #下次获取到的数据排序,要取反结果即:添加或去除‘-’ 23 #判断是否存在‘-’,存在就去除 24 if order_by_text.startswith(\'-\'): 25 order_by_text = order_by_text.strip(\'-\') 26 #没有就加上 27 else: 28 order_by_text = \'-{0}\'.format(order_by_text) 29 #不存在返回数据 30 else: 31 order_result = order_by_init 32 return order_result, order_by_text
3.3. 编写视图函数
添加前,需要考虑一些问题:如何结合前面添加的功能? 也就是说,考虑到过滤,分页,搜索,统计等。按照前面的方式,我们都是以前面为基础,分页为后盾进行组合的,这里同样是可以的。
1 ... 2 def display_objects(request, app_name, table_name): 3 #获取自定义的admin_class 4 admin_class = enabled_admins[app_name][table_name] 5 #数据查询 6 #query_set = admin_class.model.objects.all() 7 #分页处理 8 #1.分页对象参数构建:对象列表,每页显示数量 9 #query_set_list = admin_class.model.objects.all() 10 #延伸===>添加过滤条件 11 query_set_list, filter_conditions = table_filter(request, admin_class) 12 #延伸===>添加搜索功能 13 query_set_list, search_text = table_search(request, admin_class, query_set_list) 14 #延伸===>添加排序功能 15 query_set_list, order_by_text = table_sort(request, admin_class, query_set_list) 16 #2.分页对象创建 17 paginator = Paginator(query_set_list, admin_class.list_per_page) 18 #3.获取前端点击的页面数值 19 get_page = request.GET.get(\'page\') 20 #4.页面异常处理 21 try: 22 #直接获取该页内容 23 query_set = paginator.page(get_page) 24 except PageNotAnInteger: 25 #不是整数值,跳转到首页 26 query_set = paginator.page(1) 27 except EmptyPage: 28 #超出范围,跳转到最后一页 29 query_set = paginator.page(paginator.num_pages) 30 return render(request, \'king_admin/table_objs.html\', 31 {\'admin_class\': admin_class, 32 \'query_set\': query_set, 33 \'filter_conditions\': filter_conditions, 34 \'search_text\': search_text, 35 \'order_by_text\': order_by_text}) 36 ...
上述文件添加的主要内容是:
1 #延伸===>添加排序功能 2 query_set_list, order_by_text = table_sort(request, admin_class, query_set_list)
返回数据的参数:\'order_by_text\': order_by_text
3.4. 编写模板文件
由于是结合前面的功能,这里我们要继续优化分页功能的templatetags
里面的标签函数,其实就是在拼接的url
里面添加一个额外的参数,目的是传递数据。
table_objs.html
文件的分页标签修改:
1 ... 2 {% create_page_element query_set filter_conditions search_text order_by_text %} 3 ...
添加一个排序参数
标签内功能函数
以上是关于项目一:CRM(客户关系管理系统)--5的主要内容,如果未能解决你的问题,请参考以下文章