11👍
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)
10👍
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.
- Multi-Tenant Django Application
- How to use the admin autocomplete field in a custom form?
- Django sekizai {% addtoblock %} tag is not working properly
- Deploying Django to Elastic Beanstalk, migrations failed
- Django rest framework model serializers – read nested, write flat
7👍
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.
- Django — async_to_sync vs asyncio.run
- Django admin many-to-many intermediary models using through= and filter_horizontal
- How passing string on filter keyword to Django Objects Model?
- Store browser tab specific data
- Django: Create and save a model using JSON data
1👍
One other way to do it would be to work with 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())
- Foreign Key to User model
- Getting file extension in Django template
- How to mention password field in serializer?
0👍
Overriding the model’s delete method (most of the answers here do that) does not work anymore in Django 4.0.
In accordance with
FormMixin
, object deletion for POST requests is
handled inform_valid()
. Custom delete logic indelete()
handlers
should be moved toform_valid()
, or a shared helper method, as needed.
There is also a discussion here.
- Matplotlib: interactive plot on a web server
- Django import export – Unable to import model with BinaryField
- Django update on queryset to change ID of ForeignKey