前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >django优雅的实现软删除,支持Admin和DRF的软删除

django优雅的实现软删除,支持Admin和DRF的软删除

原创
作者头像
MicLon
发布于 2023-03-11 04:45:26
发布于 2023-03-11 04:45:26
2.7K00
代码可运行
举报
文章被收录于专栏:python-进阶python-进阶
运行总次数:0
代码可运行

何为软删除

当你想对数据进行删除的时候,如果使用物理删除,那么数据真的消失了。使用软删除,可以让数据保留,但是不会被真的删除。只是在字段上设置了一个值,表示数据已经被删除。

需要解决的问题

  • DRF
    • 暴露DELETE方法一旦被执行,就需要操作软删除,把is_deleted字段设置为True。
    • 同样的,DRF对外操作的其他接口,如查询,修改操作,就不允许找到已经软删除的数据。
  • 自带的Admin
    • 既然是超级管理后台,那么就允许操作任何数据,包括已经软删除的,而不是列表找不到软删除的数据。
    • 后台执行删除操作的时候,实际上是对数据进行软删除。

简而言之:

  1. drf找不到删除的数据,admin需要全部数据
  2. drf和admin删除数据都是软删除

解决方案

DRF

Django Manager 赋予了 Django的模型(Model)中操作数据库的能力。如果你还未能了解Manager,可以先去官方文档^first查阅。

其实你在项目中无时不刻不在使用Manager,还记得objects吗?也就是如:Book.objects.all()中的objects。有没有想过它到底是什么?

显然,默认的模型Manager并不能解决我们的问题,所以我们需要自定义模型的Manager。

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
class ModelManager(models.Manager):
    # 重写get_queryset方法
    def get_queryset(self):
        # 查询出所有的数据,但是不包括软删除的数据
        return super().get_queryset().filter(is_deleted=False)

这样,最简单的自定义模型Manager就完成了。我们需要把它挂载到需要的模型上。

我们格局打开,将拥有is_deleted属性的模型抽离成抽象模型基类,凡是继承此类的都自带这个Manager。

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
class BaseModel(models.Model):
    """
    模型基类
    """
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_deleted = models.BooleanField(default=False)

    class Meta:
        abstract = True
        ordering = ['-created_at', '-updated_at']
    # 替换默认的objects
    objects = ModelManager()

不仅如此,刚刚只是过滤了软删除数据,我们还需要将接口删除的操作,进行软删除,而不是真删除。

使用DRF操作删除实际上调用的是mixins.DestroyModelMixindestroy方法,具体执行删除的方法是perform_destroy

所以下一步我们需要重写这个perform_destroy方法。

回到视图层(views),重写:

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
# views.py
class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()

    def perform_destroy(self, instance):
        instance.is_deleted = True
        instance.save(update_fields=['is_deleted'])

OK,在DRF层面上,我们解决了软删除的处理。即:

  1. drf找不到删除的数据
  2. drf执行删除是软删除

Admin

首先再刚刚代码基础上,我们启用Admin,进入后台看看效果。

可以发现,由于模型Manager的加持,直接把is_deleted的数据一并过滤了。但是我们并不想如此。

所以第一反应,就是去注册模型的地方,重写模型的查询。

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    pass

这是原来的模型注册,笔者进入admin.ModelAdmin中翻阅源码,发现get_queryset方法是执行获取查询的,那么把它重写了。

那……应该重写成什么?由于我们已经在模型层通过Manager直接改变了最初的数据过滤后的样子,这里怎么重写也是无事于补的。

于是我在想,那就在定义一个模型管理器,在Admin中使用这个管理器不就好了?

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
class ModelAdminManager(models.Manager):
    pass

class BaseModel(models.Model):
    ...

    objects = ModelManager()
    objects_all = ModelAdminManager()
    # 如果仅仅是空的Manager, 可以直接写成objects_all = models.Manager()

回到Admin注册中,重写get_queryset

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):

    def get_queryset(self, request):
        return Book.objects_all.all()

剩下最后一个问题,在admin后台执行删除的时候,是软删除。当下如果执行删除是真正的物理删除数据。

此时问题就变得简单,Manager进阶用法中,可以自定义其QuerySet^second

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
class DeleteQuerySet(models.QuerySet):
    def delete(self):
        self.update(is_deleted=True)

