四、对数据记录删除
1、首先是配置整个流程框架,使整个流程运转顺利:
路由项添加:path('
主要是匹配记录的id和delete字符串。
视图函数:
- def rec_obj_delete(req,app_name,table_name,id_num):
- print('========++++++++++++',app_name,table_name,id_num)
- return render(req,"mytestapp/rec_delete.html",{})
前端页面rec_deleta.html
在修改页面中增加删除按钮,
关键点是其href

点击删除按钮,匹配路由项,调用视图函数rec_obj_delete,跳转到rec_delete.html页面。
2、确定视图函数的处理逻辑,确定前端页面的显示内容
对于Django Admin提供的删除功能,当点击删除时,提示如下:

列示出当前记录关联的记录,给删除者以提示,确定后,再进行删除。
rec_delete.html网页和后端视图函数就是合作完成关联记录的取得和展示
前端:rec_delete.html
- {% extends 'base.html' %}
- {% load tags %}
-
- {% block mybody %}
- <body>
- <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
- <a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">我的客户管理系统a>
- <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse" data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
- <span class="navbar-toggler-icon">span>
- button>
- <ul class="navbar-nav px-3">
- <li class="nav-item text-nowrap">
- <a class="nav-link" href="#">{{ request.user.userprofile.name }}a>
- li>
- ul>
- nav>
- <div class="container-fluid" style="margin-top: 20px;">
- <div class="flex-column bd-highlight">
- {% display_obj_related model_obj %}
- <form method="post">{% csrf_token %}
- <input type="submit" class="btn btn-danger" value="Yes,I'm Sure">
- <input type="hidden" value="yes" name="del_confirm">
- <input type="hidden" value="{{ model_obj.id }}" name="del_id">
- <input type=button class="btn btn-info" value="No,Take Me Back" onclick="window.history.go(-1);">
- form>
- div>
- div>
- body>
- {% endblock %}
视图函数:
- def rec_obj_delete(req,app_name,table_name,id_num):
- admin_class = mytestapp_admin.enable_admins[app_name][table_name]
- model_obj = admin_class.model.objects.get(id=id_num)
- if req.method == "POST":
- model_obj.delete()
- return redirect("/mytestapp/%s/%s/" %(app_name,table_name))
- return render(req,"mytestapp/rec_delete.html",{"model_obj":model_obj,
- "admin_class":admin_class})
关键点在display_obj_related自定义标签的编写,对model_obj这个记录的关联数据进行查找,并形成列表,组装成html代码,返回给前端。
- @register.simple_tag
- def display_obj_related(model_obj):
- # 把对象及其所有相关联的数据取出来
-
- return mark_safe(recursive_related_objs_lookup(model_obj))
-
- def recursive_related_objs_lookup(model_objs):
- '''组装对象及关联数据为列表标签'''
- ul_ele = "
"
- for model_obj in model_objs:
- li_ele = '
- %s:%s
' % (model_obj._meta.verbose_name,model_obj.__str__().strip("<>")) - # 上面是先将自身信息输出
- ul_ele += li_ele
- for m2m_field in model_obj._meta.local_many_to_many:
- # 把所有跟这个对象直接关联的m2m字段选出,是models中ManyToManyField字段,即tags字段
- sub_ul_ele = "
"
- m2m_field_obj = getattr(model_obj,m2m_field.name)
- for obj in m2m_field_obj.select_related():
- li_ele = '
- %s:%s'
% (m2m_field.verbose_name,obj.__str__().strip("<>")) - sub_ul_ele += li_ele
- sub_ul_ele += ""
- ul_ele += sub_ul_ele
- for related_obj in model_obj._meta.related_objects:
- # 这个循环是要把与对象关联的related_objects,即所有ManyToOneRel的数据列出,即customerfollowup和enrollment字段
- if 'ManyToManyRel' in related_obj.__repr__():
- if hasattr(model_obj,related_obj.get_accessor_name()):
- accessor_obj = getattr(model_obj,related_obj.get_accessor_name())
- # accessor_obj相当于customer.enrollment_set
- if hasattr(accessor_obj,'select_related'): # select_related()== all()
- target_objs = accessor_obj.select_related() # .filter(**filter_conditions)
- # target_obj相当于customer.enrollment_set.all()
- sub_ul_ele = "
"
- for o in target_objs:
- li_ele = '
- %s:%s
' % (o._meta.verbose_name,o.__str__().strip('<>')) - sub_ul_ele += li_ele
- sub_ul_ele += ""
- ul_ele += sub_ul_ele
- elif hasattr(model_obj,related_obj.get_accessor_name()):
- accessor_obj = getattr(model_obj,related_obj.get_accessor_name())
- if hasattr(accessor_obj,'select_related'):
- target_objs = accessor_obj.select_related()
- else:
- target_objs = accessor_obj
- if len(target_objs)>0:
- nodes = recursive_related_objs_lookup(target_objs)
- ul_ele += nodes
- ul_ele += ""
- return ul_ele
找关联的数据,主要是找以此表为外键的表,如对于客户表,要删除一条记录时,要判断以客户表为外键的其他表的影响,如果其他表以客户表为外键,并且,在其他表中存在了以这条记录作为外键的记录,那么删除记录,对其他表是由影响的。如删除客户表中id为1的记录,而报名表中有一条或几条记录是以这个客户为外键的,删除这条记录,报名表中对应的这几条记录如何处理?这就是关联影响,在创建models时,有一个on_deleted,表明在本表记录删除时,援引此表为外键的其他表的处理方式。相应的还有多对多和一对一关系,上面的程序主要就是找这些记录进行显示。
五、自定义动作

实现如上图的功能,定制动作,选中后,批量执行这个动作。
在DjangoAdmin中,在AdminClass中定义actions:
actions = ['test_actions',]
def test_actions(self,arg1,arg2):
print("actions:::::=>",self,'|',arg1,'|',arg2)
结果:

打印:
actions:::::=> plcrm.CustomerAdmin |
在test_actions中可以对选中的各条记录进行任意的处理。
模拟上述功能,先在前端页面增加action下拉列表框,实现每条记录前增加复选框,实现复选框的全选和全不选:
在table_objts.html中:
- <hr style="width: 100%">
- <div class="row" style="padding-left: 10px;">
- <div style="float:left;margin-left:20px;">
- <select id="action_list" name="myaction" style="width: 200px;height: 25px;">
- <option value="">----------------option>
- {% for op in admin_class.actions %}
- <option value="{{ op }}">{{ op }}option>
- {% endfor %}
- select>
- div>
- <div class="col-sm">
- <input type="button" value="Go" onclick="ActionSubmit();">
- div>
-
- <thead>
- <tr class="text-danger" style="background-color: #9fcdff;">
- <th colspan="6"><input type="checkbox" onclick="CheckAllToggle(this);">th>
- {{ header_order_tag | safe }}
- tr>
- thead>
在build_table_row自定义标签中,每行数据前加上复选框:
row_ele = row_ele +'
前端实现全选与全取消功能:即CheckAllToggle(this):
- function CheckAllToggle(ele) {
- if ($(ele).prop("checked")){
- $("input[my_id='obj_checkbox']").prop("checked",true);
- } else {
- $("input[my_id='obj_checkbox']").prop("checked",false);
- }
- }
效果如下:

点击Go按钮,将下列列表框的值和下面记录的值传递到后端,后端通过下拉列表框的值,找到对应的函数,有此函数处理传递的记录。
使用form提交,对下列列表框和Go按钮改造,做成form,这样,下拉列表框数据可自动提交,要想办法将记录数据添加到form表单中,进行提交。
- <div class="row" style="padding-left: 10px;">
- <form method="post" onsubmit="ActionSubmit(this)">
- <div style="float:left;margin-left:20px;">
- <select id="action_list" name="myaction" style="width: 200px;height: 25px;">
- <option value="">----------------option>
- {% for op in admin_class.actions %}
- <option value="{{ op }}">{{ op }}option>
- {% endfor %}
- select>
- div>
- {% csrf_token %}
- <input type="hidden" name="del_confirm" value="yes"/>
- <div style="float: left;margin-left: 10px">
- <input type="submit" value="Go">
- div>
- form>
-
- 。。。
- <script>
- function CheckAllToggle(ele) {
- if ($(ele).prop("checked")){
- $("input[my_id='obj_checkbox']").prop("checked",true);
- } else {
- $("input[my_id='obj_checkbox']").prop("checked",false);
- }
- }
- function ActionSubmit(form_ele) {
- var selected_ids = [];
- $("input[my_id='obj_checkbox']:checked").each(function () {
- selected_ids.push($(this).val());
- });
- var selected_action = $('#action_list').val();
- console.log(selected_ids);
- console.log(selected_action);
- if(selected_ids.length == 0){
- alert("no object got selected!");
- }
- if(!selected_action){
- alert("no action got selected!");
- }
- var selected_ids_ele = "toString() +">"
- $(form_ele).append(selected_ids_ele)
- }
- script>
-
后台处理还借助display_table_objs视图函数:
- def display_table_objs(req,app_name,table_name):
- print("====>",req.POST)
- # models_module = importlib.import_module('%s.models' %(app_name))
- # model_obj = getattr(models_module,"Customer")
- # print("------->>>",model_obj)
- url_path = req.path
- current_order_mark = req.GET.get('order_mark','0')
- current_page = int(req.GET.get('p',1))
- current_search_mark = req.GET.get('search_mark',"")
- admin_class = mytestapp_admin.enable_admins[app_name][table_name]
- if req.method == "POST":
- select_ids = req.POST.get('selected_ids')
- select_ids = select_ids.split(',')
- actions = req.POST.get('myaction')
- action_obj = getattr(admin_class,actions)
- models_objs = admin_class.model.objects.filter(id__in=select_ids)
- action_obj(admin_class,req,models_objs)
-
- # models_data = admin_class.model.objects.values_list(*admin_class.list_display)
- filter_f_a = {} # 保存传递过来的顾虑项键值对
- for filter_f in admin_class.list_filter:
- f_temp = req.GET.get(filter_f)
- if f_temp:
- filter_f_a[filter_f] = f_temp
-
- filter_url = ""
- # 过滤条件形成请求地址的参数串
- if filter_f_a.__len__() != 0:
- for k,v in filter_f_a.items():
- filter_url = filter_url + k + "=" + str(v) +"&"
- # 有查询,查询条件增加到请求地址的参数中
- if current_search_mark.__len__()>0:
- filter_url = filter_url +'search_mark=' + current_search_mark +'&'
- print(filter_url)
-
- order_url = ""
- if current_order_mark == '0':
- order_url = filter_url
- else:
- order_url = order_url + "order_mark=" + current_order_mark +"&"
-
- per_page = admin_class.list_per_page
- if current_search_mark.__len__() == 0:
- total_count = admin_class.model.objects.filter(**filter_f_a).count()
- else:
- q_and = Q()
- q_or = Q()
- q_or.connector = 'OR'
- for column in admin_class.list_search:
- q_or.children.append(("%s__contains"%column,current_search_mark))
- q_and.connector = "AND"
-
- total_count = admin_class.model.objects.filter(**filter_f_a).filter(q_or).count()
-
- print('url_path:',url_path)
- obj_page = PageHelper(total_count,current_page,url_path,page_rec_count=per_page,filter_url=filter_url,order_url=order_url)
- page_tag = obj_page.pager_tag_str()
-
- filter_tag = myutils.built_filter_tag(admin_class,filter_f_a,url_path,current_search_mark)
-
- header_order_tag = myutils.order_tag(admin_class,url_path,filter_url,current_order_mark)
-
- # models_data_page = models_data[obj_page.page_rec_start:obj_page.page_rec_end]
- # print('>>>>>>>>',models_data_page)
- return render(req,"mytestapp/table_objs.html",{"admin_class":admin_class,"model_class_name":admin_class.model.__name__,
- 'page_tag':page_tag,'obj_page':obj_page,'total_count':total_count,
- 'filter_tag':filter_tag,'filter_f_a':filter_f_a,'header_order_tag':header_order_tag,
- 'order_mark':current_order_mark,'search_mark':current_search_mark,'url_path':url_path
关键点看req.method == ‘POST’段:
在这里接收POST提交过来的数据,获得动作的对象,获得models对象,调用动作对象函数,执行自定义的函数。
- class CustomerAdmin(BaseAdmin):
- list_display = ['qq','name','phone','source','referral_from','consult_course','tags','status']
- list_per_page = 4
- list_filter = ['qq','source','status','consult_course','tags']
- list_search = ['qq','name']
- filter_horizontal = ['tags']
- actions = ['delete_action',]
- def delete_action(self,req,model_objs):
- print("运行delete_action",self,req,model_objs)
- model_objs.delete()
这样就完成了自定义动作的执行。
实际上,在前面我们已经做过删除的功能了,这里可以借助这个功能,就是在自定义的delete_action中获取数据后,跳转到rec_delete.html,给出这些记录的关联数据,然后就衔接到上面的删除功能上。
- def delete_action(self,req,model_objs):
- print("运行delete_action",self,req,model_objs)
- # 跳转到rec_delete.html,借助已经实现的功能,调用rec_obj_delete(req,app_name,table_name,id_num):
- # return render(req,'mytestapp/rec_delete.html',{}) #需要改造,匹配rec_obj_delete的参数
- model_objs.delete()
- print("删除执行完毕")
关键点就是页面之间跳转时,参数要保证也传递过去。