客户关系管理系统(二)
仿照Django-admin运用到自己项目的生产中
创建一个king-admin的应用
python manage.py startapp king-admin
把 king-admin 注册到配置文件中

在根级url.py中把king-admin中的url与之相关联
url(r'^king_admin/', include("king_admin.urls")),
在king-admin应用中创建一个king-admin.py的配置文件
配置文件的格式是
{'应用的名字':{'表的名字':'配置的内容'}}
{‘crm’:{'Userprofile':admin-class}}
通过模型类找到应用的名字
打开django自带的shell
python manage.py shell from crm.models import * UserProfile._meta.app_label
根据模型类获取表的名字(同样也在shell实验)
UserProfile._meta.model_name
配置文件king-damin.py的代码如下:
#__author: Administrator #date: 2017/1/5 from crm import models enabled_admins = {} class BaseAdmin(object): list_display = [] list_filters = [] list_per_page = 20 class CustomerAdmin(BaseAdmin): list_display = ['qq','name','source','consultant','consult_course','date','status'] list_filters = ['source','consultant','consult_course','status'] #model = models.Customer class CustomerFollowUpAdmin(BaseAdmin): list_display = ('customer','consultant','date') def register(model_class,admin_class=None): if model_class._meta.app_label not in enabled_admins: enabled_admins[model_class._meta.app_label] = {} #enabled_admins['crm'] = {} #admin_obj = admin_class() admin_class.model = model_class #绑定model 对象和admin 类 enabled_admins[model_class._meta.app_label][model_class._meta.model_name] = admin_class #enabled_admins['crm']['customerfollowup'] = CustomerFollowUpAdmin register(models.Customer,CustomerAdmin) register(models.CustomerFollowUp,CustomerFollowUpAdmin)
在king-admin应用views.py中定义显示表的视图函数
from django.shortcuts import render from king_admin import king_admin
def index(request):
return render(request,'king_admin/table_index.html',{'table_list':king_admin.enabled_admins}}
配置显示表的视图函数配置url
from django.conf.urls import url
from king_admin import views
urlpatterns = [
url(r'^$',views.index,name='table_index'),
]
前端渲染代码如下:
{{ table_list }}
在浏览器中输入以下的网址
http://127.0.0.1:8000/king_admin/

在前端的页面要显示应用的名字和对应的表如下所示
显示表的名字前端代码
{{ admin.model._meta.verbose_name }}
但是回报如下的错误信息原因是前端的模板语言变量和属性不支持下划线开头的命名方式

解决上面的方式是自定义一个标签,通过自定义的标签把表的名字作为返回值返回
在king-admin应用下创建 templatetags(固定必须这样写)目录,在templatetags目录下创建tags.py文件(这个文件的名字可以随意)
from django import template register = template.Library() @register.simple_tag def render_app_name(admin_class): return admin_class.model._meta.verbose_name
在前端中调用如下:
{% load tags %}
{{ admin.model._meta.verbose_name }}
前端完整代码如下
{% for app_name,app_tables in table_list.items %}
<table class="table table-hover">
<thead>
<tr>
<th>{{ app_name }}</th>
</tr>
</thead>
<tbody>
{% for table_name,admin in app_tables.items %}
<tr>
<td>
<a href="#">
{% render_app_name admin %}
</a>
</td>
<td>add</td>
<td>change</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}
刷新页面如下所示

在king-admin中的views.py中定义显示每个表中的内容的视图函数
def display_table_objs(request,app_name,table_name): admin_class = king_admin.enabled_admins[app_name][table_name] return render(request,'king_admin/table_objs.html',{"admin_class":admin_class})
定义其url
url(r'^(\w+)/(\w+)$',views.display_table_objs,name="table_objs"),
在模板 table_index.html 中 把 a 链接的跳转路径改为上面显示表内容的路径
<a href="{% url 'table_objs' app_name table_name %}"> {% render_app_name admin %} </a>
在table_obj.html的前端代码如下:
{{ admin_class.list_display }}
点击各个表显示的前端页面如下
补充前端的代码,只显示需要显示的字段
{% for column in admin_class.list_display %}
<th>{{ column }}</th>
{% endfor %}
刷新页面如下所示

继续补充前端的代码使之显示表的字段和要显示的字段一一对应,因为model是动态的添加进去的,在前端不显示,需要使用自定义的标签来返回所有表中的数据
在templatetags目录下的tags.py中添加可以返回所有表对象的自定义标签
@register.simple_tag def get_query_sets(admin_class): return admin_class.model.objects.all()
思路:通过需要查询的字段去映射表的对象
在templatetags目录下的tags.py中添加可以映射需要显示字段的自定义标签
@register.simple_tag def build_table_row(obj,admin_class): row_ele = "" for column in admin_class.list_display: field_obj = obj._meta.get_field(column) column_data = getattr(obj,column) row_ele += "<td>%s</td>" % column_data return mark_safe(row_ele)
前端代码如下:
{% get_query_sets admin_class as query_sets %}
{% for obj in query_sets %}
<tr>
{% build_table_row obj admin_class %}
</tr>
{% endfor %}
刷新页面如下所示

