mirror of
https://github.com/django/django.git
synced 2025-07-18 16:49:13 +00:00
[1.0.X] Fixed #10899 -- Ensured that log messages for deletions in the admin contain useful descriptions. Thanks to Jeremy Dunck for the patch.
Merge of r10686 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@10720 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
41ba8e7ca6
commit
8397c1fdfa
@ -46,7 +46,7 @@ class BaseModelAdmin(object):
|
|||||||
|
|
||||||
If kwargs are given, they're passed to the form Field's constructor.
|
If kwargs are given, they're passed to the form Field's constructor.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# If the field specifies choices, we don't need to look for special
|
# If the field specifies choices, we don't need to look for special
|
||||||
# admin widgets - we just need to use a select widget of some kind.
|
# admin widgets - we just need to use a select widget of some kind.
|
||||||
if db_field.choices:
|
if db_field.choices:
|
||||||
@ -79,17 +79,17 @@ class BaseModelAdmin(object):
|
|||||||
if isinstance(db_field, models.TimeField):
|
if isinstance(db_field, models.TimeField):
|
||||||
kwargs['widget'] = widgets.AdminTimeWidget
|
kwargs['widget'] = widgets.AdminTimeWidget
|
||||||
return db_field.formfield(**kwargs)
|
return db_field.formfield(**kwargs)
|
||||||
|
|
||||||
# For TextFields, add a custom CSS class.
|
# For TextFields, add a custom CSS class.
|
||||||
if isinstance(db_field, models.TextField):
|
if isinstance(db_field, models.TextField):
|
||||||
kwargs['widget'] = widgets.AdminTextareaWidget
|
kwargs['widget'] = widgets.AdminTextareaWidget
|
||||||
return db_field.formfield(**kwargs)
|
return db_field.formfield(**kwargs)
|
||||||
|
|
||||||
# For URLFields, add a custom CSS class.
|
# For URLFields, add a custom CSS class.
|
||||||
if isinstance(db_field, models.URLField):
|
if isinstance(db_field, models.URLField):
|
||||||
kwargs['widget'] = widgets.AdminURLFieldWidget
|
kwargs['widget'] = widgets.AdminURLFieldWidget
|
||||||
return db_field.formfield(**kwargs)
|
return db_field.formfield(**kwargs)
|
||||||
|
|
||||||
# For IntegerFields, add a custom CSS class.
|
# For IntegerFields, add a custom CSS class.
|
||||||
if isinstance(db_field, models.IntegerField):
|
if isinstance(db_field, models.IntegerField):
|
||||||
kwargs['widget'] = widgets.AdminIntegerFieldWidget
|
kwargs['widget'] = widgets.AdminIntegerFieldWidget
|
||||||
@ -104,7 +104,7 @@ class BaseModelAdmin(object):
|
|||||||
if isinstance(db_field, models.CharField):
|
if isinstance(db_field, models.CharField):
|
||||||
kwargs['widget'] = widgets.AdminTextInputWidget
|
kwargs['widget'] = widgets.AdminTextInputWidget
|
||||||
return db_field.formfield(**kwargs)
|
return db_field.formfield(**kwargs)
|
||||||
|
|
||||||
# For FileFields and ImageFields add a link to the current file.
|
# For FileFields and ImageFields add a link to the current file.
|
||||||
if isinstance(db_field, models.ImageField) or isinstance(db_field, models.FileField):
|
if isinstance(db_field, models.ImageField) or isinstance(db_field, models.FileField):
|
||||||
kwargs['widget'] = widgets.AdminFileWidget
|
kwargs['widget'] = widgets.AdminFileWidget
|
||||||
@ -281,56 +281,56 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
def get_formsets(self, request, obj=None):
|
def get_formsets(self, request, obj=None):
|
||||||
for inline in self.inline_instances:
|
for inline in self.inline_instances:
|
||||||
yield inline.get_formset(request, obj)
|
yield inline.get_formset(request, obj)
|
||||||
|
|
||||||
def log_addition(self, request, object):
|
def log_addition(self, request, object):
|
||||||
"""
|
"""
|
||||||
Log that an object has been successfully added.
|
Log that an object has been successfully added.
|
||||||
|
|
||||||
The default implementation creates an admin LogEntry object.
|
The default implementation creates an admin LogEntry object.
|
||||||
"""
|
"""
|
||||||
from django.contrib.admin.models import LogEntry, ADDITION
|
from django.contrib.admin.models import LogEntry, ADDITION
|
||||||
LogEntry.objects.log_action(
|
LogEntry.objects.log_action(
|
||||||
user_id = request.user.pk,
|
user_id = request.user.pk,
|
||||||
content_type_id = ContentType.objects.get_for_model(object).pk,
|
content_type_id = ContentType.objects.get_for_model(object).pk,
|
||||||
object_id = object.pk,
|
object_id = object.pk,
|
||||||
object_repr = force_unicode(object),
|
object_repr = force_unicode(object),
|
||||||
action_flag = ADDITION
|
action_flag = ADDITION
|
||||||
)
|
)
|
||||||
|
|
||||||
def log_change(self, request, object, message):
|
def log_change(self, request, object, message):
|
||||||
"""
|
"""
|
||||||
Log that an object has been successfully changed.
|
Log that an object has been successfully changed.
|
||||||
|
|
||||||
The default implementation creates an admin LogEntry object.
|
The default implementation creates an admin LogEntry object.
|
||||||
"""
|
"""
|
||||||
from django.contrib.admin.models import LogEntry, CHANGE
|
from django.contrib.admin.models import LogEntry, CHANGE
|
||||||
LogEntry.objects.log_action(
|
LogEntry.objects.log_action(
|
||||||
user_id = request.user.pk,
|
user_id = request.user.pk,
|
||||||
content_type_id = ContentType.objects.get_for_model(object).pk,
|
content_type_id = ContentType.objects.get_for_model(object).pk,
|
||||||
object_id = object.pk,
|
object_id = object.pk,
|
||||||
object_repr = force_unicode(object),
|
object_repr = force_unicode(object),
|
||||||
action_flag = CHANGE,
|
action_flag = CHANGE,
|
||||||
change_message = message
|
change_message = message
|
||||||
)
|
)
|
||||||
|
|
||||||
def log_deletion(self, request, object, object_repr):
|
def log_deletion(self, request, object, object_repr):
|
||||||
"""
|
"""
|
||||||
Log that an object has been successfully deleted. Note that since the
|
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
|
object is deleted, it might no longer be safe to call *any* methods
|
||||||
on the object, hence this method getting object_repr.
|
on the object, hence this method getting object_repr.
|
||||||
|
|
||||||
The default implementation creates an admin LogEntry object.
|
The default implementation creates an admin LogEntry object.
|
||||||
"""
|
"""
|
||||||
from django.contrib.admin.models import LogEntry, DELETION
|
from django.contrib.admin.models import LogEntry, DELETION
|
||||||
LogEntry.objects.log_action(
|
LogEntry.objects.log_action(
|
||||||
user_id = request.user.id,
|
user_id = request.user.id,
|
||||||
content_type_id = ContentType.objects.get_for_model(self.model).pk,
|
content_type_id = ContentType.objects.get_for_model(self.model).pk,
|
||||||
object_id = object.pk,
|
object_id = object.pk,
|
||||||
object_repr = object_repr,
|
object_repr = object_repr,
|
||||||
action_flag = DELETION
|
action_flag = DELETION
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def construct_change_message(self, request, form, formsets):
|
def construct_change_message(self, request, form, formsets):
|
||||||
"""
|
"""
|
||||||
Construct a change message from a changed object.
|
Construct a change message from a changed object.
|
||||||
@ -356,10 +356,10 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
'object': force_unicode(deleted_object)})
|
'object': force_unicode(deleted_object)})
|
||||||
change_message = ' '.join(change_message)
|
change_message = ' '.join(change_message)
|
||||||
return change_message or _('No fields changed.')
|
return change_message or _('No fields changed.')
|
||||||
|
|
||||||
def message_user(self, request, message):
|
def message_user(self, request, message):
|
||||||
"""
|
"""
|
||||||
Send a message to the user. The default implementation
|
Send a message to the user. The default implementation
|
||||||
posts a message using the auth Message object.
|
posts a message using the auth Message object.
|
||||||
"""
|
"""
|
||||||
request.user.message_set.create(message=message)
|
request.user.message_set.create(message=message)
|
||||||
@ -370,7 +370,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
the object is being changed, and False if it's being added.
|
the object is being changed, and False if it's being added.
|
||||||
"""
|
"""
|
||||||
return form.save(commit=False)
|
return form.save(commit=False)
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
"""
|
"""
|
||||||
Given a model instance save it to the database.
|
Given a model instance save it to the database.
|
||||||
@ -408,14 +408,14 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
"admin/%s/change_form.html" % app_label,
|
"admin/%s/change_form.html" % app_label,
|
||||||
"admin/change_form.html"
|
"admin/change_form.html"
|
||||||
], context, context_instance=template.RequestContext(request))
|
], context, context_instance=template.RequestContext(request))
|
||||||
|
|
||||||
def response_add(self, request, obj, post_url_continue='../%s/'):
|
def response_add(self, request, obj, post_url_continue='../%s/'):
|
||||||
"""
|
"""
|
||||||
Determines the HttpResponse for the add_view stage.
|
Determines the HttpResponse for the add_view stage.
|
||||||
"""
|
"""
|
||||||
opts = obj._meta
|
opts = obj._meta
|
||||||
pk_value = obj._get_pk_val()
|
pk_value = obj._get_pk_val()
|
||||||
|
|
||||||
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
|
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
|
||||||
# Here, we distinguish between different save types by checking for
|
# Here, we distinguish between different save types by checking for
|
||||||
# the presence of keys in request.POST.
|
# the presence of keys in request.POST.
|
||||||
@ -424,7 +424,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
if request.POST.has_key("_popup"):
|
if request.POST.has_key("_popup"):
|
||||||
post_url_continue += "?_popup=1"
|
post_url_continue += "?_popup=1"
|
||||||
return HttpResponseRedirect(post_url_continue % pk_value)
|
return HttpResponseRedirect(post_url_continue % pk_value)
|
||||||
|
|
||||||
if request.POST.has_key("_popup"):
|
if request.POST.has_key("_popup"):
|
||||||
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
|
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
|
||||||
# escape() calls force_unicode.
|
# escape() calls force_unicode.
|
||||||
@ -443,14 +443,14 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
else:
|
else:
|
||||||
post_url = '../../../'
|
post_url = '../../../'
|
||||||
return HttpResponseRedirect(post_url)
|
return HttpResponseRedirect(post_url)
|
||||||
|
|
||||||
def response_change(self, request, obj):
|
def response_change(self, request, obj):
|
||||||
"""
|
"""
|
||||||
Determines the HttpResponse for the change_view stage.
|
Determines the HttpResponse for the change_view stage.
|
||||||
"""
|
"""
|
||||||
opts = obj._meta
|
opts = obj._meta
|
||||||
pk_value = obj._get_pk_val()
|
pk_value = obj._get_pk_val()
|
||||||
|
|
||||||
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
|
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)}
|
||||||
if request.POST.has_key("_continue"):
|
if request.POST.has_key("_continue"):
|
||||||
self.message_user(request, msg + ' ' + _("You may edit it again below."))
|
self.message_user(request, msg + ' ' + _("You may edit it again below."))
|
||||||
@ -503,7 +503,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
form.save_m2m()
|
form.save_m2m()
|
||||||
for formset in formsets:
|
for formset in formsets:
|
||||||
self.save_formset(request, form, formset, change=False)
|
self.save_formset(request, form, formset, change=False)
|
||||||
|
|
||||||
self.log_addition(request, new_object)
|
self.log_addition(request, new_object)
|
||||||
return self.response_add(request, new_object)
|
return self.response_add(request, new_object)
|
||||||
else:
|
else:
|
||||||
@ -599,11 +599,11 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
form.save_m2m()
|
form.save_m2m()
|
||||||
for formset in formsets:
|
for formset in formsets:
|
||||||
self.save_formset(request, form, formset, change=True)
|
self.save_formset(request, form, formset, change=True)
|
||||||
|
|
||||||
change_message = self.construct_change_message(request, form, formsets)
|
change_message = self.construct_change_message(request, form, formsets)
|
||||||
self.log_change(request, new_object, change_message)
|
self.log_change(request, new_object, change_message)
|
||||||
return self.response_change(request, new_object)
|
return self.response_change(request, new_object)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = ModelForm(instance=obj)
|
form = ModelForm(instance=obj)
|
||||||
prefixes = {}
|
prefixes = {}
|
||||||
@ -624,7 +624,7 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets)
|
inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, fieldsets)
|
||||||
inline_admin_formsets.append(inline_admin_formset)
|
inline_admin_formsets.append(inline_admin_formset)
|
||||||
media = media + inline_admin_formset.media
|
media = media + inline_admin_formset.media
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'title': _('Change %s') % force_unicode(opts.verbose_name),
|
'title': _('Change %s') % force_unicode(opts.verbose_name),
|
||||||
'adminform': adminForm,
|
'adminform': adminForm,
|
||||||
@ -705,11 +705,11 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
if perms_needed:
|
if perms_needed:
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
obj_display = force_unicode(obj)
|
obj_display = force_unicode(obj)
|
||||||
obj.delete()
|
|
||||||
|
|
||||||
self.log_deletion(request, obj, obj_display)
|
self.log_deletion(request, obj, obj_display)
|
||||||
|
obj.delete()
|
||||||
|
|
||||||
self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(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):
|
if not self.has_change_permission(request, None):
|
||||||
return HttpResponseRedirect("../../../../")
|
return HttpResponseRedirect("../../../../")
|
||||||
return HttpResponseRedirect("../../")
|
return HttpResponseRedirect("../../")
|
||||||
@ -784,7 +784,7 @@ class InlineModelAdmin(BaseModelAdmin):
|
|||||||
self.verbose_name = self.model._meta.verbose_name
|
self.verbose_name = self.model._meta.verbose_name
|
||||||
if self.verbose_name_plural is None:
|
if self.verbose_name_plural is None:
|
||||||
self.verbose_name_plural = self.model._meta.verbose_name_plural
|
self.verbose_name_plural = self.model._meta.verbose_name_plural
|
||||||
|
|
||||||
def _media(self):
|
def _media(self):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
js = []
|
js = []
|
||||||
|
@ -6,7 +6,7 @@ from django.core.files import temp as tempfile
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.contrib.auth.models import User, Permission
|
from django.contrib.auth.models import User, Permission
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.admin.models import LogEntry
|
from django.contrib.admin.models import LogEntry, DELETION
|
||||||
from django.contrib.admin.sites import LOGIN_FORM_KEY
|
from django.contrib.admin.sites import LOGIN_FORM_KEY
|
||||||
from django.contrib.admin.util import quote
|
from django.contrib.admin.util import quote
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
@ -557,6 +557,9 @@ class AdminViewPermissionsTest(TestCase):
|
|||||||
post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict)
|
post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict)
|
||||||
self.assertRedirects(post, '/test_admin/admin/')
|
self.assertRedirects(post, '/test_admin/admin/')
|
||||||
self.failUnlessEqual(Article.objects.all().count(), 2)
|
self.failUnlessEqual(Article.objects.all().count(), 2)
|
||||||
|
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')
|
||||||
self.client.get('/test_admin/admin/logout/')
|
self.client.get('/test_admin/admin/logout/')
|
||||||
|
|
||||||
class AdminViewStringPrimaryKeyTest(TestCase):
|
class AdminViewStringPrimaryKeyTest(TestCase):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user