在删除实例之前使用 DeleteView 进行验证
Validation using DeleteView before deleting instance
在删除对象之前通过一些验证来处理对象删除的最佳方法是什么?例如,在我的设置中有两个模型 - 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()
但后来我意识到基于 class 的视图 DeleteView 没有任何类型的 form_valid() 方法。我应该扩展通用 FormView 而不是 DeleteView 还是我缺少更好的方法?
对于您的特定情况,我将简单地覆盖您视图的 queryset
属性以排除 Team
与关联的 Game
。
class TeamDeleteView(DeleteView):
queryset = Team.objects.distinct().exclude(games__isnull=False)
有一个 Django ticket opened to make the DeleteView
behave like other form views but until 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"
)
您甚至可以像管理员一样使用 e
的 protected_objects
属性 来显示更有意义的错误消息。
在这种情况下,我同时使用了 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 和干净的方法,但是您仍然需要做一些工作来获取对象,设置某种形式(在 deleteview 中不需要)。
这真的是你想如何解决它的问题。其他一些问题是:您是否在 GET 上引发异常,或者您想显示一个漂亮的页面让用户知道他们无法删除该对象。这可以在两种视图类型中完成。
如果您还有更多要点,请更新您的问题,我会更新我的回复。
我认为最好的方法是覆盖模型的删除方法。例如:
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)
另一种方法是使用 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())
在删除对象之前通过一些验证来处理对象删除的最佳方法是什么?例如,在我的设置中有两个模型 - 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()
但后来我意识到基于 class 的视图 DeleteView 没有任何类型的 form_valid() 方法。我应该扩展通用 FormView 而不是 DeleteView 还是我缺少更好的方法?
对于您的特定情况,我将简单地覆盖您视图的 queryset
属性以排除 Team
与关联的 Game
。
class TeamDeleteView(DeleteView):
queryset = Team.objects.distinct().exclude(games__isnull=False)
有一个 Django ticket opened to make the DeleteView
behave like other form views but until 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"
)
您甚至可以像管理员一样使用 e
的 protected_objects
属性 来显示更有意义的错误消息。
在这种情况下,我同时使用了 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 和干净的方法,但是您仍然需要做一些工作来获取对象,设置某种形式(在 deleteview 中不需要)。
这真的是你想如何解决它的问题。其他一些问题是:您是否在 GET 上引发异常,或者您想显示一个漂亮的页面让用户知道他们无法删除该对象。这可以在两种视图类型中完成。
如果您还有更多要点,请更新您的问题,我会更新我的回复。
我认为最好的方法是覆盖模型的删除方法。例如:
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)
另一种方法是使用 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())