如何使Django的slugify与Unicode字符串正常工作?

问题描述 投票:36回答:8

我能做些什么来防止slugify过滤器从剥离出的非ASCII字母数字字符? (我使用Django 1.0.2)

cnprog.com有问题的URL中国字,所以我看着他们的代码。他们不是在模板中使用slugify,而不是他们调用Question模型得到永久链接这个方法

def get_absolute_url(self):
    return '%s%s' % (reverse('question', args=[self.id]), self.title)

难道他们slugifying的网址或不?

python django unicode django-templates slug
8个回答
92
投票

有一个叫unidecode Python包,我已经通过了askbot Q&A论坛,它可以很好地用于基于拉丁文字母和甚至看起来合理希腊:

>>> import unidecode
>>> from unidecode import unidecode
>>> unidecode(u'διακριτικός')
'diakritikos'

它与亚洲语言的奇怪的事情:

>>> unidecode(u'影師嗎')
'Ying Shi Ma '
>>> 

这是否有意义?

在askbot我们计算像这样蛞蝓:

from unidecode import unidecode
from django.template import defaultfilters
slug = defaultfilters.slugify(unidecode(input_text))

23
投票

Mozilla的网站团队一直在努力的实现:在https://github.com/mozilla/unicode-slugify http://davedash.com/2011/03/24/how-we-slug-at-mozilla/示例代码


15
投票

此外,slugify的Django的版本不使用re.UNICODE标志,所以它不会甚至试图理解\w\s的意义,因为它涉及到非ASCII字符。

这种定制的版本为我工作很好:

def u_slugify(txt):
        """A custom version of slugify that retains non-ascii characters. The purpose of this
        function in the application is to make URLs more readable in a browser, so there are 
        some added heuristics to retain as much of the title meaning as possible while 
        excluding characters that are troublesome to read in URLs. For example, question marks 
        will be seen in the browser URL as %3F and are thereful unreadable. Although non-ascii
        characters will also be hex-encoded in the raw URL, most browsers will display them
        as human-readable glyphs in the address bar -- those should be kept in the slug."""
        txt = txt.strip() # remove trailing whitespace
        txt = re.sub('\s*-\s*','-', txt, re.UNICODE) # remove spaces before and after dashes
        txt = re.sub('[\s/]', '_', txt, re.UNICODE) # replace remaining spaces with underscores
        txt = re.sub('(\d):(\d)', r'\1-\2', txt, re.UNICODE) # replace colons between numbers with dashes
        txt = re.sub('"', "'", txt, re.UNICODE) # replace double quotes with single quotes
        txt = re.sub(r'[?,:!@#~`+=$%^&\\*()\[\]{}<>]','',txt, re.UNICODE) # remove some characters altogether
        return txt

注意最后的正则表达式替换。这是一种解决方法,以与更强大的表达r'\W',这似乎或者去掉一些非ASCII字符或不正确地重新编码它们,如下面的python解释会话所示的问题:

Python 2.5.1 (r251:54863, Jun 17 2009, 20:37:34) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import re
>>> # Paste in a non-ascii string (simplified Chinese), taken from http://globallives.org/wiki/152/
>>> str = '您認識對全球社區感興趣的中國攝影師嗎'
>>> str
'\xe6\x82\xa8\xe8\xaa\x8d\xe8\xad\x98\xe5\xb0\x8d\xe5\x85\xa8\xe7\x90\x83\xe7\xa4\xbe\xe5\x8d\x80\xe6\x84\x9f\xe8\x88\x88\xe8\xb6\xa3\xe7\x9a\x84\xe4\xb8\xad\xe5\x9c\x8b\xe6\x94\x9d\xe5\xbd\xb1\xe5\xb8\xab\xe5\x97\x8e'
>>> print str
您認識對全球社區感興趣的中國攝影師嗎
>>> # Substitute all non-word characters with X
>>> re_str = re.sub('\W', 'X', str, re.UNICODE)
>>> re_str
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\xa3\xe7\x9a\x84\xe4\xb8\xad\xe5\x9c\x8b\xe6\x94\x9d\xe5\xbd\xb1\xe5\xb8\xab\xe5\x97\x8e'
>>> print re_str
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX?的中國攝影師嗎
>>> # Notice above that it retained the last 7 glyphs, ostensibly because they are word characters
>>> # And where did that question mark come from?
>>> 
>>> 
>>> # Now do the same with only the last three glyphs of the string
>>> str = '影師嗎'
>>> print str
影師嗎
>>> str
'\xe5\xbd\xb1\xe5\xb8\xab\xe5\x97\x8e'
>>> re.sub('\W','X',str,re.U)
'XXXXXXXXX'
>>> re.sub('\W','X',str)
'XXXXXXXXX'
>>> # Huh, now it seems to think those same characters are NOT word characters

