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

news2025/8/6 10:03:55

四、对数据记录删除

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

路由项添加:path('<str:app_name>/<str:table_name>/<int:id_num>/delete/',views.rec_obj_delete,name='rec_delete'),

主要是匹配记录的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

在修改页面中增加删除按钮,<div class="col-sm-2"><button type="button" class="btn btn-danger pull-left"><a href="/mytestapp/plcrm/customer/{{ form_obj.instance.id }}/delete/">Delete</a></button></div>

关键点是其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 = "<ul>"
    for model_obj in model_objs:
        li_ele = '<li>%s:%s</li>' % (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 = "<ul>"
            m2m_field_obj = getattr(model_obj,m2m_field.name)
            for obj in m2m_field_obj.select_related():
                li_ele = '<li>%s:%s' % (m2m_field.verbose_name,obj.__str__().strip("<>"))
                sub_ul_ele += li_ele
            sub_ul_ele += "</ul>"
            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 = "<ul style='color:red'>"
                            for o in target_objs:
                                li_ele = '<li>%s:%s</li>' % (o._meta.verbose_name,o.__str__().strip('<>'))
                                sub_ul_ele += li_ele
                            sub_ul_ele += "</ul>"
                            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 += "</ul>"
    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 | <WSGIRequest: POST '/admin/plcrm/customer/'> | <QuerySet [<Customer: 123654789>, <Customer: 1234352354352>, <Customer: 8899009898>]>

在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 +'<tr><td colspan="6"><input my_id="obj_checkbox" type="checkbox" value="%s"</td>'%(row_data.id)

前端实现全选与全取消功能:即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 = "<input name='selected_ids' type='hidden' value=" +selected_ids.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("删除执行完毕")

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/34610.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode每日一题:809.情感丰富的文字~~~双指针+计数器】

题目描述 有时候人们会用重复写一些字母来表示额外的感受&#xff0c;比如 “hello” -> “heeellooo”, “hi” -> “hiii”。我们将相邻字母都相同的一串字符定义为相同字母组&#xff0c;例如&#xff1a;“h”, “eee”, “ll”, “ooo”。 对于一个给定的字符串 S…

MyBatis核心对象简介说明

转自: MyBatis核心对象简介说明 MyBatis 有三个基本要素&#xff1a; 核心接口和类MyBatis核心配置文件&#xff08;mybatis-config.xml&#xff09;SQL映射文件&#xff08;mapper.xml&#xff09;下面首先介绍 MyBatis 的核心接口和类&#xff0c;如下图 每个 MyBatis 应…

华为云会议网络研讨会,按次订购更方便!

如今&#xff0c;云会议已经成为日常办公协作的常态&#xff0c;但在线发布会、大型培训会、大型招聘会等大型会议&#xff0c;常规云会议由于会议容量有限、人多会控难、角色单一等技术限制&#xff0c;已经无法满足大型会议的需求&#xff0c;此时&#xff0c;网络研讨会便能…

【电商】电商后台系统整体介绍

电商后台系统支撑了电商企业亿万级的交易量&#xff0c;其重要性不言而喻。本文章将从电商后台系统的的各个模块来分析&#xff0c;让大家的后台真正”硬“起来。 当前关于产品经理的文章主要偏向方法论、业界动向、产品分析、用户体验、交互等&#xff0c;关于后台系统的文章比…

m在simulink进行DS-CDMA建模,然后通过MATLAB调用simulink模型进行误码率仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 研究信道编码在噪声和衰落信道条件下传输和保护数据或图像类型源的性能。在工作于AWGN和瑞利衰落信道&#xff08;可根据需要选择信道类型&#xff09;的Simulink中创建二进制PSK&#x…

Metabase学习教程:视图-5

用地图可视化数据 如何使用标注地图、区域地图和网格地图来可视化Metabase中的数据。 本文介绍如何在Metabase中使用地图来可视化数据。本文中使用的美国地图是使用示例数据库每次安装Metabase时都会用到。 地图类型 Metabase具有三种地图类型&#xff1a; 标注地图标记特…

【Spring(五)】引入篇:一文带你弄懂AOP的底层原理(动态代理)

有关Spring的所有文章都收录于我的专栏&#xff1a;&#x1f449;Spring&#x1f448; 目录 一、前言 二、使用AOP需要的依赖 三、引入 四、AOP的底层原理之动态代理 五、总结 相关文章 【Spring&#xff08;一&#xff09;】如何获取对象&#xff08;Bean&#xff09;【Sprin…

[附源码]SSM计算机毕业设计民宿客栈管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于QT封装海康SDK(MvCameraControl),访问控制海康相机

1、封装类HkCameraApi,此类可以访问控制海康的网络相机、USB相机,可以同时采集多台相机的视频、控制设置的各种属性设置。 2、采集效果如下: 3.、该类已经上传,下载地址:基于C++封装HkCameraApi类,用于访问控制海康相机-C++文档类资源-CSDN下载 4、HkCameraApi类的头文…

【附源码】计算机毕业设计JAVA疫情下的居民管理系统

【附源码】计算机毕业设计JAVA疫情下的居民管理系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA…

算法图解学习4 递归

random recording 随心记录 What seems to us as bitter trials are often blessings in disguise. 看起来对我们痛苦的试炼&#xff0c;常常是伪装起来的好运。 递归 背景导入 在一个盒中盒找钥匙 第一种实现方法&#xff0c;如下图 第二种方法&#xff0c;如图 两种方法伪代…

6.jQuery中的Ajax上传文件

目录 1 上传文件 2 loading效果 1 上传文件 后端接到数据后保存在upload_file文件夹下 前端依然使用FormData处理文件 contentType:false的意思是 使用FormData默认的Content-Type值 processData:false的意思是 不对FormData中的数据进行url编码&#xff0c;而是将Form…

与专业安全厂商相比,戴尔做安全的优势是什么?

上文介绍了戴尔大谈零信任架构的原因&#xff0c;也提到了现代安全的三大要素&#xff0c;分别为&#xff1a;信任的基础、简化的零信任采纳和网络恢复计划。事实上&#xff0c;戴尔作为全球大型IT基础设施提供商&#xff0c;能提供多种网络安全能力来构建现代安全&#xff0c;…

当你碰到了MySQL中的死锁,你了解这些机制吗?

MySQL死锁怎么来的&#xff1f; 当两个及以上的事务&#xff0c;双方都在等待对方释放已经持有的锁或因为加锁顺序不一致造成循环等待锁资源&#xff0c;就会出现“死锁”。 总结一下生产死锁的4个条件&#xff1a; 两个或者两个以上事务 每个事务都已经持有锁并且申请新的锁…

[附源码]java毕业设计游戏战队考核系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Baklib帮助中心:自助服务指南

根据 Social endurance 的调查&#xff0c;64%的客户希望在 Twitter 上发帖一小时内得到回复&#xff0c;85%的客户希望公司在6小时内回复。 虽然这种客户期望的趋势几乎适用于所有行业&#xff0c;但某些行业——如电子商务应用程序——的流量正创下历史新高。大多数公司可能…

如何在矩池云上安装语音识别模型 Whisper

如何在矩池云上安装语音识别模型 Whisper Whisper 是 OpenAI 近期开源的一个语音识别的模型&#xff0c;研究人员基于 680,000 小时的标记音频数据进行训练&#xff0c;它同时也是一个多任务模型&#xff0c;可以进行多语言语音识别以及语音翻译任务&#xff0c;可以将语音音频…

【CVPR 2022】QueryDet:加速高分辨率小目标检测

大连不负众望&#xff0c;疫情了&#xff0c;我们又封校了&#xff0c;可能初步封个5678天&#xff0c;微笑jpg 论文地址&#xff1a;https://arxiv.org/pdf/2103.09136.pdf 项目地址&#xff1a;https://github.com/ ChenhongyiYang/QueryDet-PyTorch 1. 简介 背景&#xf…

java中的线程池

文章目录前言线程池的优点线程池的实现原理线程池的创建线程池提交任务线程池的关闭合理配置线程池线程池的监控总结前言 在处理一些比较复杂或者费时的任务的时候&#xff0c;我们常常会选择多线程的方式去处理。那么怎么创建多个线程呢&#xff0c;当然不可能是一个一个创建…

微信如何制作自己的小程序【微信小程序】

小程序在微信的生态中&#xff0c;一直是比较受到企业商家的欢迎&#xff0c; 由于小程序的制作门槛比较低&#xff0c;开发人员可以根据自己喜欢的风格进行定制开发。 微信平台上&#xff0c;很多商家都在为制作小程序而发愁。那么微信如何制作自己的小程序呢&#xff1f; 一…