diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 02adc99953..712f07eec7 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -601,6 +601,12 @@ class ModelAdmin(BaseModelAdmin): """ obj.save() + def delete_model(self, requet, obj): + """ + Given a model instance delete it from the database. + """ + obj.delete() + def save_formset(self, request, form, formset, change): """ Given an inline formset save it to the database. @@ -1122,7 +1128,7 @@ class ModelAdmin(BaseModelAdmin): raise PermissionDenied obj_display = force_unicode(obj) self.log_deletion(request, obj, obj_display) - obj.delete() + self.delete_model(request, obj) self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)}) diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index ac517e868b..cc918be922 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -757,6 +757,12 @@ templates used by the :class:`ModelAdmin` views: ``ModelAdmin`` methods ---------------------- +.. warning:: + + :meth:`ModelAdmin.save_model` and :meth:`ModelAdmin.delete_model` must + save/delete the object, they are not for veto purposes, rather they allow + you to perform extra operations. + .. method:: ModelAdmin.save_model(self, request, obj, form, change) The ``save_model`` method is given the ``HttpRequest``, a model instance, @@ -770,6 +776,13 @@ For example to attach ``request.user`` to the object prior to saving:: obj.user = request.user obj.save() +.. method:: ModelAdmin.delete_model(self, request, obj) + +.. versionadded:: 1.3 + +The ``delete_model`` method is given the ``HttpRequest`` and a model instance. +Use this method to do pre- or post-delete operations. + .. method:: ModelAdmin.save_formset(self, request, form, formset, change) The ``save_formset`` method is given the ``HttpRequest``, the parent diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt index 1a939b0e3a..0e9290c37f 100644 --- a/docs/topics/db/multi-db.txt +++ b/docs/topics/db/multi-db.txt @@ -458,7 +458,7 @@ database other than that that specified by your router chain, you'll need to write custom :class:`~django.contrib.admin.ModelAdmin` classes that will direct the admin to use a specific database for content. -``ModelAdmin`` objects have four methods that require customization for +``ModelAdmin`` objects have five methods that require customization for multiple-database support:: class MultiDBModelAdmin(admin.ModelAdmin): @@ -469,6 +469,10 @@ multiple-database support:: # Tell Django to save objects to the 'other' database. obj.save(using=self.using) + def delete_model(self, requqest, obj): + # Tell Django to delete objects from the 'other' database + obj.delete(using=self.using) + def queryset(self, request): # Tell Django to look for objects on the 'other' database. return super(MultiDBModelAdmin, self).queryset(request).using(self.using) diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index b25a9b9a96..ee0f5d51a1 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -107,6 +107,25 @@ class ArticleAdmin(admin.ModelAdmin): modeladmin_year.admin_order_field = 'date' modeladmin_year.short_description = None + def delete_model(self, request, obj): + EmailMessage( + 'Greetings from a deleted object', + 'I hereby inform you that some user deleted me', + 'from@example.com', + ['to@example.com'] + ).send() + return super(ArticleAdmin, self).delete_model(request, obj) + + def save_model(self, request, obj, form, change=True): + EmailMessage( + 'Greetings from a created object', + 'I hereby inform you that some user created me', + 'from@example.com', + ['to@example.com'] + ).send() + return super(ArticleAdmin, self).save_model(request, obj, form, change) + + class CustomArticle(models.Model): content = models.TextField() date = models.DateTimeField() diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 4c96306332..a3fe34f0bf 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -3,6 +3,7 @@ import re import datetime from django.conf import settings +from django.core import mail from django.core.files import temp as tempfile from django.contrib.auth import admin # Register auth models with the admin. from django.contrib.auth.models import User, Permission, UNUSABLE_PASSWORD @@ -540,6 +541,8 @@ class AdminViewPermissionsTest(TestCase): post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertRedirects(post, '/test_admin/admin/') self.failUnlessEqual(Article.objects.all().count(), 4) + self.assertEquals(len(mail.outbox), 1) + self.assertEquals(mail.outbox[0].subject, 'Greetings from a created object') self.client.get('/test_admin/admin/logout/') # Super can add too, but is redirected to the change list view @@ -695,6 +698,8 @@ class AdminViewPermissionsTest(TestCase): post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict) self.assertRedirects(post, '/test_admin/admin/') self.failUnlessEqual(Article.objects.all().count(), 2) + self.assertEquals(len(mail.outbox), 1) + self.assertEquals(mail.outbox[0].subject, 'Greetings from a deleted object') article_ct = ContentType.objects.get_for_model(Article) logged = LogEntry.objects.get(content_type=article_ct, action_flag=DELETION) self.failUnlessEqual(logged.object_id, u'1') @@ -1440,8 +1445,6 @@ class AdminInheritedInlinesTest(TestCase): self.failUnlessEqual(BarAccount.objects.all()[0].username, "%s-1" % bar_user) self.failUnlessEqual(Persona.objects.all()[0].accounts.count(), 2) -from django.core import mail - class AdminActionsTest(TestCase): fixtures = ['admin-views-users.xml', 'admin-views-actions.xml']