• Python入门自学进阶-Web框架——27、DjangoAdmin项目应用-数据记录操作2


    四、对数据记录删除

    1、首先是配置整个流程框架,使整个流程运转顺利:

    路由项添加:path('///delete/',views.rec_obj_delete,name='rec_delete'),

    主要是匹配记录的id和delete字符串。

    视图函数:

    1. def rec_obj_delete(req,app_name,table_name,id_num):
    2. print('========++++++++++++',app_name,table_name,id_num)
    3. 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

    1. {% extends 'base.html' %}
    2. {% load tags %}
    3. {% block mybody %}
    4. <body>
    5. <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
    6. <a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">我的客户管理系统a>
    7. <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">
    8. <span class="navbar-toggler-icon">span>
    9. button>
    10. <ul class="navbar-nav px-3">
    11. <li class="nav-item text-nowrap">
    12. <a class="nav-link" href="#">{{ request.user.userprofile.name }}a>
    13. li>
    14. ul>
    15. nav>
    16. <div class="container-fluid" style="margin-top: 20px;">
    17. <div class="flex-column bd-highlight">
    18. {% display_obj_related model_obj %}
    19. <form method="post">{% csrf_token %}
    20. <input type="submit" class="btn btn-danger" value="Yes,I'm Sure">
    21. <input type="hidden" value="yes" name="del_confirm">
    22. <input type="hidden" value="{{ model_obj.id }}" name="del_id">
    23. <input type=button class="btn btn-info" value="No,Take Me Back" onclick="window.history.go(-1);">
    24. form>
    25. div>
    26. div>
    27. body>
    28. {% endblock %}

    视图函数:

    1. def rec_obj_delete(req,app_name,table_name,id_num):
    2. admin_class = mytestapp_admin.enable_admins[app_name][table_name]
    3. model_obj = admin_class.model.objects.get(id=id_num)
    4. if req.method == "POST":
    5. model_obj.delete()
    6. return redirect("/mytestapp/%s/%s/" %(app_name,table_name))
    7. return render(req,"mytestapp/rec_delete.html",{"model_obj":model_obj,
    8. "admin_class":admin_class})

    关键点在display_obj_related自定义标签的编写,对model_obj这个记录的关联数据进行查找,并形成列表,组装成html代码,返回给前端。

    1. @register.simple_tag
    2. def display_obj_related(model_obj):
    3. # 把对象及其所有相关联的数据取出来
    4. return mark_safe(recursive_related_objs_lookup(model_obj))
    5. def recursive_related_objs_lookup(model_objs):
    6. '''组装对象及关联数据为列表标签'''
    7. ul_ele = "
        "
    8. for model_obj in model_objs:
    9. li_ele = '
    10. %s:%s
    11. ' % (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中:

    1. <hr style="width: 100%">
    2. <div class="row" style="padding-left: 10px;">
    3. <div style="float:left;margin-left:20px;">
    4. <select id="action_list" name="myaction" style="width: 200px;height: 25px;">
    5. <option value="">----------------option>
    6. {% for op in admin_class.actions %}
    7. <option value="{{ op }}">{{ op }}option>
    8. {% endfor %}
    9. select>
    10. div>
    11. <div class="col-sm">
    12. <input type="button" value="Go" onclick="ActionSubmit();">
    13. div>
    14. <thead>
    15. <tr class="text-danger" style="background-color: #9fcdff;">
    16. <th colspan="6"><input type="checkbox" onclick="CheckAllToggle(this);">th>
    17. {{ header_order_tag | safe }}
    18. tr>
    19. thead>

     在build_table_row自定义标签中,每行数据前加上复选框:
    row_ele = row_ele +''%(row_data.id)

    前端实现全选与全取消功能:即CheckAllToggle(this):

    1. function CheckAllToggle(ele) {
    2. if ($(ele).prop("checked")){
    3. $("input[my_id='obj_checkbox']").prop("checked",true);
    4. } else {
    5. $("input[my_id='obj_checkbox']").prop("checked",false);
    6. }
    7. }

    效果如下:

     点击Go按钮,将下列列表框的值和下面记录的值传递到后端,后端通过下拉列表框的值,找到对应的函数,有此函数处理传递的记录。

    使用form提交,对下列列表框和Go按钮改造,做成form,这样,下拉列表框数据可自动提交,要想办法将记录数据添加到form表单中,进行提交。

    1. <div class="row" style="padding-left: 10px;">
    2. <form method="post" onsubmit="ActionSubmit(this)">
    3. <div style="float:left;margin-left:20px;">
    4. <select id="action_list" name="myaction" style="width: 200px;height: 25px;">
    5. <option value="">----------------option>
    6. {% for op in admin_class.actions %}
    7. <option value="{{ op }}">{{ op }}option>
    8. {% endfor %}
    9. select>
    10. div>
    11. {% csrf_token %}
    12. <input type="hidden" name="del_confirm" value="yes"/>
    13. <div style="float: left;margin-left: 10px">
    14. <input type="submit" value="Go">
    15. div>
    16. form>
    17. 。。。
    18. <script>
    19. function CheckAllToggle(ele) {
    20. if ($(ele).prop("checked")){
    21. $("input[my_id='obj_checkbox']").prop("checked",true);
    22. } else {
    23. $("input[my_id='obj_checkbox']").prop("checked",false);
    24. }
    25. }
    26. function ActionSubmit(form_ele) {
    27. var selected_ids = [];
    28. $("input[my_id='obj_checkbox']:checked").each(function () {
    29. selected_ids.push($(this).val());
    30. });
    31. var selected_action = $('#action_list').val();
    32. console.log(selected_ids);
    33. console.log(selected_action);
    34. if(selected_ids.length == 0){
    35. alert("no object got selected!");
    36. }
    37. if(!selected_action){
    38. alert("no action got selected!");
    39. }
    40. var selected_ids_ele = "toString() +">"
    41. $(form_ele).append(selected_ids_ele)
    42. }
    43. script>

     后台处理还借助display_table_objs视图函数:

    1. def display_table_objs(req,app_name,table_name):
    2. print("====>",req.POST)
    3. # models_module = importlib.import_module('%s.models' %(app_name))
    4. # model_obj = getattr(models_module,"Customer")
    5. # print("------->>>",model_obj)
    6. url_path = req.path
    7. current_order_mark = req.GET.get('order_mark','0')
    8. current_page = int(req.GET.get('p',1))
    9. current_search_mark = req.GET.get('search_mark',"")
    10. admin_class = mytestapp_admin.enable_admins[app_name][table_name]
    11. if req.method == "POST":
    12. select_ids = req.POST.get('selected_ids')
    13. select_ids = select_ids.split(',')
    14. actions = req.POST.get('myaction')
    15. action_obj = getattr(admin_class,actions)
    16. models_objs = admin_class.model.objects.filter(id__in=select_ids)
    17. action_obj(admin_class,req,models_objs)
    18. # models_data = admin_class.model.objects.values_list(*admin_class.list_display)
    19. filter_f_a = {} # 保存传递过来的顾虑项键值对
    20. for filter_f in admin_class.list_filter:
    21. f_temp = req.GET.get(filter_f)
    22. if f_temp:
    23. filter_f_a[filter_f] = f_temp
    24. filter_url = ""
    25. # 过滤条件形成请求地址的参数串
    26. if filter_f_a.__len__() != 0:
    27. for k,v in filter_f_a.items():
    28. filter_url = filter_url + k + "=" + str(v) +"&"
    29. # 有查询,查询条件增加到请求地址的参数中
    30. if current_search_mark.__len__()>0:
    31. filter_url = filter_url +'search_mark=' + current_search_mark +'&'
    32. print(filter_url)
    33. order_url = ""
    34. if current_order_mark == '0':
    35. order_url = filter_url
    36. else:
    37. order_url = order_url + "order_mark=" + current_order_mark +"&"
    38. per_page = admin_class.list_per_page
    39. if current_search_mark.__len__() == 0:
    40. total_count = admin_class.model.objects.filter(**filter_f_a).count()
    41. else:
    42. q_and = Q()
    43. q_or = Q()
    44. q_or.connector = 'OR'
    45. for column in admin_class.list_search:
    46. q_or.children.append(("%s__contains"%column,current_search_mark))
    47. q_and.connector = "AND"
    48. total_count = admin_class.model.objects.filter(**filter_f_a).filter(q_or).count()
    49. print('url_path:',url_path)
    50. obj_page = PageHelper(total_count,current_page,url_path,page_rec_count=per_page,filter_url=filter_url,order_url=order_url)
    51. page_tag = obj_page.pager_tag_str()
    52. filter_tag = myutils.built_filter_tag(admin_class,filter_f_a,url_path,current_search_mark)
    53. header_order_tag = myutils.order_tag(admin_class,url_path,filter_url,current_order_mark)
    54. # models_data_page = models_data[obj_page.page_rec_start:obj_page.page_rec_end]
    55. # print('>>>>>>>>',models_data_page)
    56. return render(req,"mytestapp/table_objs.html",{"admin_class":admin_class,"model_class_name":admin_class.model.__name__,
    57. 'page_tag':page_tag,'obj_page':obj_page,'total_count':total_count,
    58. 'filter_tag':filter_tag,'filter_f_a':filter_f_a,'header_order_tag':header_order_tag,
    59. 'order_mark':current_order_mark,'search_mark':current_search_mark,'url_path':url_path

    关键点看req.method == ‘POST’段:

    在这里接收POST提交过来的数据,获得动作的对象,获得models对象,调用动作对象函数,执行自定义的函数。

    1. class CustomerAdmin(BaseAdmin):
    2. list_display = ['qq','name','phone','source','referral_from','consult_course','tags','status']
    3. list_per_page = 4
    4. list_filter = ['qq','source','status','consult_course','tags']
    5. list_search = ['qq','name']
    6. filter_horizontal = ['tags']
    7. actions = ['delete_action',]
    8. def delete_action(self,req,model_objs):
    9. print("运行delete_action",self,req,model_objs)
    10. model_objs.delete()

    这样就完成了自定义动作的执行。

    实际上,在前面我们已经做过删除的功能了,这里可以借助这个功能,就是在自定义的delete_action中获取数据后,跳转到rec_delete.html,给出这些记录的关联数据,然后就衔接到上面的删除功能上。

    1. def delete_action(self,req,model_objs):
    2. print("运行delete_action",self,req,model_objs)
    3. # 跳转到rec_delete.html,借助已经实现的功能,调用rec_obj_delete(req,app_name,table_name,id_num):
    4. # return render(req,'mytestapp/rec_delete.html',{}) #需要改造,匹配rec_obj_delete的参数
    5. model_objs.delete()
    6. print("删除执行完毕")

    关键点就是页面之间跳转时,参数要保证也传递过去。

  • 相关阅读:
    产品探秘:智影AI——你的创意视频制作神器!
    JVM-GC
    C++中noncopyable不可拷贝类的使用
    数字图像处理(九)双边滤波
    uniapp中unicloud接入支付宝订阅消息完整教程
    单片机常识篇
    python安装与配置
    Keepalived LVS群集
    Java多线程进阶——CAS与synchronized优化
    部署LVS-DR 集群及实验
  • 原文地址:https://blog.csdn.net/kaoa000/article/details/127923524