Validation using DeleteView before deleting instance
I think the best approach will be overriding the model's delete method. For example:
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)
For your particular case I would simply override the queryset
attribute of your view to exclude Team
s with associated Game
s.
class TeamDeleteView(DeleteView):
queryset = Team.objects.distinct().exclude(games__isnull=False)
There's a Django ticket opened to make the DeleteView
behave like other form views but until the proposed patch is merged and released (It won't make it in 1.8) you'll have to completely override the delete
method of your view like the following:
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)
Edit:
From your accepted solution it looks like you're trying to prevent deletion at the model level. Such enforcement should be done by using a PROTECT
on_delete
handler.
from django.db import models
class Team(models.Model):
pass
class Game(models.Model):
team = models.ForeignKey(Team, on_delete=models.PROTECT)
You'll still have to deal with the raised ProtectedError
in your view:
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"
)
You could even use the protected_objects
property of e
to display a more meaningful error message just like the admin does.
I've used both DeleteView and FormView for this scenario. Both have their pros and cons.
DeleteView is nice because it's based on the SingleObjectMixin and you can easily get access to the object you want to delete. One nice way of doing this is raising an exception in get_object. This makes it so that you can raise exception on both get and post.
def get_object(self, qs):
obj = super(FooView, self).get_object(qs)
if obj.can_delete():
return obj
raise PermissionDenied
FormView is nice because you can leverage form_invalid and the clean methods, but then you still have to do the work to get the object, setup some sort of form (not needed in deleteview).
It's really a matter of how you want to tackle it. Some other questions are: do you raise an exception on GET, or do you want to show a nice page letting the user know they can't delete the object. This can be done in both View types.
Update your question if you have more points to go over and i'll update my response.