Repository: jazzband/django-simple-history

Issue:
custom user model not working with SimpleHIstoryAdmin
If my user model is not `auth.User` then rendering `object_history.html` template fails with NoReverseMatch.

The problem is in [line 28](https://github.com/treyhunner/django-simple-history/blob/master/simple_history/templates/simple_history/object_history.html#L28).

If I change `auth_user_change` to `myapp_user_change` everything works, but it's not a general fix =)

Proper app and model name can be found in [AUTH_USER_MODEL](https://docs.djangoproject.com/en/dev/topics/auth/customizing/#auth-custom-user) but, as far as I understand, there is no template filter which can pull them out.

Possible fixes are
- remove link from template =)
- add `user_app` and `user_model` to context in SimpleHistoryManager. [This](https://github.com/django/django/blob/master/django/contrib/auth/__init__.py#L107) may help.



Discussion:
Thanks for reporting this.

I think we should calculate the the correct URL and add it to the [history_view template context](https://github.com/treyhunner/django-simple-history/blob/master/simple_history/admin.py#L56)

A test case for this one would be great.  It would probably be similar to [AdminSiteTest.test_history_list](https://github.com/treyhunner/django-simple-history/blob/master/simple_history/tests/tests.py#L326) but with a custom user model.

> I think we should calculate the the correct URL and add it to the history_view template context.

Do you mean calculate a mapping between users and urls? There may be several users for the history of one object.

> A test case for this one would be great. 

It will require [override_settings](https://docs.djangoproject.com/en/1.4/topics/testing/#overriding-settings), which is not available for django 1.3. Will it be OK to use `@skipif(django.VERSION < (1.4))` ?

I will create a pull request for this one.

> Do you mean calculate a mapping between users and urls? There may be several users for the history of one object.

I mean calculate the appropriate replacement for the `"admin:auth_user_change"` string that will be used in the `{% url %}` tag.  Since we're using the new Django 1.5 `{% url %}` template tag that string can be substituted based on a context variable.

> Will it be OK to use `@skipif(django.VERSION < (1.4))` ?

I think that's fine.

> I will create a pull request for this one.

Great.  Thanks.


Resolving patch:
diff --git a/runtests.py b/runtests.py
--- a/runtests.py
+++ b/runtests.py
@@ -3,6 +3,7 @@
 from shutil import rmtree
 from os.path import abspath, dirname, join
 
+import django
 from django.conf import settings
 
 
@@ -12,24 +13,32 @@
 if not settings.configured:
     media_root = join(abspath(dirname(__file__)), 'test_files')
     rmtree(media_root, ignore_errors=True)
+
+    installed_apps = (
+        'django.contrib.contenttypes',
+        'django.contrib.auth',
+        'django.contrib.sessions',
+        'django.contrib.admin',
+        'simple_history',
+        'simple_history.tests',
+        'simple_history.tests.external',
+    )
+    auth_user_model = 'auth.User'
+    if django.VERSION >= (1, 5):
+        installed_apps += ('simple_history.tests.custom_user', )
+        auth_user_model = 'custom_user.CustomUser'
+
     settings.configure(
         ROOT_URLCONF='simple_history.tests.urls',
         MEDIA_ROOT=media_root,
         STATIC_URL='/static/',
-        INSTALLED_APPS=(
-            'django.contrib.contenttypes',
-            'django.contrib.auth',
-            'django.contrib.sessions',
-            'django.contrib.admin',
-            'simple_history',
-            'simple_history.tests',
-            'simple_history.tests.external'
-        ),
+        INSTALLED_APPS=installed_apps,
         DATABASES={
             'default': {
                 'ENGINE': 'django.db.backends.sqlite3',
             }
         },
+        AUTH_USER_MODEL=auth_user_model
     )
 
 
diff --git a/simple_history/admin.py b/simple_history/admin.py
--- a/simple_history/admin.py
+++ b/simple_history/admin.py
@@ -20,6 +20,12 @@
 except ImportError:  # django 1.3 compatibility
     from django.utils.encoding import force_unicode as force_text
 
+try:
+    from django.contrib.auth import get_user_model
+    User = get_user_model()
+except ImportError:  # django 1.4 compatibility
+    from django.contrib.auth.models import User
+
 
 class SimpleHistoryAdmin(admin.ModelAdmin):
     object_history_template = "simple_history/object_history.html"
@@ -47,6 +53,8 @@ def history_view(self, request, object_id, extra_context=None):
         action_list = history.filter(**{pk_name: object_id})
         # If no history was found, see whether this object even exists.
         obj = get_object_or_404(model, pk=unquote(object_id))
+        content_type = ContentType.objects.get_for_model(User)
+        admin_user_view = 'admin:%s_%s_change' % (content_type.app_label, content_type.model)
         context = {
             'title': _('Change history: %s') % force_text(obj),
             'action_list': action_list,
@@ -54,7 +62,8 @@ def history_view(self, request, object_id, extra_context=None):
             'object': obj,
             'root_path': getattr(self.admin_site, 'root_path', None),
             'app_label': app_label,
-            'opts': opts
+            'opts': opts,
+            'admin_user_view': admin_user_view
         }
         context.update(extra_context or {})
         context_instance = template.RequestContext(request, current_app=self.admin_site.name)


Test patch:
diff --git a/simple_history/tests/custom_user/__init__.py b/simple_history/tests/custom_user/__init__.py
new file mode 100644
diff --git a/simple_history/tests/custom_user/models.py b/simple_history/tests/custom_user/models.py
new file mode 100644
--- /dev/null
+++ b/simple_history/tests/custom_user/models.py
@@ -0,0 +1,11 @@
+from __future__ import unicode_literals
+from django.contrib import admin
+from django.contrib.auth.admin import UserAdmin
+
+from django.contrib.auth.models import AbstractUser
+
+
+class CustomUser(AbstractUser):
+    pass
+
+admin.site.register(CustomUser, UserAdmin)
\ No newline at end of file
diff --git a/simple_history/tests/models.py b/simple_history/tests/models.py
--- a/simple_history/tests/models.py
+++ b/simple_history/tests/models.py
@@ -1,7 +1,12 @@
 from __future__ import unicode_literals
 
 from django.db import models
-from django.contrib.auth.models import User
+try:
+    from django.contrib.auth import get_user_model
+    User = get_user_model()
+except ImportError:  # django 1.4 compatibility
+    from django.contrib.auth.models import User
+
 from simple_history.models import HistoricalRecords
 from simple_history import register
 
diff --git a/simple_history/tests/tests.py b/simple_history/tests/tests.py
--- a/simple_history/tests/tests.py
+++ b/simple_history/tests/tests.py
@@ -6,7 +6,11 @@
 from django_webtest import WebTest
 from django.core.files.base import ContentFile
 from django.core.urlresolvers import reverse
-from django.contrib.auth.models import User
+try:
+    from django.contrib.auth import get_user_model
+    User = get_user_model()
+except ImportError:  # django 1.4 compatibility
+    from django.contrib.auth.models import User
 
 from .models import Poll, Choice, Restaurant, Person, FileModel, Document
 from .models import ExternalModel1, ExternalModel3
@@ -315,7 +319,7 @@ def get_history_url(model, history_index=None):
 
 class AdminSiteTest(WebTest):
     def setUp(self):
-        self.user = User.objects.create_superuser('u', 'u@example.com', 'pass')
+        self.user = User.objects.create_superuser('user_login', 'u@example.com', 'pass')
 
     def login(self, user=None):
         if user is None:
@@ -327,11 +331,14 @@ def login(self, user=None):
 
     def test_history_list(self):
         self.login()
-        poll = Poll.objects.create(question="why?", pub_date=today)
+        poll = Poll(question="why?", pub_date=today)
+        poll._history_user = self.user
+        poll.save()
         response = self.app.get(get_history_url(poll))
         self.assertIn(get_history_url(poll, 0), response.unicode_normal_body)
         self.assertIn("Poll object", response.unicode_normal_body)
         self.assertIn("Created", response.unicode_normal_body)
+        self.assertIn(self.user.username, response.unicode_normal_body)
 
     def test_history_form_permission(self):
         self.login(self.user)