Как заставить Django slugify правильно работать со строками Unicode?

что я могу сделать, чтобы предотвратить slugify фильтр от зачистки не-ASCII символы? (Я использую Django 1.0.2)

cnprog.com имеет китайские символы в URL-адресах вопроса, поэтому я посмотрел в их коде. Они не используют slugify в шаблонах вместо этого они вызывают этот метод в Question модель, чтобы получить permalinks

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

они slugifying URLs или нет?

7 ответов


есть пакет python под названием unidecode что я принял для форума 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))

команда веб-сайта Mozilla работает над реализацией : https://github.com/mozilla/unicode-slugify пример кода в http://davedash.com/2011/03/24/how-we-slug-at-mozilla/


кроме того, версия Django slugify не использует 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'-', 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 и/или китайский язык.

для теперь обходной путь-избегать классов символов и делать замены на основе явно определенных наборов символов.


Я боюсь, что определение слизняка 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 не использует официальный


С Джанго >= 1.9, django.utils.text.slugify есть


возможно, вы захотите посмотреть: https://github.com/un33k/django-uuslug

Он позаботится о обоих " U " для вас. U и U в Unicode.

Это сделает работу для вас хлопот бесплатно.


Это то, что я использую:

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

SlugHiFi-это обертка для регулярного slugify, с той разницей, что она заменяет национальные символы их английскими алфавитными аналогами.

Итак, вместо " Ą "вы получаете" A", вместо" Ł "= > " L " и так далее.