django/django — pull request #2666 by nott (2014-05-16 16:05:47+00:00)
Title: Fixed #22266 - CharField primary keys with underscores are (un)escaped differently in the admin pages

ModelAdmin.get_urls defines the urls like these:

url(r'^$', wrap(self.changelist_view), name='%s_%s_changelist' % info),
url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info),
url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info),
url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info),
url(r'^(.+)/$', wrap(self.change_view), name='%s_%s_change' % info),

As long as we do not know the exact primary key regexp, we have to use (.+)
Consider we have 2 entries with the following PKs: "test_123" and "test_123/history". There is no way to distinguish change view for "test_123/history" and history view for "test_123".
That is why PK quoting was introduced. django.contrib.admin.utils contains 2 functions: quote and unquote. So reversing admin url should usually look like:

reverse('admin:app_model_change', args=(quote(pk),))

Quoting and unquoting doesn't change numerical PKs, that is why this problem haven't been noticed before.
The first redirect doesn't work, as there is a bug with quoting not being applied in add_view.

I suppose we are really lacking the documentation (​https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#overriding-vs-replacing-an-admin-template is evertyhing I could find).

There is no way to get rid of quoting if we leave (.+) url pattern for PK. So we can leave quoting/unquoting as is and fix the redirect in add_view....ntrib.admin)