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

时间:2015-02-16 02:28:52

标签: django django-forms

在删除对象之前,通过某些验证处理删除对象的最佳方法是什么?例如,在我的设置中有两个模型 - 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,还是有更好的方法,我错过了?

4 个答案:

答案 0 :(得分:8)

对于您的特定情况,我只需覆盖您视图的queryset属性,即可排除TeamGame的{​​{1}}。

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

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

编辑:

从您接受的解决方案看起来您似乎试图阻止在模型级别删除。应通过使用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的{​​{1}}属性来显示更有意义的错误消息,就像管理员一样。

答案 1 :(得分: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上引发异常,或者您是否希望显示一个很好的页面,让用户知道他们无法删除该对象。这可以在两种视图类型中完成。

如果您有更多要点可以更新您的问题,我会更新我的回复。

答案 2 :(得分:7)

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

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)

答案 3 :(得分:0)

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