在上面source字段希望显示的数字对应的汉字内容
解决思路如下:
首先判断这个字段有没有choices方法,有的话把它显示出来
In [29]: m = Customer.objects.all()[0]
In [30]: s1=m._meta.get_field('source')
In [32]: s1.choices
Out[32]:
((0, '转介绍'),
(1, 'QQ群'),
(2, '官网'),
(3, '百度推广'),
(4, '51CTO'),
(5, '知乎'),
(6, '市场推广'))
m.get_source_display()
为空直接获取的是它的属性
In [35]: s1=m._meta.get_field('name')
In [36]: s1.choices
Out[36]: []
In [40]: getattr(m,'name')
Out[40]: 'zyc'
故可以对 自定义的build_table_row 标签做简单的判断需要显示的字段的choices属性是否为空
from django.utils.safestring import mark_safe @register.simple_tag def build_table_row(obj,admin_class): row_ele = "" for column in admin_class.list_display: field_obj = obj._meta.get_field(column) if field_obj.choices:#choices type column_data = getattr(obj,"get_%s_display" % column)() else: column_data = getattr(obj,column) row_ele += "<td>%s</td>" % column_data return mark_safe(row_ele) # 标签HTML转义
刷新页面如下所示

上面的日期格式不正确同样可以通过 build_table_row 自定义标签来解决
In [9]: m = Customer.objects.all()[0] In [10]: type(m.date).__name__ Out[10]: 'datetime'
完整代码
def build_table_row(obj,admin_class): row_ele = "" for column in admin_class.list_display: field_obj = obj._meta.get_field(column) if field_obj.choices:#choices type column_data = getattr(obj,"get_%s_display" % column)() else: column_data = getattr(obj,column) if type(column_data).__name__ == 'datetime': column_data = column_data.strftime("%Y-%m-%d %H:%M:%S") row_ele += "<td>%s</td>" % column_data return mark_safe(row_ele)
刷新页面如下所示
对查询到的数据进行分页,补充 display_table_objs 视图函数,增加分页
首先把以前的查询到的表给注释掉

def display_table_objs(request,app_name,table_name): # 1 admin_class = king_admin.enabled_admins[app_name][table_name] # 2 分页, 返回的是当前查询到的页 object_list=admin_class.model.objects.all() paginator = Paginator(object_list,1) page = request.GET.get('page') try: query_sets = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. query_sets = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. query_sets = paginator.page(paginator.num_pages) context={"query_sets":query_sets,"admin_class":admin_class} return render(request,'king_admin/table_objs.html',context)
自定义一个标签来显示分页的逻辑
@register.simple_tag def render_page_ele(loop_counter,query_sets): # query_sets.number 当前的页数 if abs(query_sets.number - loop_counter) <= 2: ele_class = "" if query_sets.number == loop_counter: ele_class = "active" ele = '''<li class="%s"><a href="?page=%s">%s</a></li>''' %(ele_class,loop_counter,loop_counter) return mark_safe(ele) return ''
前端分页代码如下所示
<ul class="pagination"> {% if query_sets.has_previous %} <li class=""><a href="?page={{ query_sets.previous_page_number }}">上页</a></li> {% endif %} {% for loop_counter in query_sets.paginator.page_range %} {% render_page_ele loop_counter query_sets%} {% endfor %} {% if query_sets.has_next %} <li class=""><a href="?page={{ query_sets.next_page_number }}">下页</a></li> {% endif %} </ul>
刷新页面如下所示

过滤的思路(就是已字典的形式进行查询)
In [14]: from crm.models import *
In [15]: filters={"consultant_id":1,"source":0}
In [16]: Customer.objects.filter(**filters)
Out[16]: <QuerySet [<Customer: 123>]>
前端实现可以显示过滤的字段代码如下
<form class="" method="get"> {% for condtion in admin_class.list_filters %} <div class="col-lg-2"> <span>{{ condtion }}</span> {% render_filter_ele condtion admin_class%} </div> {% endfor %} <button type="SUBMIT" class="btn btn-success">检索</button> </form>
添加自定义的标签
@register.simple_tag def render_filter_ele(condtion,admin_class): select_ele = '''<select class="form-control" name='%s' ><option value=''>----</option>''' %condtion field_obj = admin_class.model._meta.get_field(condtion) if field_obj.choices: ''' ((0, '转介绍'), (1, 'QQ群'), (2, '官网'), (3, '百度推广'), (4, '51CTO'), (5, '知乎'), (6, '市场推广')) ''' selected = '' for choice_item in field_obj.choices: select_ele += '''<option value='%s' %s>%s</option>''' %(choice_item[0],selected,choice_item[1]) selected ='' if type(field_obj).__name__ == "ForeignKey": selected = '' for choice_item in field_obj.get_choices()[1:]: select_ele += '''<option value='%s' %s>%s</option>''' %(choice_item[0],selected,choice_item[1]) selected = '' select_ele += "</select>" return mark_safe(select_ele)
刷新前端页面如下所示:

