From 6ba64896622213ce44ace2c605e4eaafed1e9fc5 Mon Sep 17 00:00:00 2001 From: Jacob Kaplan-Moss Date: Sat, 9 Aug 2008 16:45:15 +0000 Subject: [PATCH] Broke the admin's use of LogEntry and user messages out into callbacks on ModelAdmin. Refs #6002. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8265 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/admin/options.py | 228 +++++++++++++++++++++----------- 1 file changed, 150 insertions(+), 78 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 364a1b98529..70a1b563b82 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -262,16 +262,9 @@ class ModelAdmin(BaseModelAdmin): super(ModelAdmin, self).__init__() def __call__(self, request, url): - # Check that LogEntry, ContentType and the auth context processor are installed. from django.conf import settings if settings.DEBUG: - from django.contrib.admin.models import LogEntry - if not LogEntry._meta.installed: - raise ImproperlyConfigured("Put 'django.contrib.admin' in your INSTALLED_APPS setting in order to use the admin application.") - if not ContentType._meta.installed: - raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in your INSTALLED_APPS setting in order to use the admin application.") - if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS: - raise ImproperlyConfigured("Put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.") + self.check_dependancies() # Delegate to the appropriate method, based on the URL. if url is None: @@ -285,6 +278,23 @@ class ModelAdmin(BaseModelAdmin): else: return self.change_view(request, unquote(url)) + def check_dependancies(self): + """ + Check that all things needed to run the admin have been correctly installed. + + The default implementation checks that LogEntry, ContentType and the + auth context processor are installed. + """ + from django.conf import settings + from django.contrib.admin.models import LogEntry + + if not LogEntry._meta.installed: + raise ImproperlyConfigured("Put 'django.contrib.admin' in your INSTALLED_APPS setting in order to use the admin application.") + if not ContentType._meta.installed: + raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in your INSTALLED_APPS setting in order to use the admin application.") + if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS: + raise ImproperlyConfigured("Put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.") + def _media(self): from django.conf import settings @@ -359,72 +369,60 @@ class ModelAdmin(BaseModelAdmin): def get_formsets(self, request, obj=None): for inline in self.inline_instances: yield inline.get_formset(request, obj) - - def save_add(self, request, form, formsets, post_url_continue): + + def log_addition(self, request, object): """ - Saves the object in the "add" stage and returns an HttpResponseRedirect. - - `form` is a bound Form instance that's verified to be valid. + Log that an object has been successfully added. + + The default implementation creates an admin LogEntry object. """ from django.contrib.admin.models import LogEntry, ADDITION - opts = self.model._meta - new_object = form.save(commit=True) - - if formsets: - for formset in formsets: - # HACK: it seems like the parent obejct should be passed into - # a method of something, not just set as an attribute - formset.instance = new_object - formset.save() - - pk_value = new_object._get_pk_val() - LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(self.model).id, pk_value, force_unicode(new_object), ADDITION) - msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)} - # Here, we distinguish between different save types by checking for - # the presence of keys in request.POST. - if request.POST.has_key("_continue"): - request.user.message_set.create(message=msg + ' ' + _("You may edit it again below.")) - if request.POST.has_key("_popup"): - post_url_continue += "?_popup=1" - return HttpResponseRedirect(post_url_continue % pk_value) - - if request.POST.has_key("_popup"): - return HttpResponse('' % \ - # escape() calls force_unicode. - (escape(pk_value), escape(new_object))) - elif request.POST.has_key("_addanother"): - request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) - return HttpResponseRedirect(request.path) - else: - request.user.message_set.create(message=msg) - # Figure out where to redirect. If the user has change permission, - # redirect to the change-list page for this object. Otherwise, - # redirect to the admin index. - if self.has_change_permission(request, None): - post_url = '../' - else: - post_url = '../../../' - return HttpResponseRedirect(post_url) - save_add = transaction.commit_on_success(save_add) - - def save_change(self, request, form, formsets=None): + LogEntry.objects.log_action( + user_id = request.user.pk, + content_type_id = ContentType.objects.get_for_model(object).pk, + object_id = object.pk, + object_repr = force_unicode(object), + action_flag = ADDITION + ) + + def log_change(self, request, object, message): """ - Saves the object in the "change" stage and returns an HttpResponseRedirect. - - `form` is a bound Form instance that's verified to be valid. - - `formsets` is a sequence of InlineFormSet instances that are verified to be valid. + Log that an object has been successfully changed. + + The default implementation creates an admin LogEntry object. """ from django.contrib.admin.models import LogEntry, CHANGE - opts = self.model._meta - new_object = form.save(commit=True) - pk_value = new_object._get_pk_val() - - if formsets: - for formset in formsets: - formset.save() - - # Construct the change message. + LogEntry.objects.log_action( + user_id = request.user.pk, + content_type_id = ContentType.objects.get_for_model(object).pk, + object_id = object.pk, + object_repr = force_unicode(object), + action_flag = CHANGE, + change_message = message + ) + + def log_deletion(self, request, object, object_repr): + """ + Log that an object has been successfully deleted. Note that since the + object is deleted, it might no longer be safe to call *any* methods + on the object, hence this method getting object_repr. + + The default implementation creates an admin LogEntry object. + """ + from django.contrib.admin.models import LogEntry, DELETION + LogEntry.objects.log_action( + user_id = request.user.id, + content_type_id = ContentType.objects.get_for_model(self.model).pk, + object_id = object.pk, + object_repr = object_repr, + action_flag = DELETION + ) + + + def construct_change_message(self, request, form, formsets): + """ + Construct a change message from a changed object. + """ change_message = [] if form.changed_data: change_message.append(_('Changed %s.') % get_text_list(form.changed_data, _('and'))) @@ -445,25 +443,98 @@ class ModelAdmin(BaseModelAdmin): % {'name': deleted_object._meta.verbose_name, 'object': deleted_object}) change_message = ' '.join(change_message) - if not change_message: - change_message = _('No fields changed.') - LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(self.model).id, pk_value, force_unicode(new_object), CHANGE, change_message) + return change_message or _('No fields changed.') + + def message_user(self, request, message): + """ + Send a message to the user. The default implementation + posts a message using the auth Message object. + """ + request.user.message_set.create(message=message) + + def save_add(self, request, form, formsets, post_url_continue): + """ + Saves the object in the "add" stage and returns an HttpResponseRedirect. + + `form` is a bound Form instance that's verified to be valid. + """ + opts = self.model._meta + new_object = form.save(commit=True) + + if formsets: + for formset in formsets: + # HACK: it seems like the parent obejct should be passed into + # a method of something, not just set as an attribute + formset.instance = new_object + formset.save() + + pk_value = new_object._get_pk_val() + self.log_addition(request, new_object) + + msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)} + # Here, we distinguish between different save types by checking for + # the presence of keys in request.POST. + if request.POST.has_key("_continue"): + self.message_user(request, msg + ' ' + _("You may edit it again below.")) + if request.POST.has_key("_popup"): + post_url_continue += "?_popup=1" + return HttpResponseRedirect(post_url_continue % pk_value) + + if request.POST.has_key("_popup"): + return HttpResponse('' % \ + # escape() calls force_unicode. + (escape(pk_value), escape(new_object))) + elif request.POST.has_key("_addanother"): + self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) + return HttpResponseRedirect(request.path) + else: + self.message_user(request, msg) + + # Figure out where to redirect. If the user has change permission, + # redirect to the change-list page for this object. Otherwise, + # redirect to the admin index. + if self.has_change_permission(request, None): + post_url = '../' + else: + post_url = '../../../' + return HttpResponseRedirect(post_url) + save_add = transaction.commit_on_success(save_add) + + def save_change(self, request, form, formsets=None): + """ + Saves the object in the "change" stage and returns an HttpResponseRedirect. + + `form` is a bound Form instance that's verified to be valid. + + `formsets` is a sequence of InlineFormSet instances that are verified to be valid. + """ + opts = self.model._meta + new_object = form.save(commit=True) + pk_value = new_object._get_pk_val() + + if formsets: + for formset in formsets: + formset.save() + + change_message = self.construct_change_message(request, form, formsets) + self.log_change(request, new_object, change_message) msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)} if request.POST.has_key("_continue"): - request.user.message_set.create(message=msg + ' ' + _("You may edit it again below.")) + self.message_user(request, msg + ' ' + _("You may edit it again below.")) if request.REQUEST.has_key('_popup'): return HttpResponseRedirect(request.path + "?_popup=1") else: return HttpResponseRedirect(request.path) elif request.POST.has_key("_saveasnew"): - request.user.message_set.create(message=_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': new_object}) + msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': new_object} + self.message_user(request, msg) return HttpResponseRedirect("../%s/" % pk_value) elif request.POST.has_key("_addanother"): - request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) + self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) return HttpResponseRedirect("../add/") else: - request.user.message_set.create(message=msg) + self.message_user(request, msg) return HttpResponseRedirect("../") save_change = transaction.commit_on_success(save_change) @@ -649,7 +720,6 @@ class ModelAdmin(BaseModelAdmin): def delete_view(self, request, object_id, extra_context=None): "The 'delete' admin view for this model." - from django.contrib.admin.models import LogEntry, DELETION opts = self.model._meta app_label = opts.app_label @@ -678,8 +748,10 @@ class ModelAdmin(BaseModelAdmin): raise PermissionDenied obj_display = str(obj) obj.delete() - LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(self.model).id, object_id, obj_display, DELETION) - request.user.message_set.create(message=_('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)}) + + self.log_deletion(request, obj, obj_display) + self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)}) + if not self.has_change_permission(request, None): return HttpResponseRedirect("../../../../") return HttpResponseRedirect("../../")