我不确定是什么问题上面,但我猜,这源于“whatever is classified as alphanumeric in the Unicode character properties database,”那是如何实现的。我听说蟒蛇3.x中有更好的Unicode处理高优先级,所以这可能是已经固定的。或者,也许这是正确的python的行为,我滥用Unicode和/或中国的语言。

现在,一个解决办法是,以避免字符类,并根据明确定义的字符集替换。


11
投票

使用Django> = 1.9,django.utils.text.slugify具有allow_unicode参数:

>>> slugify("你好 World", allow_unicode=True)
"你好-world"

如果你使用Django <= 1.8(你不应该因为2018年4月),你可以pick up the code from Django 1.9


9
投票

恐怕Django的蛞蝓的定义是指ASCII,尽管Django文档没有明确说明这一点。这是defaultfilters为slugify的源...你可以看到,值转换为ASCII码,在错误的情况下的“忽略”选项:

import unicodedata
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
return mark_safe(re.sub('[-\s]+', '-', value))

在此基础上,我猜cnprog.com没有使用正式的slugify功能。你不妨去适应Django的片断,如果你想有一个不同的行为之上。

话虽如此,不过,对于URL中的RFC确实状态,非US-ASCII字符(或者,更具体地讲,任何事情比其他字母,数字和$ -_。+!*'())应该用%16进制编码。如果你看一下,你的浏览器发送(比如说,使用Firebug)的实际原始GET请求,你会看到,中国字符其实在发送之前编码...浏览器只是使它漂亮的显示器看。我怀疑这就是为什么slugify只ASCII,FWIW坚持。


7
投票

你可能想看看:https://github.com/un33k/django-uuslug

它会照顾这两个“U”给你的。 ü独特和U采用Unicode。

它会为你无忧无虑做的工作。


4
投票

这是我使用:

http://trac.django-fr.org/browser/site/trunk/djangofr/links/slughifi.py

SlugHiFi是定期slugify的包装,用它取代国家字符与他们的英文字母同行的差异。

因此,而不是“A”你拿“A”,而不是“L” =>“L”,等等。


1
投票

我感兴趣的,允许段塞,这就是为什么我试图基准一些对相同字符串的可用工具只有ASCII字符:

  • Unicode SlugifyIn [5]: %timeit slugify('Παίζω τρέχω %^&*@# και γ%^(λώ la fd/o', only_ascii=True) 37.8 µs ± 86.7 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 'paizo-trekho-kai-glo-la-fdo'
  • Django UuslugIn [3]: %timeit slugify('Παίζω τρέχω %^&*@# και γ%^(λώ la fd/o') 35.3 µs ± 303 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 'paizo-trekho-kai-g-lo-la-fd-o'
  • Awesome SlugifyIn [3]: %timeit slugify('Παίζω τρέχω %^&*@# και γ%^(λώ la fd/o') 47.1 µs ± 1.94 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each) 'Paizo-trekho-kai-g-lo-la-fd-o'
  • Python SlugifyIn [3]: %timeit slugify('Παίζω τρέχω %^&*@# και γ%^(λώ la fd/o') 24.6 µs ± 122 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 'paizo-trekho-kai-g-lo-la-fd-o'
  • django.utils.text.slugify UnidecodeIn [15]: %timeit slugify(unidecode('Παίζω τρέχω %^&*@# και γ%^(λώ la fd/o')) 36.5 µs ± 89.7 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 'paizo-trekho-kai-glo-la-fdo'
© www.soinside.com 2019 - 2024. All rights reserved.