# 修改原来的objects_all
class BaseModel(models.Model):
    ...

    objects = ModelManager()
    # objects_all = ModelAdminManager.from_queryset(DeleteQuerySet)()
    # or
    objects_all = models.Manager.from_queryset(DeleteQuerySet)()

改完后在admin进行删除操作:

OK,在Admin层面上,我们解决了软删除的处理。即:

  1. admin能够展示被软删除的数据
  2. admin执行删除是软删除

完整代码:

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
#admin.py
from apps.book.models import Book


@admin.register(Book)
class BookAdmin(admin.ModelAdmin):

    list_display = ('title', 'is_deleted')

    def get_queryset(self, request):
        return Book.objects_all.all()
代码语言:python
代码运行次数:0
运行
AI代码解释
复制
# models.py
from django.db import models


# Create your models here.
class DeleteQuerySet(models.QuerySet):
    def delete(self):
        self.update(is_deleted=True)


class ModelManager(models.Manager):
    _queryset_class = DeleteQuerySet

    def get_queryset(self):
        return self._queryset_class(
            model=self.model, using=self._db, hints=self._hints
        ).filter(is_deleted=False)


class ModelAdminManager(models.Manager):
    pass
    # _queryset_class = DeleteQuerySet


class BaseModel(models.Model):
    """
    BaseModel
    """
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_deleted = models.BooleanField(default=False, verbose_name='软删除')

    class Meta:
        abstract = True
        ordering = ['-created_at', '-updated_at']

    objects = ModelManager()
    # objects_all = ModelAdminManager.from_queryset(DeleteQuerySet)()
    objects_all = models.Manager.from_queryset(DeleteQuerySet)()
代码语言:python
代码运行次数:0
运行
AI代码解释
复制
# views.py
from rest_framework.viewsets import ModelViewSet


