项目一:CRM(客户关系管理系统)--6

Posted EagleSour

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目一:CRM(客户关系管理系统)--6相关的知识,希望对你有一定的参考价值。

功能是永远加不完的!重头戏的Action放在后面作为压轴,接下来该添加三级页面啦!

1. 添加编辑页面轮廓

有的朋友可能会问:为何直接写编辑页面而没有写添加页面?那是因为二者是相互继承的关系,个人觉得先写编辑比较好,然后添加继承编辑页面,改动基本上后台数据的更新与存储的问题。

1.1. 原生admin路由分析

如图:

 

1.2. 构建编辑页面路由

king_admin应用下的urls.py文件中添加:

1 from django.conf.urls import url
2 from king_admin import views
3 urlpatterns = [
4     url(r\'^$\', views.index, name=\'table_index\'),
5     url(r\'^(\\w+)/(\\w+)/$\', views.display_objects, name=\'display_objects\'),
6     url(r\'^(\\w+)/(\\w+)/(\\d+)/edit/$\', views.table_object_edit,name="table_object_edit"),  #添加该行数据
7 ]

 

1.3. 添加视图函数

king_admin应用下的views.py文件中添加:

1 def table_object_edit(request, app_name, table_name, object_id):
2     return render(request, \'king_admin/table_object_edit.html\')

 

1.4. 添加模板文件

template/king_admin/目录下的table_object_edit.html文件,并在文件中添加如下内容:

1 {%  extends \'king_admin/table_index.html\' %}
2 {% block extra-css-resources %}
3      
4 {% endblock %}
5 {% block container %}
6      
7 {% endblock %}

 

1.5.添加编辑页面的入口

上面我们已经写完基本的页面流程,现在来添加编辑页面入口来打通整体流程,二级显示页面我们通过在templates中进行编写,修改这里即可,只需要添加一个参数(request),索引值和判断:

 1 ...
 2  
 3 <-----------------------创建表格行数据-----------------------------
 4 @register.simple_tag
 5 def create_row(request, query_set_obj, admin_class):
 6     #创建标签元素--空,None不行
 7     element = \'\'
 8  
 9     #遍历要显示的models字段
10     for number, row in enumerate(admin_class.list_display):
11     #获取显示字段对应的字段对象
12         field_obj = admin_class.model._meta.get_field(row)
13     #获取数据
14         #判断choice
15         if field_obj.choices:
16             #通过反射获取对象里面的值,并执行该方法get_字段_display()获取choices里面的数值
17             row_data = getattr(query_set_obj, \'get_{0}_display\'.format(row))()
18         else:
19             row_data = getattr(query_set_obj, row)
20  
21         #时间格式转换
22         if type(row_data).__name__ == \'datetime\':
23             row_data = row_data.strftime(\'%Y-%m-%d %H-%M-%S\')
24  
25         #添加编辑页面入口
26         if number == 0: #add a tag, 可以跳转到修改页
27             row_data = "<a href=\'{request_path}{obj_id}/edit/\'>{data}</a>".format(request_path=request.path,
28                                                                                         obj_id=query_set_obj.id,
29                                                                                         data=row_data)
30  
31         #标签元素的拼接
32         element += "<td>{0}</td>".format(row_data)
33     return mark_safe(element)
34  
35 ...

上述内容改完后,不要忘记在模板文件中还要添加一个参数:request

访问看看:

 

2. 填充编辑页面详情

2.1. 编写动态生成ModelForm派生类的功能函数

由于数据量很大,每条数据都要进入到第三级页面中,生成新的页面进行操作:添加、修改、删除等。那么,我是不是要针对每条数据都要写一套代码,很显然是不可能的!作为程序员,重复代码就是最大的BUG,尤其是大量的同样代码。

在三级页面中,不论是编辑、添加还是删除都有共通的地方。那么该怎么实现呢?好在Django为我们提供了便捷的方式:动态生成Form表单。

Django中,原生的admin常结合models使用的Form表单功能是ModelForm,它能够配合数据库动态生成数据表单。

我们知道如何动态的实现表单功能了,那它同样的问题就是代码的重复问题,那么多的数据,我们要写那么多的ModelForm吗?回答是肯定的:不可能! 不想写重复代码其实也很简单的,我们只需要动态生成ModelForm不就行了嘛!

通常我们使用ModelForm会这样:

1 from django.forms import ModelForm
2 from crm import models
3 #要动态生成的目标类
4 class CustomerModelForm(ModelForm):
5     class Meta:
6         model =  models.Customer
7         fields = "__all__"

现在,我们要让它自动创建:在king_admin项目的目录下,创建forms.py文件。

 1 from django.forms import ModelForm
 2  
 3 def create_model_form(request,admin_class):
 4     \'\'\'
 5     动态生成ModelForm类
 6     :param request:
 7     :param admin_class:
 8     :return:
 9     \'\'\'
10     <-----------------------类成员构造-----------------------------
11     class Meta:
12         model = admin_class.model
13         fields = "__all__"
14     #类的成员
15     attrs = {\'Meta\':Meta}
16  
17     <-----------------------动态创建类-----------------------------
18     #type函数创建类--->type(\'类名\',(基类,),以字典形式的类的成员)
19     _model_form_class =  type("DynamicModelForm",(ModelForm,),attrs)
20  
21     <-----------------定义创建对象的方法-----------------------------
22     # 定义__new__方法,用于创建类,cls类名
23     def __new__(cls, *args, **kwargs):
24         # 遍历数据库的所有字段和字段对应的对象
25         for field_name, field_obj in cls.base_fields.items():
26             # 为字段对象的组件添加class属性
27             field_obj.widget.attrs[\'class\'] = \'form-control\'
28         # 创建当前类的实例--->即创建子类
29         return ModelForm.__new__(cls)
30         # 定义元数据
31  
32     <-------------------为对象添加属性--------------------------------
33     #为该类添加__new__静态方法,当调用该类时,会先执行__new__方法,创建对象
34     # 这里会覆盖父类的__new__
35     setattr(_model_form_class,\'__new__\',__new__)
36  
37     return _model_form_class

 

2.2 编写视图函数

在二级显示页面中,我们通过点击id值跳转到编辑页面,然后经过路由触发视图函数获取到相关数据,修改会经过上面创建的表单验证类进行验证:

 1 ...
 2 from king_admin.forms import create_model_form
 3  
 4 ...
 5 def table_object_edit(request, app_name, table_name, object_id):
 6     admin_class = site.enabled_admins[app_name][table_name]
 7     #创建ModelForm类
 8     model_form = create_model_form(request, admin_class)
 9     #通过id获取数据库内容
10     object_list = admin_class.model.objects.get(id=object_id)
11  
12     if request.method == \'POST\':
13         #表单进行验证,更新数据
14         form_object = model_form(request.POST, instance=object_list)
15         if form_object.is_valid():
16             form_object.save()
17     else:
18         form_object = model_form(instance=object_list)
19  
20     return render(request, \'king_admin/table_object_edit.html\', {"form_object": form_object,
21                                                                  "admin_class": admin_class,
22                                                                  "app_name": app_name,
23                                                                  "table_name": table_name})

2.3 编写模板文件

在新建的table_object_edit.html中我们添加的应该会比较多,因此,会拆分成几大部分编写。

2.3.1 基础字段内容显示

先将数据内容显示和字段名称显示出来:

 1 {%  extends \'king_admin/table_index.html\' %}
 2  
 3 {% block extra-css-resources %}
 4  
 5 {% endblock %}
 6  
 7  
 8 {% block container %}
 9     <form method="post" class="form-horizontal">
10     {% csrf_token %}
11         {% for field in form_object %}
12         <div class="form-group" >
13             {# 显示名称 #}
14             <label  class="col-sm-3 control-label" style="font-weight: normal">
15                     {{ field.label }}
16             </label>
17             {# 显示数据输入框 #}
18             <div class="col-lg-6" style="width: auto">
19                 {{ field }}
20             </div>
21       </div>
22         {% endfor %}
23     </form>
24 {% endblock %}

效果如下:

 

2.3.2 必填字段显示设置

效果我们看到了,已经达到预期。在回过头看看form_object到底是什么?

在视图函数中,我们通过打印form_object(自行添加print(form_object))能够看到如下内容:

 1 <tr><th><label for="id_name">Name:</label></th><td><input class="form-control" id="id_name" maxlength="32" name="name" type="text" value="dfgh" /></td></tr>
 2 <tr><th><label for="id_qq">Qq:</label></th><td><input class="form-control" id="id_qq" maxlength="64" name="qq" type="text" value="0002" required /></td></tr>
 3 <tr><th><label for="id_qq_name">Qq name:</label></th><td><input class="form-control" id="id_qq_name" maxlength="64" name="qq_name" type="text" value="发电规划" /></td></tr>
 4 <tr><th><label for="id_phone">Phone:</label></th><td><input class="form-control" id="id_phone" maxlength="64" name="phone" type="text" value="234蚊346" /></td></tr>
 5 <tr><th><label for="id_source">Source:</label></th><td><select class="form-control" id="id_source" name="source" required>
 6 <option value="">---------</option>
 7 <option value="0" selected="selected">转介绍</option>
 8 <option value="1">QQ群</option>
 9 <option value="2">官网</option>
10 <option value="3">百度推广</option>
11 <option value="4">51CTO</option>
12 <option value="5">知乎</option>
13 <option value="6">市场推广</option>
14 </select></td></tr>
15 <tr><th><label for="id_referral_from">转介绍人qq:</label></th><td><input class="form-control" id="id_referral_from" maxlength="64" name="referral_from" type="text" value="567456" /></td></tr>
16 <tr><th><label for="id_consult_course">咨询课程:</label></th><td><select class="form-control" id="id_consult_course" name="consult_course" required>
17 <option value="">---------</option>
18 <option value="4">成功学</option>
19 <option value="5">搜索引擎</option>
20 <option value="6" selected="selected">内核开发</option>
21 <option value="7">相亲</option>
22 </select></td></tr>
23 <tr><th><label for="id_content">咨询详情:</label></th><td><textarea class="form-control" cols="40" id="id_content" name="content" rows="10" required>
24 如图</textarea></td></tr>
25 <tr><th><label for="id_tags">Tags:</label></th><td><select multiple="multiple" class="form-control" id="id_tags" name="tags">
26 <option value="3" selected="selected">大牛</option>
27 </select></td></tr>
28 <tr><th><label for="id_status">Status:</label></th><td><select class="form-control" id="id_status" name="status" required>
29 <option value="0">已报名</option>
30 <option value="1" selected="selected">未报名</option>
31 </select></td></tr>
32 <tr><th><label for="id_consultant">Consultant:</label></th><td><select class="form-control" id="id_consultant" name="consultant" required>
33 <option value="">---------</option>
34 <option value="1" selected="selected">三弗</option>
35 </select></td></tr>
36 <tr><th><label for="id_memo">Memo:</label></th><td><textarea class="form-control" cols="40" id="id_memo" name="memo" rows="10">
37 如图用户</textarea></td></tr>

细心的朋友会发现一个required,也会问这是什么鬼?哪来的?其实回头看看打印内容目的就是要看这个required属性。

在独立使用Form表单时,我们可以独立设置相关字段的参数,同样在MoldelForm中也是可以的,但是有一点是值得注意的: If the model field has blank=True, then required is set to False on the form field. Otherwise, required=True.这是官方的原话。意思是:如果在model中字段参数中设置了blank=True,那么Form中的required=False;若没设置blank=True,则默认为required=True(具体看源码的初始化函数设定)。

参考:Django官方文档和源码

现在,明白了required是怎么来的了吧!好了,开始该处理它了,只需要我们在模板文件中添加一个简单的判断和样式修饰:

 1 {# 显示名称 #}
 2 {% if field.field.required %}
 3     <label  class="col-sm-3 control-label" style="font-weight: normal">
 4             <span style="color: red">*</span>{{ field.label }}
 5     </label>
 6 {% else %}
 7     <label  class="col-sm-3 control-label" style="font-weight: normal">
 8         {{ field.label }}
 9 </label>
10 {% endif %}   

渲染后的效果:

 

2.3.3 添加保存按钮

基本的显示字段和样式添加的差不多了,接下来就是添加提交的按钮(保存)。在同一个form表单中添加如下: