From 5ed512383640e56e8dd95a08b2ddf14119523c6b Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 13:00:14 -0700 Subject: [PATCH 01/24] started on history --- djadmin2/templatetags/admin2_tags.py | 8 ++++++++ .../djadmin2theme_default/includes/history.html | 10 ++++++++++ .../templates/djadmin2theme_default/index.html | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html diff --git a/djadmin2/templatetags/admin2_tags.py b/djadmin2/templatetags/admin2_tags.py index 622946c3..a3952691 100644 --- a/djadmin2/templatetags/admin2_tags.py +++ b/djadmin2/templatetags/admin2_tags.py @@ -136,3 +136,11 @@ def render(context, model_instance, attribute_name): # It must be a method instead. field = None return renderer(value, field) + + +@register.inclusion_tag('djadmin2theme_default/includes/history.html', + takes_context=True) +def action_history(context): + from django.contrib.admin.models import LogEntry + actions = LogEntry.objects.filter(user__pk=context['user'].pk) + return {'actions': actions} diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html new file mode 100644 index 00000000..e3934141 --- /dev/null +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html @@ -0,0 +1,10 @@ +{% if actions %} +
    + {% for action in actions %} +
  1. {{ action.change_message }}
  2. + {% endfor %} +
+{% else %} +

None available

+{% endif %} + diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/index.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/index.html index 914d4e25..b2749e32 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/index.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/index.html @@ -12,7 +12,7 @@

{% trans "Recent Actions" %}

{% trans "My Actions" %}
- TODO + {% action_history %}
{% endblock content %} From fb0e73a37edb081f907c7ac185f111c142afa53f Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 13:04:47 -0700 Subject: [PATCH 02/24] PEP8 --- djadmin2/views.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/djadmin2/views.py b/djadmin2/views.py index 1617d054..b5c9baaf 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -81,8 +81,8 @@ def post(self, request): selected_model_pks = request.POST.getlist('selected_model_pk') queryset = self.model.objects.filter(pk__in=selected_model_pks) - # If action_callable is a class subclassing from actions.BaseListAction - # then we generate the callable object. + # If action_callable is a class subclassing from + # actions.BaseListAction then we generate the callable object. if hasattr(action_callable, "process_queryset"): response = action_callable.as_view(queryset=queryset)(request) else: @@ -130,7 +130,8 @@ def get_queryset(self): search_term = self.request.GET.get('q', None) search_use_distinct = False if self.model_admin.search_fields and search_term: - queryset, search_use_distinct = self.get_search_results(queryset, search_term) + queryset, search_use_distinct = self.get_search_results( + queryset, search_term) if self.model_admin.list_filter: queryset = self.build_list_filter(queryset).qs @@ -185,7 +186,8 @@ def get_context_data(self, **kwargs): return context def get_success_url(self): - view_name = 'admin2:{}_{}_index'.format(self.app_label, self.model_name) + view_name = 'admin2:{}_{}_index'.format( + self.app_label, self.model_name) return reverse(view_name) def get_actions(self): @@ -208,7 +210,8 @@ class ModelDetailView(AdminModel2Mixin, generic.DetailView): permissions.ModelViewPermission) -class ModelEditFormView(AdminModel2Mixin, Admin2ModelFormMixin, extra_views.UpdateWithInlinesView): +class ModelEditFormView(AdminModel2Mixin, Admin2ModelFormMixin, + extra_views.UpdateWithInlinesView): """Context Variables :model: Type of object you are editing @@ -229,7 +232,8 @@ def get_context_data(self, **kwargs): return context -class ModelAddFormView(AdminModel2Mixin, Admin2ModelFormMixin, extra_views.CreateWithInlinesView): +class ModelAddFormView(AdminModel2Mixin, Admin2ModelFormMixin, + extra_views.CreateWithInlinesView): """Context Variables :model: Type of object you are editing From 4cfe10e64537557a90bdc5b55895faa7cda92f3d Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 13:05:56 -0700 Subject: [PATCH 03/24] actual data in history template --- .../djadmin2theme_default/includes/history.html | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html index e3934141..04e879b1 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html @@ -1,7 +1,18 @@ {% if actions %}
    {% for action in actions %} -
  1. {{ action.change_message }}
  2. +
  3. + {% if action.is_addition %} + + + {% elif action.is_change %} + ~ + {% else %} + - + {% endif %} + {% with action.get_edited_object as object %} + {{ object }} + {% endwith %} +
  4. {% endfor %}
{% else %} From 76ab676530be00e9ba9af50b41bae8df88d0dbf0 Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 13:40:50 -0700 Subject: [PATCH 04/24] more html --- .../templates/djadmin2theme_default/includes/history.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html index 04e879b1..dffcb896 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html @@ -9,9 +9,7 @@ {% else %} - {% endif %} - {% with action.get_edited_object as object %} - {{ object }} - {% endwith %} + {{ action }} {% endfor %} From 6273e68dde27cf77e2b92395981497ca80637b4e Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 13:46:00 -0700 Subject: [PATCH 05/24] Stealing the logger from the old admin. --- djadmin2/models.py | 83 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/djadmin2/models.py b/djadmin2/models.py index a34119f8..2333db68 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -2,13 +2,94 @@ """ Boilerplate for now, will serve a purpose soon! """ from __future__ import division, absolute_import, unicode_literals +from django.conf import settings +from django.contrib.contenttypes.models import ContentType +from django.contrib.admin.util import quote +from django.db import models from django.db.models import signals +from django.utils.translation import ugettext, ugettext_lazy as _ +from django.utils.encoding import smart_text +from django.utils.encoding import python_2_unicode_compatible from . import permissions +class LogEntryManager(models.Manager): + def log_action(self, user_id, content_type_id, object_id, object_repr, + action_flag, change_message=''): + e = self.model(None, None, user_id, content_type_id, + smart_text(object_id), object_repr[:200], action_flag, + change_message) + e.save() + + +@python_2_unicode_compatible +class LogEntry(models.Model): + ADDITION = 1 + CHANGE = 2 + DELETION = 3 + + action_time = models.DateTimeField(_('action time'), auto_now=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL) + content_type = models.ForeignKey(ContentType, blank=True, null=True) + object_id = models.TextField(_('object id'), blank=True, null=True) + object_repr = models.CharField(_('object repr'), max_length=200) + action_flag = models.PositiveSmallIntegerField(_('action flag')) + change_message = models.TextField(_('change message'), blank=True) + + objects = LogEntryManager() + + class Meta: + verbose_name = _('log entry') + verbose_name_plural = _('log entries') + ordering = ('-action_time',) + + def __repr__(self): + return smart_text(self.action_time) + + def __str__(self): + if self.action_flag == self.ADDITION: + return ugettext('Added "%(object)s".') % { + 'object': self.object_repr} + elif self.action_flag == self.CHANGE: + return ugettext('Changed "%(object)s" - %(changes)s') % { + 'object': self.object_repr, + 'changes': self.change_message, + } + elif self.action_flag == self.DELETION: + return ugettext('Deleted "%(object)s."') % { + 'object': self.object_repr} + + return ugettext('LogEntry Object') + + def is_addition(self): + return self.action_flag == self.ADDITION + + def is_change(self): + return self.action_flag == self.CHANGE + + def is_deletion(self): + return self.action_flag == self.DELETION + + def get_edited_object(self): + "Returns the edited object represented by this log entry" + return self.content_type.get_object_for_this_type(pk=self.object_id) + + def get_admin_url(self): + """ + Returns the admin URL to edit the object represented by this log entry. + This is relative to the Django admin index page. + """ + if self.content_type and self.object_id: + return '{0.app_label}/{0.model}/{1}'.format( + self.content_type, + quote(self.object_id) + ) + return None + # setup signal handlers here, since ``models.py`` will be imported by django # for sure if ``djadmin2`` is listed in the ``INSTALLED_APPS``. -signals.post_syncdb.connect(permissions.create_view_permissions, +signals.post_syncdb.connect( + permissions.create_view_permissions, dispatch_uid="django-admin2.djadmin2.permissions.create_view_permissions") From 598b0a704fbafae2f6595e4e88ecc28475e932f6 Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 13:59:08 -0700 Subject: [PATCH 06/24] basics for recording history --- djadmin2/models.py | 14 ++++++++------ djadmin2/templatetags/admin2_tags.py | 5 ++--- djadmin2/views.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/djadmin2/models.py b/djadmin2/models.py index 2333db68..aa94a61b 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -15,11 +15,11 @@ class LogEntryManager(models.Manager): - def log_action(self, user_id, content_type_id, object_id, object_repr, - action_flag, change_message=''): + def log_action(self, user_id, obj, action_flag, change_message=''): + content_type_id = ContentType.objects.get_for_model(obj).id e = self.model(None, None, user_id, content_type_id, - smart_text(object_id), object_repr[:200], action_flag, - change_message) + smart_text(obj.id), obj.__unicode__()[:200], + action_flag, change_message) e.save() @@ -30,8 +30,10 @@ class LogEntry(models.Model): DELETION = 3 action_time = models.DateTimeField(_('action time'), auto_now=True) - user = models.ForeignKey(settings.AUTH_USER_MODEL) - content_type = models.ForeignKey(ContentType, blank=True, null=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL, + related_name='log_entries') + content_type = models.ForeignKey(ContentType, blank=True, null=True, + related_name='log_entries') object_id = models.TextField(_('object id'), blank=True, null=True) object_repr = models.CharField(_('object repr'), max_length=200) action_flag = models.PositiveSmallIntegerField(_('action flag')) diff --git a/djadmin2/templatetags/admin2_tags.py b/djadmin2/templatetags/admin2_tags.py index a3952691..833cef0e 100644 --- a/djadmin2/templatetags/admin2_tags.py +++ b/djadmin2/templatetags/admin2_tags.py @@ -7,7 +7,7 @@ from django import template from django.db.models.fields import FieldDoesNotExist -from .. import utils, renderers +from .. import utils, renderers, models register = template.Library() @@ -141,6 +141,5 @@ def render(context, model_instance, attribute_name): @register.inclusion_tag('djadmin2theme_default/includes/history.html', takes_context=True) def action_history(context): - from django.contrib.admin.models import LogEntry - actions = LogEntry.objects.filter(user__pk=context['user'].pk) + actions = models.LogEntry.objects.filter(user__pk=context['user'].pk) return {'actions': actions} diff --git a/djadmin2/views.py b/djadmin2/views.py index b5c9baaf..d8532a65 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -22,6 +22,7 @@ from . import permissions, utils from .forms import AdminAuthenticationForm +from .models import LogEntry from .viewmixins import Admin2Mixin, AdminModel2Mixin, Admin2ModelFormMixin from .filters import build_list_filter @@ -231,6 +232,11 @@ def get_context_data(self, **kwargs): context['action_name'] = ugettext_lazy("Change") return context + def forms_valid(self, form, inlines): + response = super(ModelEditFormView, self).forms_valid(form, inlines) + LogEntry.objects.log_action(self.request.user.id, self.object, 2) + return response + class ModelAddFormView(AdminModel2Mixin, Admin2ModelFormMixin, extra_views.CreateWithInlinesView): @@ -253,6 +259,11 @@ def get_context_data(self, **kwargs): context['action_name'] = ugettext_lazy("Add") return context + def forms_valid(self, form, inlines): + response = super(ModelAddFormView, self).forms_valid(form, inlines) + LogEntry.objects.log_action(self.request.user.id, self.object, 1) + return response + class ModelDeleteView(AdminModel2Mixin, generic.DeleteView): """Context Variables @@ -283,6 +294,10 @@ def _format_callback(obj): }) return context + def delete(self, request, *args, **kwargs): + LogEntry.objects.log_action(request.user.id, self.get_object(), 3) + return super(ModelDeleteView, self).delete(request, *args, **kwargs) + class PasswordChangeView(Admin2Mixin, generic.UpdateView): From ae39a26e13fa64e8591dba9da40570f3492aa4e8 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 15:58:31 -0700 Subject: [PATCH 07/24] Use model properties for action ints. --- djadmin2/views.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/djadmin2/views.py b/djadmin2/views.py index d8532a65..3177076a 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -234,7 +234,8 @@ def get_context_data(self, **kwargs): def forms_valid(self, form, inlines): response = super(ModelEditFormView, self).forms_valid(form, inlines) - LogEntry.objects.log_action(self.request.user.id, self.object, 2) + LogEntry.objects.log_action( + self.request.user.id, self.object, LogEntry.CHANGE) return response @@ -261,7 +262,8 @@ def get_context_data(self, **kwargs): def forms_valid(self, form, inlines): response = super(ModelAddFormView, self).forms_valid(form, inlines) - LogEntry.objects.log_action(self.request.user.id, self.object, 1) + LogEntry.objects.log_action( + self.request.user.id, self.object, LogEntry.ADDITION) return response @@ -295,7 +297,8 @@ def _format_callback(obj): return context def delete(self, request, *args, **kwargs): - LogEntry.objects.log_action(request.user.id, self.get_object(), 3) + LogEntry.objects.log_action( + request.user.id, self.get_object(), LogEntry.DELETION) return super(ModelDeleteView, self).delete(request, *args, **kwargs) From 555588f13c05a0723c8b31fcdd550dd35881245a Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 16:08:38 -0700 Subject: [PATCH 08/24] PEP8 --- djadmin2/viewmixins.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/djadmin2/viewmixins.py b/djadmin2/viewmixins.py index fc816a6d..d3af797f 100644 --- a/djadmin2/viewmixins.py +++ b/djadmin2/viewmixins.py @@ -43,8 +43,10 @@ def dispatch(self, request, *args, **kwargs): if self.raise_exception: raise PermissionDenied # return a forbidden response else: - return redirect_to_login(request.get_full_path(), - self.get_login_url(), self.get_redirect_field_name()) + return redirect_to_login( + request.get_full_path(), + self.get_login_url(), + self.get_redirect_field_name()) return super(PermissionMixin, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): @@ -67,7 +69,8 @@ class Admin2Mixin(PermissionMixin): index_path = reverse_lazy('admin2:dashboard') def get_template_names(self): - return [os.path.join(settings.ADMIN2_THEME_DIRECTORY, self.default_template_name)] + return [os.path.join( + settings.ADMIN2_THEME_DIRECTORY, self.default_template_name)] def get_model(self): return self.model From 4089ad4aab8e3b6b65bc90dcca411a4869c28f3a Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 16:30:40 -0700 Subject: [PATCH 09/24] PEP8 all the things. Add in new history url. --- djadmin2/types.py | 53 +++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/djadmin2/types.py b/djadmin2/types.py index 1e6e88c3..ffbefcf8 100644 --- a/djadmin2/types.py +++ b/djadmin2/types.py @@ -151,13 +151,15 @@ def get_create_kwargs(self): kwargs = self.get_default_view_kwargs() kwargs.update({ 'inlines': self.inlines, - 'form_class': self.create_form_class if self.create_form_class else self.form_class, + 'form_class': (self.create_form_class if + self.create_form_class else self.form_class), }) return kwargs def get_update_kwargs(self): kwargs = self.get_default_view_kwargs() - form_class = self.update_form_class if self.update_form_class else self.form_class + form_class = (self.update_form_class if + self.update_form_class else self.form_class) if form_class is None: form_class = modelform_factory(self.model) kwargs.update({ @@ -173,7 +175,8 @@ def get_delete_kwargs(self): return self.get_default_view_kwargs() def get_index_url(self): - return reverse('admin2:{}'.format(self.get_prefixed_view_name('index'))) + return reverse('admin2:{}'.format( + self.get_prefixed_view_name('index'))) def get_api_list_kwargs(self): kwargs = self.get_default_api_view_kwargs() @@ -186,7 +189,8 @@ def get_api_detail_kwargs(self): return self.get_default_api_view_kwargs() def get_urls(self): - return patterns('', + return patterns( + '', url( regex=r'^$', view=self.index_view.as_view(**self.get_index_kwargs()), @@ -212,10 +216,16 @@ def get_urls(self): view=self.delete_view.as_view(**self.get_delete_kwargs()), name=self.get_prefixed_view_name('delete') ), + url( + regex=r'^(?P[0-9]+)/history/$', + view=self.history_view.as_view(**self.get_history_kwargs()), + name=self.get_prefixed_view_name('history') + ) ) def get_api_urls(self): - return patterns('', + return patterns( + '', url( regex=r'^$', view=self.api_list_view.as_view(**self.get_api_list_kwargs()), @@ -223,7 +233,8 @@ def get_api_urls(self): ), url( regex=r'^(?P[0-9]+)/$', - view=self.api_detail_view.as_view(**self.get_api_detail_kwargs()), + view=self.api_detail_view.as_view( + **self.get_api_detail_kwargs()), name=self.get_prefixed_view_name('api_detail'), ), ) @@ -244,9 +255,9 @@ def get_list_actions(self): class_actions = getattr(cls, 'list_actions', []) for action in class_actions: actions_dict[action.__name__] = { - 'name': action.__name__, - 'description': actions.get_description(action), - 'action_callable': action + 'name': action.__name__, + 'description': actions.get_description(action), + 'action_callable': action } return actions_dict @@ -270,22 +281,28 @@ def construct_formset(self): class Admin2TabularInline(Admin2Inline): - template = os.path.join(settings.ADMIN2_THEME_DIRECTORY, 'edit_inlines/tabular.html') + template = os.path.join( + settings.ADMIN2_THEME_DIRECTORY, 'edit_inlines/tabular.html') class Admin2StackedInline(Admin2Inline): - template = os.path.join(settings.ADMIN2_THEME_DIRECTORY, 'edit_inlines/stacked.html') + template = os.path.join( + settings.ADMIN2_THEME_DIRECTORY, 'edit_inlines/stacked.html') def immutable_admin_factory(model_admin): - """ Provide an ImmutableAdmin to make it harder for developers to dig themselves into holes. - See https://github.com/twoscoops/django-admin2/issues/99 - Frozen class implementation as namedtuple suggested by Audrey Roy - - Note: This won't stop developers from saving mutable objects to the result, but hopefully - developers attempting that 'workaround/hack' will read our documentation. + """ + Provide an ImmutableAdmin to make it harder for developers to + dig themselves into holes. + See https://github.com/twoscoops/django-admin2/issues/99 + Frozen class implementation as namedtuple suggested by Audrey Roy + + Note: This won't stop developers from saving mutable objects to + the result, but hopefully developers attempting that + 'workaround/hack' will read our documentation. """ ImmutableAdmin = namedtuple('ImmutableAdmin', model_admin.model_admin_attributes, verbose=False) - return ImmutableAdmin(*[getattr(model_admin, x) for x in model_admin.model_admin_attributes]) + return ImmutableAdmin(*[getattr( + model_admin, x) for x in model_admin.model_admin_attributes]) From 82d9e7ae65c9eeb86596afc7812c5c1dfb19f22b Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 16:36:31 -0700 Subject: [PATCH 10/24] PEP8 love. --- djadmin2/core.py | 30 ++++++++++++++++++++---------- djadmin2/types.py | 4 ++++ djadmin2/views.py | 7 +++++++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/djadmin2/core.py b/djadmin2/core.py index ce60da2d..509cee9a 100644 --- a/djadmin2/core.py +++ b/djadmin2/core.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*-: """ -WARNING: This file about to undergo major refactoring by @pydanny per Issue #99. +WARNING: This file about to undergo major refactoring by @pydanny per +Issue #99. """ from __future__ import division, absolute_import, unicode_literals @@ -22,7 +23,8 @@ class Admin2(object): It keeps a registry of all registered Models and collects the urls of their related ModelAdmin2 instances. - It also provides an index view that serves as an entry point to the admin site. + It also provides an index view that serves as an entry point to the + admin site. """ index_view = views.IndexView app_index_view = views.AppIndexView @@ -46,7 +48,8 @@ def register(self, model, model_admin=None, **kwargs): If a model is already registered, this will raise ImproperlyConfigured. """ if model in self.registry: - raise ImproperlyConfigured('%s is already registered in django-admin2' % model) + raise ImproperlyConfigured( + '%s is already registered in django-admin2' % model) if not model_admin: model_admin = types.ModelAdmin2 self.registry[model] = model_admin(model, admin=self, **kwargs) @@ -62,12 +65,14 @@ def deregister(self, model): """ Deregisters the given model. Remove the model from the self.app as well - If the model is not already registered, this will raise ImproperlyConfigured. + If the model is not already registered, this will raise + ImproperlyConfigured. """ try: del self.registry[model] except KeyError: - raise ImproperlyConfigured('%s was never registered in django-admin2' % model) + raise ImproperlyConfigured( + '%s was never registered in django-admin2' % model) # Remove the model from the apps registry # Get the app label @@ -101,7 +106,8 @@ def get_admin_by_name(self, name): for object_admin in self.registry.values(): if object_admin.name == name: return object_admin - raise ValueError(u'No object admin found with name {}'.format(repr(name))) + raise ValueError( + u'No object admin found with name {}'.format(repr(name))) def get_index_kwargs(self): return { @@ -122,7 +128,8 @@ def get_api_index_kwargs(self): } def get_urls(self): - urlpatterns = patterns('', + urlpatterns = patterns( + '', url(regex=r'^$', view=self.index_view.as_view(**self.get_index_kwargs()), name='dashboard' @@ -140,18 +147,21 @@ def get_urls(self): name='logout' ), url(regex=r'^(?P\w+)/$', - view=self.app_index_view.as_view(**self.get_app_index_kwargs()), + view=self.app_index_view.as_view( + **self.get_app_index_kwargs()), name='app_index' ), url(regex=r'^api/v0/$', - view=self.api_index_view.as_view(**self.get_api_index_kwargs()), + view=self.api_index_view.as_view( + **self.get_api_index_kwargs()), name='api_index' ), ) for model, model_admin in self.registry.iteritems(): model_options = utils.model_options(model) - urlpatterns += patterns('', + urlpatterns += patterns( + '', url('^{}/{}/'.format( model_options.app_label, model_options.object_name.lower()), diff --git a/djadmin2/types.py b/djadmin2/types.py index ffbefcf8..c77a2d43 100644 --- a/djadmin2/types.py +++ b/djadmin2/types.py @@ -102,6 +102,7 @@ class ModelAdmin2(object): update_view = views.ModelEditFormView detail_view = views.ModelDetailView delete_view = views.ModelDeleteView + history_view = views.ModelHistoryView # API configuration api_serializer_class = None @@ -174,6 +175,9 @@ def get_detail_kwargs(self): def get_delete_kwargs(self): return self.get_default_view_kwargs() + def get_history_kwargs(self): + return self.get_default_view_kwargs() + def get_index_url(self): return reverse('admin2:{}'.format( self.get_prefixed_view_name('index'))) diff --git a/djadmin2/views.py b/djadmin2/views.py index 3177076a..da5f4721 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -302,6 +302,13 @@ def delete(self, request, *args, **kwargs): return super(ModelDeleteView, self).delete(request, *args, **kwargs) +class ModelHistoryView(Admin2Mixin, generic.ListView): + def get_context_data(self, **kwargs): + context = super(ModelHistoryView, self).get_context_data(**kwargs) + context['model'] = self.get_model() + return context + + class PasswordChangeView(Admin2Mixin, generic.UpdateView): default_template_name = 'auth/password_change_form.html' From f7ef5757093e09837bc29a052cf9fad381074cb7 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 16:37:18 -0700 Subject: [PATCH 11/24] Should be a detail view. --- djadmin2/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djadmin2/views.py b/djadmin2/views.py index da5f4721..ae1731eb 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -302,7 +302,7 @@ def delete(self, request, *args, **kwargs): return super(ModelDeleteView, self).delete(request, *args, **kwargs) -class ModelHistoryView(Admin2Mixin, generic.ListView): +class ModelHistoryView(Admin2Mixin, generic.DetailView): def get_context_data(self, **kwargs): context = super(ModelHistoryView, self).get_context_data(**kwargs) context['model'] = self.get_model() From d62b612583fc5a06f23db55aee9f5b9e5c3ab22f Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 16:38:00 -0700 Subject: [PATCH 12/24] nevermind. --- djadmin2/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djadmin2/views.py b/djadmin2/views.py index ae1731eb..da5f4721 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -302,7 +302,7 @@ def delete(self, request, *args, **kwargs): return super(ModelDeleteView, self).delete(request, *args, **kwargs) -class ModelHistoryView(Admin2Mixin, generic.DetailView): +class ModelHistoryView(Admin2Mixin, generic.ListView): def get_context_data(self, **kwargs): context = super(ModelHistoryView, self).get_context_data(**kwargs) context['model'] = self.get_model() From 7dbf50426f7770568cc999aa4b8c33e8698b6453 Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 16:38:13 -0700 Subject: [PATCH 13/24] model_history template --- .../djadmin2theme_default/model_history.html | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html new file mode 100644 index 00000000..d6337745 --- /dev/null +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html @@ -0,0 +1,40 @@ +{% extends "djadmin2theme_default/base.html" %} +{% load admin2_tags i18n %} + +{% block title %}{% trans "History for" %} {{ object }}{% endblock title %} + +{% block page_title %}{% trans "History for" %} {{ object }}{% endblock page_title %} + +{% block breadcrumbs %} +
  • + {% trans "Home" %} + / +
  • +
  • + {{ app_label|title }} + / +
  • +
  • + {{ model_name_pluralized|title }} + / +
  • +
  • + {{ object }} + / +
  • +
  • {% trans "History" %}
  • +{% endblock breadcrumbs %} + +{% block content %} + +

    +{% blocktrans with object=object %} + History for {{ object }} +{% endblocktrans %} + +

      + {% for log in object_list %} +
    1. {{ log }}
    2. + {% endfor %} +
    +{% endblock content %} From ee1c90cd9f6f29dba7858dbafa22088950c08b90 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 16:51:16 -0700 Subject: [PATCH 14/24] Working model history view. --- djadmin2/views.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/djadmin2/views.py b/djadmin2/views.py index da5f4721..3a1bfe4a 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -3,19 +3,21 @@ import operator +from django.contrib.auth import get_user_model from django.contrib.auth.forms import (PasswordChangeForm, AdminPasswordChangeForm) from django.contrib.auth.views import (logout as auth_logout, login as auth_login) -from django.contrib.auth import get_user_model +from django.contrib.contenttypes.models import ContentType from django.core.urlresolvers import reverse, reverse_lazy -from django.utils.translation import ugettext_lazy from django.db import models +from django.db.models.fields import FieldDoesNotExist from django.http import HttpResponseRedirect +from django.shortcuts import get_object_or_404 from django.utils.encoding import force_text from django.utils.text import capfirst +from django.utils.translation import ugettext_lazy from django.views import generic -from django.db.models.fields import FieldDoesNotExist import extra_views @@ -302,12 +304,29 @@ def delete(self, request, *args, **kwargs): return super(ModelDeleteView, self).delete(request, *args, **kwargs) -class ModelHistoryView(Admin2Mixin, generic.ListView): +class ModelHistoryView(AdminModel2Mixin, generic.ListView): + default_template_name = "model_history.html" + permission_classes = ( + permissions.IsStaffPermission, + permissions.ModelChangePermission + ) + def get_context_data(self, **kwargs): context = super(ModelHistoryView, self).get_context_data(**kwargs) context['model'] = self.get_model() + context['object'] = self.get_object() return context + def get_object(self): + return get_object_or_404(self.get_model(), pk=self.kwargs.get('pk')) + + def get_queryset(self): + content_type = ContentType.objects.get_for_model(self.get_object()) + return LogEntry.objects.filter( + content_type=content_type, + object_id=self.get_object().id + ) + class PasswordChangeView(Admin2Mixin, generic.UpdateView): From ee2ca9feff2febc14e6eb65b2ff249fbf1d5ff8f Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 16:54:22 -0700 Subject: [PATCH 15/24] template updates --- .../djadmin2theme_default/includes/history.html | 2 +- .../djadmin2theme_default/model_update_form.html | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html index dffcb896..e8a32090 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html @@ -9,7 +9,7 @@ {% else %} - {% endif %} - {{ action }} + {{ action }} {% endfor %} diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html index fc200948..d78a58a9 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html @@ -9,7 +9,7 @@ {% block page_title %}{% blocktrans with action=action model_name=model_name %}{{ action_name }} {{ model_name }}{% endblocktrans %}{% endblock page_title %} {% block page_title_link %} -History + History {% endblock page_title_link %} {% block breadcrumbs %} @@ -41,14 +41,14 @@ {% block content %}
    - + {% if view.model_admin.save_on_top %} {% include "djadmin2theme_default/includes/save_buttons.html" %} {% endif %}
    - +
    {% csrf_token %} {{ form|crispy }} @@ -63,11 +63,11 @@

    {{ formset.model|model_verbose_name_plural|capfirst }}

    - + {% if view.model_admin.save_on_bottom %} {% include "djadmin2theme_default/includes/save_buttons.html" %} {% endif %} - + From 99ae00a0ae0af70e3c803f0ac3c5411826b12a10 Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 16:56:08 -0700 Subject: [PATCH 16/24] show user that performed action --- .../templates/djadmin2theme_default/model_history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html index d6337745..34685663 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html @@ -34,7 +34,7 @@
      {% for log in object_list %} -
    1. {{ log }}
    2. +
    3. {{ log }} {% trans "by" %} {{ log.user }}
    4. {% endfor %}
    {% endblock content %} From acae0a2620ad44d742496c92832b4c74984f5c31 Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 17:02:40 -0700 Subject: [PATCH 17/24] icons --- .../djadmin2theme_default/includes/history.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html index e8a32090..c4b9f9bf 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/includes/history.html @@ -1,15 +1,16 @@ {% if actions %} -
      +
        {% for action in actions %}
      1. {% if action.is_addition %} - + + {% elif action.is_change %} - ~ + {% else %} - - + {% endif %} {{ action }} + {{ action.content_type.model }}
      2. {% endfor %}
      From 52c337a72c25c961a3983bc4c0b18d4ae4e13032 Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 17:16:07 -0700 Subject: [PATCH 18/24] force_text for object_repr and condition on history link --- djadmin2/models.py | 5 +++-- .../templates/djadmin2theme_default/model_update_form.html | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/djadmin2/models.py b/djadmin2/models.py index aa94a61b..a341637e 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -7,9 +7,10 @@ from django.contrib.admin.util import quote from django.db import models from django.db.models import signals -from django.utils.translation import ugettext, ugettext_lazy as _ +from django.utils.encoding import force_text from django.utils.encoding import smart_text from django.utils.encoding import python_2_unicode_compatible +from django.utils.translation import ugettext, ugettext_lazy as _ from . import permissions @@ -18,7 +19,7 @@ class LogEntryManager(models.Manager): def log_action(self, user_id, obj, action_flag, change_message=''): content_type_id = ContentType.objects.get_for_model(obj).id e = self.model(None, None, user_id, content_type_id, - smart_text(obj.id), obj.__unicode__()[:200], + smart_text(obj.id), force_text(obj)[:200], action_flag, change_message) e.save() diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html index d78a58a9..adab5365 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html @@ -9,7 +9,9 @@ {% block page_title %}{% blocktrans with action=action model_name=model_name %}{{ action_name }} {{ model_name }}{% endblocktrans %}{% endblock page_title %} {% block page_title_link %} +{% if object.pk %} History +{% endif %} {% endblock page_title_link %} {% block breadcrumbs %} From 69f2a357f8f7a5d7caa308f57583e8e096b5d842 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 17:23:48 -0700 Subject: [PATCH 19/24] Why don't you PEP your 8! Add in change messages. --- djadmin2/viewmixins.py | 34 ++++++++++++++++++++++++++++++++++ djadmin2/views.py | 15 ++++++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/djadmin2/viewmixins.py b/djadmin2/viewmixins.py index d3af797f..ecc26a7d 100644 --- a/djadmin2/viewmixins.py +++ b/djadmin2/viewmixins.py @@ -8,6 +8,10 @@ from django.core.urlresolvers import reverse, reverse_lazy from django.forms.models import modelform_factory from django.http import HttpResponseRedirect +from django.utils.encoding import force_text +from django.utils.text import get_text_list +from django.utils.translation import ugettext as _ + from braces.views import AccessMixin from . import settings, permissions @@ -142,3 +146,33 @@ def get_success_url(self): # default to index view return reverse(admin2_urlname(self, 'index')) + + 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 {0}.'.format( + get_text_list(form.changed_data, _('and'))))) + + if formsets: + for formset in formsets: + for added_object in formset.new_objects: + change_message.append( + _('Added {0} "{1}".'.format( + force_text(added_object._meta.verbose_name), + force_text(added_object)))) + for changed_object, changed_fields in formset.changed_objects: + change_message.append( + _('Changed {0} for {1} "{2}".'.format( + get_text_list(changed_fields, _('and')), + force_text(changed_object._meta.verbose_name), + force_text(changed_object)))) + for deleted_object in formset.deleted_objects: + change_message.append( + _('Deleted {0} "{1}".'.format( + force_text(deleted_object._meta.verbose_name), + force_text(deleted_object)))) + + change_message = ' '.join(change_message) + return change_message or _('No fields changed.') diff --git a/djadmin2/views.py b/djadmin2/views.py index 3a1bfe4a..4a739372 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -237,7 +237,10 @@ def get_context_data(self, **kwargs): def forms_valid(self, form, inlines): response = super(ModelEditFormView, self).forms_valid(form, inlines) LogEntry.objects.log_action( - self.request.user.id, self.object, LogEntry.CHANGE) + self.request.user.id, + self.object, + LogEntry.CHANGE, + self.construct_change_message(self.request, form, inlines)) return response @@ -265,7 +268,10 @@ def get_context_data(self, **kwargs): def forms_valid(self, form, inlines): response = super(ModelAddFormView, self).forms_valid(form, inlines) LogEntry.objects.log_action( - self.request.user.id, self.object, LogEntry.ADDITION) + self.request.user.id, + self.object, + LogEntry.ADDITION, + 'Object created.') return response @@ -300,7 +306,10 @@ def _format_callback(obj): def delete(self, request, *args, **kwargs): LogEntry.objects.log_action( - request.user.id, self.get_object(), LogEntry.DELETION) + request.user.id, + self.get_object(), + LogEntry.DELETION, + 'Object deleted.') return super(ModelDeleteView, self).delete(request, *args, **kwargs) From 57ef2a428c1e84b398613342c90162425539c7f0 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 17:30:41 -0700 Subject: [PATCH 20/24] Nicer to look at. --- .../djadmin2theme_default/model_history.html | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html index 34685663..d312040b 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html @@ -32,9 +32,24 @@ History for {{ object }} {% endblocktrans %} -
        - {% for log in object_list %} -
      1. {{ log }} {% trans "by" %} {{ log.user }}
      2. - {% endfor %} -
      + + + + + + + + + + + {% for log in object_list %} + + + + + + + {% endfor %} + +
      {% trans "Date/Time" %}{% trans "User" %}{% trans "Action" %}{% trans "Message" %}
      {{ log.action_time }}{{ log.user }}{{ log.action_flag }}{{ log.change_message }}
      {% endblock content %} From e5f1940f3e6def47b66fa5835b2c5b7dbd735ad8 Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 17:31:08 -0700 Subject: [PATCH 21/24] action_type property --- djadmin2/models.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/djadmin2/models.py b/djadmin2/models.py index a341637e..26d83abe 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -74,6 +74,16 @@ def is_change(self): def is_deletion(self): return self.action_flag == self.DELETION + @property + def action_type(self): + if self.is_addition: + return _('added') + if self.is_change: + return _('changed') + if self.is_deletion(): + return _('deleted') + return '' + def get_edited_object(self): "Returns the edited object represented by this log entry" return self.content_type.get_object_for_this_type(pk=self.object_id) From e7f8170237ae31785eea03c4e3683893fed215d8 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 17:33:19 -0700 Subject: [PATCH 22/24] Ooooooooo pretty. --- djadmin2/models.py | 4 ++-- .../templates/djadmin2theme_default/model_history.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/djadmin2/models.py b/djadmin2/models.py index 26d83abe..01a4b44a 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -76,9 +76,9 @@ def is_deletion(self): @property def action_type(self): - if self.is_addition: + if self.is_addition(): return _('added') - if self.is_change: + if self.is_change(): return _('changed') if self.is_deletion(): return _('deleted') diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html index d312040b..7d0822ba 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html @@ -46,7 +46,7 @@ {{ log.action_time }} {{ log.user }} - {{ log.action_flag }} + {{ log.action_type|capfirst }} {{ log.change_message }} {% endfor %} From 738e3a9dd8a095bfc178ec22c1d0b7172265391c Mon Sep 17 00:00:00 2001 From: Kenneth Love Date: Thu, 18 Jul 2013 17:40:51 -0700 Subject: [PATCH 23/24] template changes --- .../djadmin2theme_default/index.html | 6 +-- .../djadmin2theme_default/model_history.html | 44 ++++++++++--------- .../model_update_form.html | 2 +- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/index.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/index.html index b2749e32..adc1a2bb 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/index.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/index.html @@ -3,13 +3,13 @@ {% block content %} -
      -
      +
      +
      {% for app_label, registry in apps.items %} {% include 'djadmin2theme_default/includes/app_model_list.html' %} {% endfor %}
      -
      +

      {% trans "Recent Actions" %}

      {% trans "My Actions" %}
      {% action_history %} diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html index d312040b..f68ad8dc 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html @@ -32,24 +32,28 @@ History for {{ object }} {% endblocktrans %} - - - - - - - - - - - {% for log in object_list %} - - - - - - - {% endfor %} - -
      {% trans "Date/Time" %}{% trans "User" %}{% trans "Action" %}{% trans "Message" %}
      {{ log.action_time }}{{ log.user }}{{ log.action_flag }}{{ log.change_message }}
      +{% if object_list %} + + + + + + + + + + + {% for log in object_list %} + + + + + + + {% endfor %} + +
      {% trans "Date/Time" %}{% trans "User" %}{% trans "Action" %}{% trans "Message" %}
      {{ log.action_time }}{{ log.user }}{{ log.action_flag }}{{ log.change_message }}
      +{% else %} +

      No history for this object.

      +{% endif %} {% endblock content %} diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html index adab5365..3e303dd1 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_update_form.html @@ -10,7 +10,7 @@ {% block page_title_link %} {% if object.pk %} - History + History {% endif %} {% endblock page_title_link %} From 4959a095c4022bcedda913e6ab6eb7a44352f030 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 18 Jul 2013 17:44:19 -0700 Subject: [PATCH 24/24] fix merge error. --- .../templates/djadmin2theme_default/model_history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html index f68ad8dc..e6534b20 100644 --- a/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html +++ b/djadmin2/themes/djadmin2theme_default/templates/djadmin2theme_default/model_history.html @@ -47,7 +47,7 @@ {{ log.action_time }} {{ log.user }} - {{ log.action_flag }} + {{ log.action_type|capfirst }} {{ log.change_message }} {% endfor %}