class BookViewSet(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def perform_destroy(self, instance):
        instance.is_deleted = True
        instance.save(update_fields=['is_deleted'])

参考:

Manager

Manager QuerySet

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python中的chdir函数:更改工作目录利器
在Python中,`chdir`是一个内置函数,用于更改当前工作目录。今天就给大家简单介绍一下该函数的用法和一些注意事项,一起来学习一下吧。
华科云商小彭
2023/10/07
6380
Python中的chdir函数:更改工作目录利器
python进入文件目录 命令_python创建目录
Python可以使用os.chdir()方法转到指定目录。os.chdir() 方法用于改变当前工作目录到指定的路径。
全栈程序员站长
2022/09/27
4K0
python-获取当前工作路径
sys.argv[0] import sys print sys.argv[0]#获得的是当前执行脚本的位置(若在命令行执行的该命令,则为空) 运行结果(在python脚本中执行的结果): F:/SEG/myResearch/myProject_2/test.py os模块 import os print os.getcwd()#获得当前工作目录 print os.path.abspath('.')#获得当前工作目录 print os.path.abspath('..')#获得当前工作目录的父
py3study
2020/01/10
3.3K0
【Python让生活更美好01】os与shutil模块的常用方法总结
Python作为一种解释型的高级语言,脚本语言,又被称作“胶水语言”,就是因为其灵活的语法和其依靠浩如烟海的第三方包实现的丰富多彩的功能,而os和shutil就是这样一种功能强大的模块,可以非常快捷地帮助使用者解决日常计算机文件相关操作的一些问题,下面就对os和shutil包中一些常见的命令进行说明: import os,shutil os.getcwd()#获取当前工作目录 os.chdir('newdir')#改变当前工作目录 os.mkdir('test')#在当前工作目录下生成单级文件夹 o
Feffery
2018/04/17
7692
python 获取当前目录和文件
import os print os.getcwd() #获取当前工作目录路径 print os.path.abspath('.') #获取当前工作目录路径 print os.path.abspath('test.txt') #获取当前目录文件下的工作目录路径 print os.path.abspath('..') #获取当前工作的父目录 !注意是父目录路径 print os.path.abspath(os.curdir) #获取当前工作目录路径
用户5760343
2019/07/05
3.8K0
python os模块 --- 操作系统接口模块
1.OS模块下的常见函数 # os模块 # os.sep:取代操作系统特定的路径分隔符 # os.getcwd:得到当前工作目录,即当前python脚本工作的目录路径。 # os.getenv()和os.putenv:分别用来读取和设置环境变量 # os.listdir():返回指定目录下的所有文件和目录名 # os.remove(file):删除一个文件 # os.stat(file):获得文件属性 # os.mkdir(name):创建目录 # os.rmdir(name):删除目录 # os.exit
诡途
2020/10/16
6260
python之OS模块(对文件or目录操
  os,语义为操作系统,包含普遍的操作系统功能,与具体的平台无关。python编程时,处理文件和目录这些操作,就比如说:显示当前目录下所有文件/删除某个文件/获取文件大小……
py3study
2020/01/09
6770
Python基础教程(十四):OS 文件/目录方法
💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 💝💝💝如有需要请大家订阅我的专栏【Python系列】哟!我会定期更新相关系列的文章 💝💝💝关注!关注!!请关注!!!请大家关注下博主,您的支持是我不断创作的最大动力!!!
用户11147438
2024/06/14
1680
Python基础语法-内置函数和模块-os模块
os 模块提供了一些函数来与操作系统进行交互,比如获取当前工作目录、创建目录、删除目录、重命名文件等等。在本文中,我们将详细介绍 os 模块的常用函数。
玖叁叁
2023/04/18
3190
python模块—os
    OS模块也是我们平时工作中很常用到的一个模块,通过os模块调用系统命令,获得路劲,获取操作系统的类型等都是使用该模块。
py3study
2020/01/13
1.1K0
python之获取文件os模块
  Python 是一门易于学习、功能强大的编程语言。它提供了高效的高级数据结构,还能简单有效地面向对象编程。Python 优雅的语法和动态类型以及解释型语言的本质,使它成为多数平台上写脚本和快速开发应用的理想语言。下面我们来介绍一下python的文件os模块相关知识。
jiankang666
2022/12/05
4490
python之获取文件os模块
Python绝对路径和相对路径详解
每个运行在计算机上的程序,都有一个“当前工作目录”(或 cwd)。所有没有从根文件夹开始的文件名或路径,都假定在当前工作目录下。
用户8442333
2021/11/30
3.5K0
Python修改文件后缀名[通俗易懂]
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/09
6.2K0
python 如何查看与更换工作目录
# -*- coding: utf-8 -*- #导入OS包 import os #os.getcwd()查看当前目录 print('\n当前目录位置:') print(os.getcwd()) #os.chdir修改目录 os.chdir("E:\python_py\python1_learn") print('\n修改后目录位置:') print(os.getcwd())
学到老
2019/02/14
9200
python学习_24(目录)
os.curdir 返回当前目录 >>> os.curdir '.' os.pardir 返回当前目录的父目录 >>> os.pardir '..' #切换到上级目录 >>> os.chdir(os.pardir) >>> os.getcwd() 'E:\\' os.getcwd() 获取当前工作目录 >>> os.getcwd() 'E:\\' >>> os.chdir("个人") >>> os.getcwd() 'E:\\个人' os.chdir(path) 切换到指定工作目录 >>> os.c
py3study
2020/01/14
6230
(三十九) 初遇python之Context Managers上下文管理器
各位读者大大们大家好,今天学习python的Context Managers上下文管理器,并记录学习过程欢迎大家一起交流分享。
XXXX-user
2019/07/23
5710
(三十九) 初遇python之Context Managers上下文管理器
小朋友学Python(18):目录
Python的os模块有许多方法能帮你创建,删除和更改目录。 一、创建目录 mkdir()方法 可以使用os模块的mkdir()方法在当前目录下创建新的目录们。你需要提供一个包含了要创建的目录名称的参数。 语法: os.mkdir("dirname") 例1:在当前目录下创建一个新目录testdir import os os.mkdir("testdir") 二、获取当前目录 getcwd()方法 getcwd()方法显示当前的工作目录。 语法: os.getcwd() 注意,这里cwd是current w
海天一树
2018/04/17
6670
Python-os-01-获取当前文件所在文件夹路径
系统:Windows 7 语言版本:Anaconda3-4.3.0.1-Windows-x86_64 编辑器:pycharm-community-2016.3.2
zishendianxia
2019/10/23
1.5K0
python3基础:目录操作
os模块提供了统一的操作系统接口函数,python中对文件和文件夹的操作都要涉及到os和shutil模块,在使用前需要使用import引入,例如;
py3study
2020/01/09
1.3K0
Python3目录操作
输出: 'D:\python\jupyter' 'D:\python' ['.ipynb_checkpoints', 'bak', 'jupyter', 'project', 'test.txt', 'testdir', 'Untitled.ipynb', ]
py3study
2020/01/03
7960
相关推荐
Python中的chdir函数:更改工作目录利器
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验