看来quote()
中的unquote()
和django.contrib.admin.utils
方法不能有效地处理主键中的下划线。具体来说,我有一些看起来像cus_C2xVQnht
的字符串型主键,当我使用django管理界面通过小铅笔图标对其进行编辑时,弹出窗口将显示类似Customer with ID "cusÂxVQnht" doesn't exist. Perhaps it was deleted?
的错误(它正在将 C2 转换为代码点 00C2 ,又名 。其他有效代码点(00C7、00C6、001B等)也是如此)
如果我手动转到客户模型并找到ID,则可以将其调好并进行编辑,但是当主键中带有下划线时,URL编码似乎无法正常工作。
经过大量的挖掘,我设法找到了埋在django.contrib.admin.utils
内部的这两个函数:
def quote(s):
"""
Ensure that primary key values do not confuse the admin URLs by escaping
any '/', '_' and ':' and similarly problematic characters.
Similar to urllib.parse.quote(), except that the quoting is slightly
different so that it doesn't get automatically unquoted by the Web browser.
"""
if not isinstance(s, str):
return s
res = list(s)
for i in range(len(res)):
c = res[i]
if c in """:/_#?;@&=+$,"[]<>%\n\\""":
res[i] = '_%02X' % ord(c)
return ''.join(res)
def unquote(s):
"""Undo the effects of quote(). Based heavily on urllib.parse.unquote()."""
mychr = chr
myatoi = int
list = s.split('_')
res = [list[0]]
myappend = res.append
del list[0]
for item in list:
if item[1:2]:
try:
myappend(mychr(myatoi(item[:2], 16)) + item[2:])
except ValueError:
myappend('_' + item)
else:
myappend('_' + item)
return "".join(res)
它们似乎在管理模板渲染过程中的某个地方被调用,但是我无法弄清楚在何处/多久一次/所有位置,因此我决定做一个快速的猴子补丁来确定它是否值得追求。解决方案:我将quote()
和unquote()
中的所有下划线更改为quote
中问题字符列表中的下划线,例如...
'_%02X'
中的quote()
成为'.%02X'
split('_')
中的unquote()
成为split('.')
myappend('_' + item)
中的unquote()
成为myappend('.' + item)
执行此操作后,管理员可以正常工作,并且似乎附加到相关字段上的编辑图标的链接指向正确的模型实例,因此我可以通过单击铅笔图标进行编辑,而不会收到错误消息如上所述。
所有这些,我似乎找不到能够安全地覆盖这两种方法的方法。我真的不希望更改主键来消除下划线,因为数据库中有很多链接模型,而且看起来这将是一个巨大的痛苦。该修补程序似乎更容易且更可靠,并且鉴于它在Django的早期版本中可以正常工作,所以我认为实现它不是一个坏主意。
那么,如何覆盖这些方法?或者,作为一个相关问题,我可以在模型的__str__
方法中做些什么来缓解此问题?我会比开始编写覆盖Django admin内部构件的自定义类快得多。如果没有其他解决方案,我需要一些帮助来适当地重组数据库以调整主键,但是我可以说这些键在我正在运行的运行Django 1.11.6的“旧”站点上可以正常使用和Python 2.7.9(与当前的Django 2.1.1和Python 3.6.5相比)
请让我知道是否可以提供更多信息。谢谢!!
答案 0 :(得分:0)
此问题在django 2.2中已修复。参见https://github.com/django/django/commit/e4df8e6dc021fa472fa77f9b835db74810184748