下一步需要实现的是可以对选择的字段实现可以检索的功能
在king-admin目录下,创建utils.py,返回查询集代码如下:
def table_filter(request,admin_class): '''进行条件过滤并返回过滤后的数据''' filter_conditions = {} for k,v in request.GET.items(): if v: filter_conditions[k] =v return admin_class.model.objects.filter(**filter_conditions)
在视图函数中调用查询的结果,返回给前端,代码如下:
from king_admin.utils import table_filter def display_table_objs(request,app_name,table_name): # 1 admin_class = king_admin.enabled_admins[app_name][table_name] # 2 分页, 返回的是当前查询到的页 # object_list=admin_class.model.objects.all() object_list= table_filter(request, admin_class) paginator = Paginator(object_list,10) page = request.GET.get('page') try: query_sets = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. query_sets = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. query_sets = paginator.page(paginator.num_pages) context={"query_sets":query_sets,"admin_class":admin_class} return render(request,'king_admin/table_objs.html',context)
在前端选择过滤的信息,点击搜索

返回的结果如下:

这时想要完善把检索的条件页保留下来。最简单的做法就是后端把检索的条件返回
在utils.py中返回检索的条件,完整的代码如下:
def table_filter(request,admin_class): '''进行条件过滤并返回过滤后的数据''' filter_conditions = {} for k,v in request.GET.items(): if v: filter_conditions[k] =v return admin_class.model.objects.filter(**filter_conditions),filter_conditions
后端视图函数把检索的结果返回给前端,完整的代码如下:
def display_table_objs(request,app_name,table_name): # 1 admin_class = king_admin.enabled_admins[app_name][table_name] # 2 分页, 返回的是当前查询到的页 # object_list=admin_class.model.objects.all() object_list, filter_condtions = table_filter(request, admin_class) paginator = Paginator(object_list,10) page = request.GET.get('page') try: query_sets = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. query_sets = paginator.page(1) except EmptyPage: # If page is out of range (e.g. 9999), deliver last page of results. query_sets = paginator.page(paginator.num_pages) context={"query_sets":query_sets,"admin_class":admin_class, "filter_condtions":filter_condtions} return render(request,'king_admin/table_objs.html',context)
前端的代码,把检索的条件传给自定义标签修改如下:
<form class="" method="get"> {% for condtion in admin_class.list_filters %} <div class="col-lg-2"> <span>{{ condtion }}</span> {# {% render_filter_ele condtion admin_class filter_condtions %}#} {% render_filter_ele condtion admin_class filter_condtions %} </div> {% endfor %} <button type="SUBMIT" class="btn btn-success">检索</button> </form>
自定义的标签局部修改的完整的代码如下:
@register.simple_tag def render_filter_ele(condtion,admin_class,filter_condtions): select_ele = '''<select class="form-control" name='%s' ><option value=''>----</option>''' %condtion field_obj = admin_class.model._meta.get_field(condtion) if field_obj.choices: ''' ((0, '转介绍'), (1, 'QQ群'), (2, '官网'), (3, '百度推广'), (4, '51CTO'), (5, '知乎'), (6, '市场推广')) ''' selected = '' for choice_item in field_obj.choices: print("choice",choice_item,filter_condtions.get(condtion),type(filter_condtions.get(condtion))) if filter_condtions.get(condtion) == str(choice_item[0]): # choice (0, '转介绍') 0 <class 'str'> selected ="selected" select_ele += '''<option value='%s' %s>%s</option>''' %(choice_item[0],selected,choice_item[1]) selected ='' if type(field_obj).__name__ == "ForeignKey": selected = '' for choice_item in field_obj.get_choices()[1:]: if filter_condtions.get(condtion) == str(choice_item[0]): selected = "selected" select_ele += '''<option value='%s' %s>%s</option>''' %(choice_item[0],selected,choice_item[1]) selected = '' select_ele += "</select>" return mark_safe(select_ele)
这次检索的条件就会保留下来


浙公网安备 33010602011771号