Jul 08

今天把大概一年前写的 Django 程序拿出来做一些改进,主要这一年来 Django 做了重大升级,虽然兼容性非常不错,但是以前代码里面扩展 manage.py 的部分已经可以重写了,因为 Django 提供了 API 来进行扩展。

因为 Django 根据字符串形式的 namespace 来加载模块,所以我见过不少修改 manage.py,通过重新定义全局变量来修改 Django 设置的代码。我虽然也非常喜欢 hack Django 这个框架,但是我一定不喜欢去改 Django 本身的代码,尽可能通过外部修改来达到自己想要的功能。

trunk 中的 Django 版本提供了扩展 manage.py 的 Command 的 API,只需要在相应的 module 中加入 management/commands 目录,在 commands 下面创建多个 py 就可以得到扩展自定义命令的功能了。譬如加入 management/commands/mycommand.py,就可以通过 manage.py mycommand 来执行,非常的方便。我早起的程序通过修改 manage.py 本身来实现,现在完全可以升级一下了。

但是没想到出问题了,因为 namespace 的问题,Django 在搜索 commands 的方式上是通过 relative import 形式。但是我习惯性把 INSTALLED_APPS 这个变量中的 apps 都写成完整的 namespace,总是以项目目录为顶层包名,结果 Django 就找不到自定义的 commands。

namespaces 一直都是很麻烦,为了省事,我干脆还是把 INSTALLED_APPS 里面的东西改了,结果就可以用了,其实使用相对路径还是比较好的。最近人变懒了,不愿意去深入研究里面的细节了。

Jan 18

发现 Django i18n 的一个中文支持的 bug

如果你的浏览器的 locale 只设置了 zh-cn 的话识别中文应该是没问题的,但是如果有多个 locale,僻如我一次设置了 zh-cn, en-us, de-de,那么中文就不可能被识别出来了。

要了解这个问题,我们先看下这段代码,在 trans_real.py 里面。
主要看一个名为 get_language_from_request 的函数。
从里面打开可以看到程序的基本流程。

  1. 检测 session 中有没有设置过 django_language,如果支持这个语言,那么就返回语言代码
  2. 检测 cookies 里面有没有 django_language,如果支持这个语言,那么就返回语言代码
  3. 最后就是要从 HTTP 头中的 ACCEPT-LANGUAGE 中分析语言。就是在这一步的时候,对中文的支持就存在 bug 了。
  4. 如果都无法识别,那么从返回默认的 settings.LANGUAGE_CODE

为什么会出现多个 locale 无法识别中文的情况呢?我们来看下面的关于分析 ACCEPT_LANGUAGE 的代码:

for lang, unused in parse_accept_lang_header(accept):
    if lang == '*':
        break

    # We have a very restricted form for our language files (no encoding
    # specifier, since they all must be UTF-8 and only one possible
    # language each time. So we avoid the overhead of gettext.find() and
    # look up the MO file manually.

    normalized = locale.locale_alias.get(to_locale(lang, True))
    # 这一步会将浏览器的 locale 字符串标准化,僻如 zh-cn -> zh_CN.gb2312
    if not normalized:
        continue

    # Remove the default encoding from locale_alias
    normalized = normalized.split('.')[0]
    # 这里就把上面的的 zh_CN.gb2312 变成 zh_CN
    if normalized in _accepted:
        # We've seen this locale before and have an MO file for it, so no
        # need to check again.
        return _accepted[normalized]

    for lang in (normalized, normalized.split('_')[0]):
        # 这一步让 lang 成为 (zh_CN, zh),进而匹配 supported 这个支持的语言集合。
        # 这是出错的关键,因为无论 zh 还是 zh_CN 都是无法匹配到的。
        # 在 supported 里面只支持 zh-cn 和 zh-tw,对于 zh 和 zh_CN 是无法匹配的。
        if lang not in supported:
            continue
        langfile = os.path.join(globalpath, lang, 'LC_MESSAGES',
                'django.mo')
        if os.path.exists(langfile):
            _accepted[normalized] = lang
        return lang
return settings.LANGUAGE_CODE
Dec 25

最近用了下 memcached,把 per-4.com 的缓存换了下。

memcached 使用起来比较简单,安装后输入:

# ./memcached -d -m 2048 -l 10.0.0.40 -p 11211

就可以启用 2GB 的缓存了。

用 telnet 127.0.0.1 11211 可以查询 memcached 的运行状态,输入 stats 命令即可。