删除实例之前使用DeleteView进行验证

问题描述 投票:0回答:6

在删除对象之前进行一些验证来处理对象删除的最佳方法是什么? 例如,在我的设置中有两个模型 -

Game
Team
(它们显然是相关的)。 用户应该只能删除与任何游戏无关的团队。

我创建了一个用于删除团队的表单(没有任何字段)...

class TeamDeleteForm(ModelForm):
    class Meta:
        model = Team
        fields = []

    def clean(self):
        # Check to see if this team is tied to any existing games
        if self.instance.gameteams_set.exists():
            raise ValidationError("This team is tied to 1 or more games")
        return super().clean()

但后来我意识到基于类的视图DeleteView没有任何形式的form_valid()方法。 我应该扩展通用 FormView 而不是 DeleteView 还是有我缺少的更好方法?

django django-forms
6个回答
10
投票

我认为最好的方法是覆盖模型的删除方法。例如:

class Team(models.Model):
    ...
    def delete(self, *args, **kwargs):
        if Game.objects.filter(team__pk= self.pk).exists():
            raise Exception('This team is related to a game.')  # or you can throw your custom exception here.
        super(Team, self).delete(*args, **kwargs)

10
投票

对于您的特定情况,我只需覆盖视图的

queryset
属性即可排除
Team
及其关联的
Game

class TeamDeleteView(DeleteView):
    queryset = Team.objects.distinct().exclude(games__isnull=False)

打开了一张 Django 票证,以使

DeleteView
表现得像其他表单视图,但在 提议的补丁 合并并发布之前(它不会在 1.8 中实现),您必须完全覆盖
delete 
您的查看方法如下:

class TeamDeleteView(DeleteView):
    model = Team

    def delete(request, *args, **kwargs):
        self.object = self.get_object()
        if self.object.gameteams_set.exists():
            # Return the appropriate response
        success_url = self.get_success_url()
        self.object.delete()
        return HttpResponseRedirect(success_url)

编辑:

从您接受的解决方案来看,您似乎正在尝试防止模型级别的删除。应使用

PROTECT
on_delete
处理程序来完成此类强制执行。

from django.db import models

class Team(models.Model):
    pass

class Game(models.Model):
    team = models.ForeignKey(Team, on_delete=models.PROTECT)

您仍然需要处理您认为提出的

ProtectedError

from django.db import models
from django.http.response import HttpResponseForbidden

class TeamDeleteView(DeleteView):
    model = Team

    def delete(request, *args, **kwargs):
        try:
            return super(TeamDeleteView, self).delete(
                request, *args, **kwargs
            )
        except models.ProtectedError as e:
            # Return the appropriate response
            return HttpResponseForbidden(
                "This team is tied to 1 or more games"
            )

您甚至可以使用

protected_objects
e
属性来显示更有意义的错误消息,就像管理员一样。


7
投票

我在这个场景中同时使用了DeleteView 和FormView。两者各有优缺点。

DeleteView 很好,因为它基于 SingleObjectMixin,您可以轻松访问要删除的对象。一种很好的方法是在 get_object 中引发异常。这使得您可以在 get 和 post 上引发异常。

def get_object(self, qs):
  obj = super(FooView, self).get_object(qs)
  if obj.can_delete():
    return obj
  raise PermissionDenied

FormView 很好,因为您可以利用 form_invalid 和 clean 方法,但是您仍然需要完成获取对象的工作,设置某种表单(在 deleteview 中不需要)。

这实际上是一个你想如何解决它的问题。其他一些问题是:您是否在 GET 上引发异常,或者您是否想显示一个漂亮的页面,让用户知道他们无法删除该对象。这可以在两种视图类型中完成。

如果您有更多要点需要讨论,请更新您的问题,我会更新我的回复。


1
投票

另一种方法是使用 django.db IntegrityError!

from django.db import IntegrityError

class TeamDeleteView(DeleteView):
model = Team

    def delete(self, request, *args, **kwargs):
        """If DB Integrity Error, display msg and redirect to list"""
        try:
            return(super().delete(request, *args, **kwargs))
        except IntegrityError:
            messages.error(request, "This team is tied to 1 or more games")
            return render(request, template_name=self.template_name, context=self.get_context_data())

0
投票

重写模型的删除方法(这里的大多数答案都是这样做的)在 Django 4.0 中不再起作用。

根据

FormMixin
,POST请求的对象删除是 在
form_valid()
中处理。
delete()
处理程序中的自定义删除逻辑 应根据需要移动到
form_valid()
或共享辅助方法。

还有一个讨论这里


0
投票

Django 4.0 开始,实现删除逻辑的推荐方法从

delete()
移至
form_valid()
。 因此,以下是推荐的方式:

def form_valid(self, form):
    if form.is_valid():
        if self.object.gameteams_set.exists():
            # With field: None, we can specify non-field errors
            form.add_error(None, "This team is tied to 1 or more games.")
            return super().form_invalid(form)
        return super().form_valid(form)
    else:
        return super().form_invalid(form)
© www.soinside.com 2019 - 2024. All rights reserved.