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

python django unicode django-templates slug

19873 观看

8回复

48619 作者的声誉

如何防止slugify过滤器剥离非ASCII字母数字字符?(我正在使用Django 1.0.2)

cnprog.com的网址中包含中文字符,因此我查看了他们的代码。他们没有slugify在模板中使用,而是在Question模型中调用此方法来获取永久链接

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

他们是不是在撒谎URL?

作者: Imran 的来源 发布者: 2009 年 3 月 31 日

回应 (8)


9

72525 作者的声誉

恐怕django的slug定义意味着ascii,尽管django文档未明确说明。这是slugify的默认过滤器的来源...您可以看到将值转换为ascii,如果出现错误,则使用'ignore'选项:

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字符(或更具体地说,除了字母数字和$ -_。+!*'()之外的任何字符)都应该使用%hex表示法进行编码。如果查看浏览器发送的实际原始GET请求(例如,使用Firebug),您会发现实际上是在发送之前对汉字进行了编码...浏览器只是使它看起来很漂亮。我怀疑这就是为什么Slugify坚持只使用ascii的原因。

作者: Jarret Hardie 发布者: 31.03.2009 06:30

15

1410 作者的声誉

此外,Django的slugify版本不使用re.UNICODE标志,因此它甚至不会尝试理解s的含义,\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

我不确定上面是什么问题,但是我猜测这是由于“ 在Unicode字符属性数据库中归类为字母数字的任何内容 ”以及如何实现的。我听说python 3.x在更好的unicode处理方面具有较高的优先级,因此可能已经解决了。或者,也许这是正确的python行为,并且我滥用了unicode和/或中文。

目前,一种解决方法是避免使用字符类,并基于显式定义的字符集进行替换。

作者: Arthur Hebert 发布者: 25.10.2010 09:45

92

8298 作者的声誉

决定

我在Askbot Q&A论坛上采用了一个名为unidecode的python软件包,它适用于基于拉丁语的字母,甚至对于希腊人来说也很合理:

>>> 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))
作者: Evgeny 发布者: 27.10.2010 06:58

4

694 作者的声誉

这是我用的:

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

SlugHiFi是常规slugify的包装,不同之处在于它用英语字母对应的字符替换了国家字符。

因此,您得到的不是“Ą”,而是“ A”,而不是“Ł” =>“ L”,依此类推。

作者: mhl666 发布者: 06.01.2011 03:47

7

9815 作者的声誉

您可能要看一下:https : //github.com/un33k/django-uuslug

它将为您处理两个“ U”。ü独特和ü在Unicode中。

它将为您轻松地完成工作。

作者: un33k 发布者: 31.05.2011 06:26

23

1358 作者的声誉

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

作者: Open SEO 发布者: 12.08.2011 02:55

11

15080 作者的声誉

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

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

如果使用Django <= 1.8(自2018年4月起不应该使用),则可以从Django 1.9中获取代码

作者: Antoine Pinsard 发布者: 23.03.2016 10:30

1

3902 作者的声誉

我感兴趣的是只允许slug中的ASCII字符,这就是为什么我尝试对同一字符串的一些可用工具进行基准测试:

  • Unicode Slugify

    In [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 Uuslug

    In [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'
    
  • 很棒的Slugify

    In [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 Slugify

    In [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.slugifyUnidecode

    In [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'
    
作者: raratiru 发布者: 07.02.2019 05:27
32x32