django-simple-history icon indicating copy to clipboard operation
django-simple-history copied to clipboard

Error with identifiers with '/'

Open javigafe opened this issue 1 year ago • 1 comments

Describe the bug If the history is added to a model, which allows to have in his primary key a '/' this does store the history correctly in the database, but when trying to access the history of this through the Django administration panel if the object identifier has a '/' character it returns an error.

To Reproduce Steps to reproduce the behavior:

  1. Generate a model which allows us to add the value of the primary key and that it has a history. For example:
class Test(models.Model):
    id = models.CharField(max_length=256, null=False, primary_key=True)
    last_seen = models.DateTimeField(null=True)
    history = HistoricalRecords()

    def __str__(self):
        return str(self.machine_id)
  1. Add the ability to display the history in the Django admin panel
admin_site.register(Test, SimpleHistoryAdmin)
  1. Add an element which has a '/' in its id, for example: dsfSERGserfseRFSREE/DFEWdfawer34
  2. Modify this element to generate a history.
  3. Attempt to access the history and you get the error:
Internal Server Error: /admin/pr_test/test/dsfSERGserfseRFSREE_2FDFEWdfawer34/history/\n","stream":"stderr","time":"2024-01-19T10:51:46.594236044Z
Traceback (most recent call last):\n","stream":"stderr","time":"2024-01-19T10:51:46.594303279Z
  File \"/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py\", line 55, in inner\n","stream":"stderr","time":"2024-01-19T10:51:46.594311074Z
    response = get_response(request)\n","stream":"stderr","time":"2024-01-19T10:51:46.594318267Z
  File \"/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py\", line 197, in _get_response\n","stream":"stderr","time":"2024-01-19T10:51:46.594324411Z
    response = wrapped_callback(request, *callback_args, **callback_kwargs)\n","stream":"stderr","time":"2024-01-19T10:51:46.594331416Z
  File \"/usr/local/lib/python3.8/site-packages/django/contrib/admin/options.py\", line 688, in wrapper\n","stream":"stderr","time":"2024-01-19T10:51:46.594337845Z
    return self.admin_site.admin_view(view)(*args, **kwargs)\n","stream":"stderr","time":"2024-01-19T10:51:46.594344355Z
  File \"/usr/local/lib/python3.8/site-packages/django/utils/decorators.py\", line 134, in _wrapper_view\n","stream":"stderr","time":"2024-01-19T10:51:46.594350491Z
    response = view_func(request, *args, **kwargs)\n","stream":"stderr","time":"2024-01-19T10:51:46.594356809Z
  File \"/usr/local/lib/python3.8/site-packages/django/views/decorators/cache.py\", line 62, in _wrapper_view_func\n","stream":"stderr","time":"2024-01-19T10:51:46.594362856Z
    response = view_func(request, *args, **kwargs)\n","stream":"stderr","time":"2024-01-19T10:51:46.594369347Z
  File \"/usr/local/lib/python3.8/site-packages/django/contrib/admin/sites.py\", line 242, in inner\n","stream":"stderr","time":"2024-01-19T10:51:46.594375418Z
    return view(request, *args, **kwargs)\n","stream":"stderr","time":"2024-01-19T10:51:46.59438185Z
  File \"/usr/local/lib/python3.8/site-packages/simple_history/admin.py\", line 96, in history_view\n","stream":"stderr","time":"2024-01-19T10:51:46.594387882Z
    return self.render_history_view(\n","stream":"stderr","time":"2024-01-19T10:51:46.594394433Z
  File \"/usr/local/lib/python3.8/site-packages/simple_history/admin.py\", line 223, in render_history_view\n","stream":"stderr","time":"2024-01-19T10:51:46.594400408Z
    return render(request, template, context, **kwargs)\n","stream":"stderr","time":"2024-01-19T10:51:46.59443811Z
  File \"/usr/local/lib/python3.8/site-packages/django/shortcuts.py\", line 24, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594444819Z
    content = loader.render_to_string(template_name, context, request, using=using)\n","stream":"stderr","time":"2024-01-19T10:51:46.594451161Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/loader.py\", line 62, in render_to_string\n","stream":"stderr","time":"2024-01-19T10:51:46.594457263Z
    return template.render(context, request)\n","stream":"stderr","time":"2024-01-19T10:51:46.594468459Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/backends/django.py\", line 61, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.59447437Z
    return self.template.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594483292Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 175, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594489332Z
    return self._render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594495627Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 167, in _render\n","stream":"stderr","time":"2024-01-19T10:51:46.594506403Z
    return self.nodelist.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594512874Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594518863Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594525465Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in \u003clistcomp\u003e\n","stream":"stderr","time":"2024-01-19T10:51:46.594531886Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594538929Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 966, in render_annotated\n","stream":"stderr","time":"2024-01-19T10:51:46.594545255Z
    return self.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.59456309Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py\", line 157, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.59457142Z
    return compiled_parent._render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594578125Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 167, in _render\n","stream":"stderr","time":"2024-01-19T10:51:46.594584301Z
    return self.nodelist.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594590697Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594596619Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594602818Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in \u003clistcomp\u003e\n","stream":"stderr","time":"2024-01-19T10:51:46.594609067Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594615642Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 966, in render_annotated\n","stream":"stderr","time":"2024-01-19T10:51:46.594621793Z
    return self.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594628113Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py\", line 157, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594639962Z
    return compiled_parent._render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594646541Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 167, in _render\n","stream":"stderr","time":"2024-01-19T10:51:46.59465251Z
    return self.nodelist.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.59465887Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594664857Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.59467116Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in \u003clistcomp\u003e\n","stream":"stderr","time":"2024-01-19T10:51:46.594678265Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594684656Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 966, in render_annotated\n","stream":"stderr","time":"2024-01-19T10:51:46.594690906Z
    return self.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594698191Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py\", line 157, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594707931Z
    return compiled_parent._render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594714342Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 167, in _render\n","stream":"stderr","time":"2024-01-19T10:51:46.594720984Z
    return self.nodelist.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594727348Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594733256Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594739582Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in \u003clistcomp\u003e\n","stream":"stderr","time":"2024-01-19T10:51:46.594745781Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.59475243Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 966, in render_annotated\n","stream":"stderr","time":"2024-01-19T10:51:46.594758504Z
    return self.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594764792Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py\", line 63, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594770615Z
    result = block.nodelist.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594776952Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594782926Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.59479004Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in \u003clistcomp\u003e\n","stream":"stderr","time":"2024-01-19T10:51:46.594796172Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594802738Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 966, in render_annotated\n","stream":"stderr","time":"2024-01-19T10:51:46.594808922Z
    return self.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594820218Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py\", line 321, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594826187Z
    return nodelist.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594832646Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594838597Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594844827Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in \u003clistcomp\u003e\n","stream":"stderr","time":"2024-01-19T10:51:46.594850999Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594857525Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 966, in render_annotated\n","stream":"stderr","time":"2024-01-19T10:51:46.594863561Z
    return self.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594871037Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/library.py\", line 278, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594877195Z
    return t.render(new_context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594883577Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 177, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594889509Z
    return self._render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594895776Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 167, in _render\n","stream":"stderr","time":"2024-01-19T10:51:46.594901789Z
    return self.nodelist.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594908202Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594914084Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.59492026Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 1005, in \u003clistcomp\u003e\n","stream":"stderr","time":"2024-01-19T10:51:46.594926419Z
    return SafeString(\"\".join([node.render_annotated(context) for node in self]))\n","stream":"stderr","time":"2024-01-19T10:51:46.594932741Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 966, in render_annotated\n","stream":"stderr","time":"2024-01-19T10:51:46.594938893Z
    return self.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594945405Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py\", line 238, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594951335Z
    nodelist.append(node.render_annotated(context))\n","stream":"stderr","time":"2024-01-19T10:51:46.594957729Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/base.py\", line 966, in render_annotated\n","stream":"stderr","time":"2024-01-19T10:51:46.594963697Z
    return self.render(context)\n","stream":"stderr","time":"2024-01-19T10:51:46.594969921Z
  File \"/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py\", line 471, in render\n","stream":"stderr","time":"2024-01-19T10:51:46.594975724Z
    url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)\n","stream":"stderr","time":"2024-01-19T10:51:46.594982098Z
  File \"/usr/local/lib/python3.8/site-packages/django/urls/base.py\", line 88, in reverse\n","stream":"stderr","time":"2024-01-19T10:51:46.594989168Z
    return resolver._reverse_with_prefix(view, prefix, *args, **kwargs)\n","stream":"stderr","time":"2024-01-19T10:51:46.594992871Z
  File \"/usr/local/lib/python3.8/site-packages/django/urls/resolvers.py\", line 828, in _reverse_with_prefix\n","stream":"stderr","time":"2024-01-19T10:51:46.594994852Z
    raise NoReverseMatch(msg)\n","stream":"stderr","time":"2024-01-19T10:51:46.594996882Z
django.urls.exceptions.NoReverseMatch: Reverse for 'pr_test_test_simple_history' with arguments '('dsfSERGserfseRFSREE/DFEWdfawer34', 2)' not found. 1 pattern(s) tried: ['admin/pr_test/test/([^/]+)/history/([^/]+)/$']\n","stream":"stderr","time":"2024-01-19T10:51:46.594998804Z
[19/Jan/2024 10:51:46] \"GET /admin/pr_test/test/dsfSERGserfseRFSREE_2FDFEWdfawer34/history/ HTTP/1.1\" 500 592490\n","stream":"stderr","time":"2024-01-19T10:51:46.59500174Z

Expected behavior It is expected to display the history of elements, as it does for elements that do not have the '/' character.

Environment (please complete the following information):

  • OS: Ubuntu 22.04.3 LTS
  • Django Simple History Version: 3.4.0
  • Django Version: 4.2.7

Additional context With elements that do not contain '/' it works without any problem.

javigafe avatar Jan 19 '24 11:01 javigafe

This error occurs while rendering object URL with primary key in Change History view. This line causes the issue.

Since, primary key contains "/" which is a reserved character in URLs, somehow the object.pk needs to be URL encoded.

I was able to fix this issue using admin_urlquote filter on object.pk. However, it breaks history form view as it receives encoded value in object id and object does not exist with this value.

muneeb706 avatar Jan 23 '24 08:01 muneeb706