I have a Django application that has a model like this:
class Foo(models.Model):
name = models.CharField(max_length=100)
I have a specific instance in this model that it's name is 'bar' (for example) and I want to prevent this instance from being deleted.
I've created a signal receiver like this:
def protect_foo_bar(sender, instance, using, **kwargs):
if instance.title != 'bar':
pass
else:
raise ProtectedError(protected_objects=instance, msg='You cannot delete this object')
and I've connected this receiver to pre_delete
signal like this:
pre_delete.connect(receiver=protect_foo_bar, dispatch_uid='protect_foo_bar_signal',
sender='app_name.foo')
When I try to delete this specific object from Django's admin panel, it returns an exception (Error 500). Is it possible to force the admin panel to show an error like you cannot delete this object
and not returning an exception to the user?
EDIT:
Here's the traceback:
Traceback:
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\core\handlers\exception.py" in inner
41. response = get_response(request)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\core\handlers\base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\core\handlers\base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\contrib\admin\options.py" in wrapper
551. return self.admin_site.admin_view(view)(*args, **kwargs)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\utils\decorators.py" in _wrapped_view
149. response = view_func(request, *args, **kwargs)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\views\decorators\cache.py" in _wrapped_view_func
57. response = view_func(request, *args, **kwargs)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\contrib\admin\sites.py" in inner
224. return view(request, *args, **kwargs)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\utils\decorators.py" in _wrapper
67. return bound_func(*args, **kwargs)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\utils\decorators.py" in _wrapped_view
149. response = view_func(request, *args, **kwargs)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\utils\decorators.py" in bound_func
63. return func.__get__(self, type(self))(*args2, **kwargs2)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\contrib\admin\options.py" in changelist_view
1584. response = self.response_action(request, queryset=cl.get_queryset(request))
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\contrib\admin\options.py" in response_action
1286. response = func(self, request, queryset)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\contrib\admin\actions.py" in delete_selected
49. queryset.delete()
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\db\models\query.py" in delete
614. deleted, _rows_count = collector.delete()
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\db\models\deletion.py" in delete
279. sender=model, instance=obj, using=self.using
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\dispatch\dispatcher.py" in send
193. for receiver in self._live_receivers(sender)
File "C:\Users\asus\PycharmProjects\AleTaha\env\lib\site-packages\django\dispatch\dispatcher.py" in <listcomp>
193. for receiver in self._live_receivers(sender)
File "C:/Users/asus/PycharmProjects/AleTaha\content\signals.py" in protect_content_category
8. raise ProtectedError(protected_objects=instance, msg='دستهی «همه» را نمیتوان حذف کرد.')
Exception Type: ProtectedError at /admin/content/contentcategory/
Exception Value: ('دسته\u200cی «همه» را نمی\u200cتوان حذف کرد.', <ContentCategory: همه>)
Yes, you can override the delete_view()
and response_action()
methods of your ModelAdmin
. I would also suggest to override the has_delete_permission()
method so that the "Delete" button won't appear at all:
class MyModelAdmin(admin.ModelAdmin):
def delete_view(self, request, object_id, extra_context=None):
try:
return super().delete_view(request, object_id, extra_context)
except ProtectedError:
msg = "you cannot delete this object"
self.message_user(request, msg, messages.ERROR)
opts = self.model._meta
return_url = reverse(
'admin:%s_%s_change' % (opts.app_label, opts.model_name),
args=(object_id,),
current_app=self.admin_site.name,
)
return HttpResponseRedirect(return_url)
def response_action(self, request, queryset):
try:
return super().response_action(request, queryset)
except ProtectedError:
msg = "you cannot delete this object"
self.message_user(request, msg, messages.ERROR)
opts = self.model._meta
return_url = reverse(
'admin:%s_%s_changelist' % (opts.app_label, opts.model_name),
current_app=self.admin_site.name,
)
return HttpResponseRedirect(return_url)
def has_delete_permission(self, request, obj=None)
return super().has_delete_permission(request, obj) and (
not obj or obj.name != 'bar'
)