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

Ben*_*Ben 9 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()
Run Code Online (Sandbox Code Playgroud)

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

Sim*_*tte 8

对于您的特定情况,我只是覆盖queryset视图的属性以排除Team具有关联Games的s.

class TeamDeleteView(DeleteView):
    queryset = Team.objects.distinct().exclude(games__isnull=False)
Run Code Online (Sandbox Code Playgroud)

有一个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)
Run Code Online (Sandbox Code Playgroud)

编辑:

从您接受的解决方案看起来,您试图阻止在模型级别删除.这种强制执行应该通过使用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)
Run Code Online (Sandbox Code Playgroud)

您仍然需要处理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"
            )
Run Code Online (Sandbox Code Playgroud)

您甚至可以使用protected_objects属性e来显示更有意义的错误消息,就像管理员一样.


Est*_*ban 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
Run Code Online (Sandbox Code Playgroud)

FormView很不错,因为你可以利用form_invalid和clean方法,但是你仍然需要完成工作来获取对象,设置某种形式(在deleteview中不需要).

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

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


rud*_*dra 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)
Run Code Online (Sandbox Code Playgroud)

  • 这怎么可以成为公认的解决方案?这将导致错误500,因为您正在提出未处理的异常... (8认同)