django 在删除示例之前使用DeleteView进行验证

2o7dmzc5  于 2023-01-31  发布在  Go
关注(0)|答案(5)|浏览(185)

在删除一个对象之前进行验证的最佳方法是什么?例如,在我的设置中有两个模型-GameTeam(这两个模型显然是相关的)。用户应该只能删除没有绑定到任何游戏的团队。
我创建了一个表单(没有任何字段)用于删除团队...

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吗?或者我缺少一个更好的方法?

e4eetjau

e4eetjau1#

我认为最好的方法是重写模型的delete方法。

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)
lstz6jyr

lstz6jyr2#

对于您的特定情况,我将简单地覆盖视图的queryset属性,以排除具有关联的GameTeam

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

有一个Django ticket opened to make the DeleteView behave like other form views,但是在the proposed patch被合并和发布之前(它不会出现在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)

编辑:
从你接受的解决方案来看,你似乎试图在模型级别阻止删除,这样的强制应该通过使用PROTECTon_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"
            )

您甚至可以使用eprotected_objects属性来显示更有意义的错误消息,就像管理员所做的那样。

gxwragnw

gxwragnw3#

我在这个场景中使用了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时引发异常,或者你是否想显示一个漂亮的页面,让用户知道他们不能删除对象。这可以在两种视图类型中完成。
更新你的问题,如果你有更多的点去,我会更新我的回应。

rbl8hiat

rbl8hiat4#

另一种方法是使用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())
68de4m5k

68de4m5k5#

覆盖模型的delete方法(这里的大多数答案都是这样做的)在Django4.0中不再起作用。
根据FormMixin,POST请求的对象删除在form_valid()中处理。delete()处理程序中的自定义删除逻辑应根据需要移到form_valid()或共享帮助器方法中。
还有一个讨论here

相关问题