You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django项目File模型软删除触发RecycleBin参数错误问题

Django软删除File模型时RecycleBin创建报错

问题描述

在Django项目中,通过重写模型delete方法、设置is_deleted=True实现软删除,回收站模型RecycleBin在其他模型中运行正常,但操作File模型删除文件时,出现错误:

RecycleBin() got unexpected keyword arguments: 'title', 'content_type', 'object_id', 'deleted_by'

相关代码

RecycleBin模型

class RecycleBin(models.Model):
    title = models.CharField(
        max_length=1000, verbose_name="Title", null=False, blank=False,
    )
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    deleted_object = GenericForeignKey("content_type", "object_id")
    deleted_by = models.ForeignKey(
        "Accounts.User",
        verbose_name="Deleted By",
        on_delete=models.CASCADE,
        blank=False,
        null=False,
        related_name="deleted_by",
    )
    tenant = models.ForeignKey(
        "Tenants.Tenant",
        verbose_name="Tenant",
        max_length=1000,
        null=False,
        blank=False,
        on_delete=models.CASCADE,
    )
    date_deleted = models.DateTimeField(verbose_name="Date Deleted", auto_now_add=True)
    due_deletion_date = models.DateTimeField(verbose_name="Due Deletion", default=None, null=True, editable=False)

File模型及管理器

class AllFilesManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset()

class ActiveFileModelManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(is_deleted=False)

class File(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    name = models.CharField(max_length=1000, null=False, blank=False)
    folder = models.ForeignKey(
        "Folders.Folder", on_delete=models.CASCADE, null=False, blank=False
    )
    file = models.FileField(
        upload_to='tenants',
        storage=storage,
        max_length=1000,
        null=False, blank=False
    )
    is_deleted = models.BooleanField(default=False, verbose_name="Is Deleted?")
    created_by = models.ForeignKey(
        "Accounts.User",
        verbose_name="Created By",
        on_delete=models.CASCADE,
        blank=False,
        null=False,
        related_name="file_created_by",
    )
    modified_by = models.ForeignKey(
        "Accounts.User",
        verbose_name="Modified By",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
        related_name="file_modified_by",
    )
    tenant = models.ForeignKey(
        "Tenants.Tenant",
        verbose_name="Tenant",
        max_length=1000,
        on_delete=models.CASCADE,
        null=False,
        blank=False,
    )
    date_created = models.DateTimeField(verbose_name="Date Created", auto_now_add=True)
    date_modified = models.DateTimeField(verbose_name="Date Modified", auto_now=True)

    objects = ActiveFileModelManager()
    all_objects = AllFilesManager()

    def delete(self, using=None, keep_parents=False, user=None):
        bin = RecycleBin.objects.create(
            title=self.name,
            content_type=ContentType.objects.get_for_model(self),
            object_id=self.pk,
            deleted_by=self.modified_by,
            tenant=self.tenant,
        )
        if bin:
            self.is_deleted = True
            self.modified_by = user
            self.save()

FileAPIView视图

class FileAPIView(APIView):
    def delete(self, request, pk=None):
        ids = request.data.get("ids", [])
        if len(ids) == 0:
            try:
                file = get_object_or_404(File, id=pk)
                file.delete()
                return JsonResponse(
                    {
                        "status": status.HTTP_204_NO_CONTENT,
                        "message": "File moved to the Recycle Bin successfully!",
                    }
                )
            except Exception as e:
                return JsonResponse(
                    {
                        "status": status.HTTP_500_INTERNAL_SERVER_ERROR,
                        "message": "An error occurred while deleting the file!",
                        "results": {"error": str(e)},
                    }
                )
        else:
            try:
                for id in ids:
                    file = get_object_or_404(File, id=id)
                    file.delete()
                return JsonResponse(
                    {
                        "status": status.HTTP_204_NO_CONTENT,
                        "message": "Files moved to the Recycle Bin successfully!",
                    }
                )
            except Exception as e:
                return JsonResponse(
                    {
                        "status": status.HTTP_500_INTERNAL_SERVER_ERROR,
                        "message": "An error occurred while deleting the files!",
                        "results": {"error": str(e)},
                    }
                )

解决方法

问题核心

错误的本质是:

  1. API视图调用file.delete()时未传入当前用户参数,导致File.delete()方法中user为None;
  2. File模型的modified_by字段允许为null,若某个文件从未被修改过,self.modified_by就是None,而RecycleBin的deleted_by字段不允许为null,此时Django会将无效赋值转化为关键字参数不匹配的错误提示。

修复步骤

  1. 在API视图中传递当前用户
    修改FileAPIView里的file.delete()调用,传入当前请求的用户:

    # 单个文件删除
    file.delete(user=request.user)
    # 批量删除循环内
    file.delete(user=request.user)
    
  2. 修正File.delete()方法的deleted_by赋值逻辑
    优先使用传入的user作为删除人,避免modified_by为None的情况:

    def delete(self, using=None, keep_parents=False, user=None):
        # 确保deleted_by不为空,优先用传入的user, fallback到创建人
        deleted_user = user or self.created_by
        bin = RecycleBin.objects.create(
            title=self.name,
            content_type=ContentType.objects.get_for_model(self),
            object_id=self.pk,
            deleted_by=deleted_user,
            tenant=self.tenant,
        )
        if bin:
            self.is_deleted = True
            self.modified_by = user
            self.save()
    
  3. 可选:限制modified_by字段为非空
    如果业务要求文件必须有修改人,可以修改File模型的modified_by字段,移除null=True并设置默认值(可通过信号自动关联创建人):

    modified_by = models.ForeignKey(
        "Accounts.User",
        verbose_name="Modified By",
        on_delete=models.CASCADE,
        blank=False,
        null=False,
        related_name="file_modified_by",
    )
    

内容的提问来源于stack exchange,提问作者CoderS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:13:23