显示标签为“django”的帖子。显示所有帖子
显示标签为“django”的帖子。显示所有帖子

2008年8月10日

有备无患:truncatehanzi - 按字数截取中文的 Django template filter

Django 自带的一个模板过滤器(Template Filter)truncatewords,可以按单词数截取字符串生成摘要。不过,truncatewords 只能用空格来分隔单词,对汉字就无能为力了。所以,我写了另一个模板过滤器 truncatehanzi,它通过 unicode 编码来区分西方字符和 CJK 汉字,对包括英语字母在内的西方字符以空格或其他符号来分隔并计算单词数,而对 CJK 汉字则是按字数计算。

使用方法和 truncatewords 一样,
{{ value|truncatehanzi:6 }}
如果 value 为“截取English、Γρεεκ等字母语言和CJK汉字。”,输出“截取English、Γρεεκ等字...”。

代码分为两个部分,截取汉字的脚本 truncate_hanzi.py 和生成过滤器的脚本 filters.py。

truncate_hanzi.py

 # -*- coding: UTF-8 -*-

import re
from django.utils.encoding import force_unicode
from django.utils.functional import allow_lazy

def truncate_hanzi(s, num):
    s = force_unicode(s)
    length = int(num)
    if length <= 0:
        return u'...'
    # Set up regex for alphanumeric characters
    # \u00c0-\u02af: Latin
    # \u0370-\u1fff: Greek and alphabet characters in other language
    re_alnum = re.compile(ur'[a-zA-Z0-9_\-\u00c0-\u02af\u0370-\u1fff]', re.U)
    # Set up regex for hanzi
    # \u3040-\ufaff: CJK characters
    re_hanzi = re.compile(ur'[\u3040-\ufaff]', re.U)
    hanzi = u''
    hanzi_len = 0
    word_temp = u''
    for char in s:
        # Check for alphabet characters
        if re_alnum.match(char):
            word_temp += char
            continue
        if word_temp:
            hanzi += word_temp
            hanzi_len += 1
            word_temp = ''
        # Check for length
        if hanzi_len >= length:
            if not hanzi.endswith('...'):
                hanzi += '...'
                break
        # Check for hanzi
        if re_hanzi.match(char):
            hanzi_len += 1
        hanzi += char
    hanzi += word_temp
    return hanzi
truncate_hanzi = allow_lazy(truncate_hanzi, unicode)

def demo():
    print truncate_hanzi('截取段落工具,支持English、Γρεεκ等字母语言和CJK汉字。', 6)
    print truncate_hanzi('截取段落工具,支持English、Γρεεκ等字母语言和CJK汉字。', 11)
    print truncate_hanzi('截取段落工具,支持English、Γρεεκ等字母语言和CJK汉字。', 20)

if __name__ == '__main__':
    demo()

filters.py

 # -*- coding: UTF-8 -*-
from django.template import Library
from django.template.defaultfilters import stringfilter

register = Library()

@stringfilter
def truncatehanzi(value, arg):
    """
    Truncates a string after a certain number of words including
    alphanumeric and CJK characters.

    Argument: Number of words to truncate after.
    """
    from truncate_hanzi import truncate_hanzi
    try:
        length = int(arg)
    except ValueError: # Invalid literal for int().
        return value # Fail silently.
    return truncate_hanzi(value, length)
truncatehanzi.is_safe = True

register.filter('truncatehanzi', truncatehanzi)

2008年7月18日

有备无患:在 Django 上使用 reCAPTCHA 生成验证码

reCAPTCHA 是由 CAPTCHA 的原作者,也是人类计算学(Human Computation)的专家 Luis von Ahn 所设计一个免费的 anti-bot 服务,它在随机提取的单词上加上一些扭曲和可阻碍电脑自动识别的噪声,以利用人类远强于电脑的图形识别能力来检查屏幕前坐着的是人类,还是 bot 或自动脚本。因此,reCAPTCHA 经常被用于生成网站注册或者文章评论的验证码。

除此之外,reCAPTCHA 还有一项非常公益的设计。虽然 OCR (全称 Optical Character Recognitio,中译光学字元识别)早已被投入使用,但是对那些印刷模糊或分辨度不足的实体书籍而言,OCR 的识别率难说完美。而 reCAPTCHA 将这些上无法由电脑自动识别的单词扫描下来,以图片的形式存入数据库,并在网络上以验证码(CAPTCHA)的形式交由人类来破译,而破译的结果将被用于帮助世界上的实体书加快数字化的进程。

你可能会问,既然电脑无法识别这些单词,那么 reCAPTCHA 系统又怎么知道用户填写的是正确的验证码呢?reCAPTCHA 的网站上有详细的解释

一个不能被 OCR 正确识别的新单词,总是会和另一个已知答案的单词一起提交给用户。接下来用户需要同时识别这两个单词。如果用户正确识别出其中已知答案的那个单词,系统即假设用户所识别出的未知答案的新单词也可能是正确的。如果其他的用户也识别出相同的答案,系统则逐步提高这种可能性。最终当这种可能性超过某个阈值时,系统就可以将其认作已知答案的单词了。

言归正传,reCAPTCHA 有丰富的 API,包括对 PHP、JAVA、Ruby,自然还有 Python 的支持。如果要在 Django 上使用 reCAPTCHA,我们可以从 PyPI 下载 reCAPTCHA 的 Python 客户端,或者直接用 easy_install 安装。


# easy_install recaptcha-client

不知道为什么,虽然安装成功了,但是我不能导入 recaptcha 模块(wyt:import captcha 或 import recaptcha 都没有用,哪位如果顺利导入过的话,能不能分享一下你的经验呢?先谢了),所以我是把 egg 中的脚本直接复制到我的 Django 项目目录下了。

接下来,我们需要在 reCAPTCHA 注册并申请一对公钥/私钥,并将其保存在 settings.py 中。

settings.py

1 # reCAPTCHA keys
2 RECAPTCHA_PUBLIC_KEY = "6LdqgAIAAAUSHDmo4IIBmsjUsduAUMUoBDZc3J_T"
3 RECAPTCHA_PRIVATE_KEY = "6LdqgAIAAbjsJKLbj2KOjPO6D4isfJ_AzLSO_256"

然后,我们要为注册表单添加验证码了。在 Django Snippets 上,oggy 已经将 reCAPTCHA 抽象成一个 RecaptchaForm 类,所以我们只要让注册表单(Registration Form)继承这个类,就可以为注册表单添加验证码的功能了。由于 Django newform 默认是根据 field 的定义顺序来生成表单,所以在继承 RecaptchaForm 的时候,也应该把它放在最后继承。

recaptcha/forms.py

01 from django import newforms as forms
02 from django.newforms import ValidationError
03 from django.conf import settings
04 from recaptcha import captcha
05 
06 
07 class RecaptchaWidget(forms.Widget):
08     """ A Widget which "renders" the output of captcha.displayhtml """
09     def render(self, *args, **kwargs):
10         return captcha.displayhtml(settings.RECAPTCHA_PUBLIC_KEY)
11 
12 class DummyWidget(forms.Widget):
13     """
14     A dummy Widget class for a placeholder input field which will
15     be created by captcha.displayhtml
16 
17     """
18     # make sure that labels are not displayed either
19     is_hidden=True
20     def render(self, *args, **kwargs):
21         return ''
22 
23 class RecaptchaForm(forms.Form):
24     """
25     A form class which uses reCAPTCHA for user validation.
26    
27     If the captcha is not guessed correctly, a ValidationError is raised
28     for the appropriate field
29     """
30     recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
31     recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
32 
33     def __init__(self, request, *args, **kwargs):
34         super(RecaptchaForm, self).__init__(*args, **kwargs)
35         self._request = request
36 
37     def clean_recaptcha_response_field(self):
38         if 'recaptcha_challenge_field' in self.cleaned_data:
39             self.validate_captcha()
40         return self.cleaned_data['recaptcha_response_field']
41 
42     def clean_recaptcha_challenge_field(self):
43         if 'recaptcha_response_field' in self.cleaned_data:
44             self.validate_captcha()
45         return self.cleaned_data['recaptcha_challenge_field']
46 
47     def validate_captcha(self):
48         rcf = self.cleaned_data['recaptcha_challenge_field']
49         rrf = self.cleaned_data['recaptcha_response_field']
50         ip_address = self._request.META['REMOTE_ADDR']
51         check = captcha.submit(rcf, rrf, settings.RECAPTCHA_PRIVATE_KEY, ip_address)
52         if not check.is_valid:
53             raise ValidationError('You have not entered the correct words')


user/forms.py

01 from myproject.recaptcha.forms import RecaptchaForm
02 
03 class RegistrationForm(forms.Form):
04     """
05     Form for registering a new user account.
06      
07     """
08 
09 class RegistrationFormWithRecaptcha(RegistrationForm, RecaptchaForm):
10     """
11     Subclass of ``RegistrationForm`` and ``RecaptchaForm`` which can tell whether its user
12     is a human or a computer.
13 
14     """

2007年9月23日

有备无患:在支持FastCGI的Lighttpd服务器上部署Django的笔记

Django是一个鼓励快速开发和简练设计的基于Python的高级网络框架。这篇文章详细记录了我在本地部署Django的过程,服务器是支持FastCGI的Lighttpd,操作系统是Gentoo Linux。条条大路通罗马,网上也有很多Django的教程,但这可能是更适合Django本地学习和更有效率的一条捷径。欢迎FIX-ME

开读之前,请注意一下时效性:Django版本:0.96,Lighttpd版本:1.4.18,日期:2007年9月。

一、安装Lighttpd


无论什么时候,在Gentoo上安装软件都是一件愉快的事情,因为我们可以通过USE flags来选择需要安装的包。如果你和我一样记不住这些USE flags的具体意义,不妨用euse查看一下,例如:


# euse -i fastcgi

Lighttpd包括下列USE flags:

  • bzip2 - 为mod_compress提供bzip2支持
  • fastcgi - 提供FastCGI支持,使用Django或PHP都需要FastCGI来提升性能
  • gdbm -提供GNU数据库支持
  • ipv6 - 提供IPv6支持
  • lua - 为mod_cml提供Lua支持
  • memcache - 为for mod_cml提供内存缓存支持
  • mysql - 为mod_mysql_vhost提供MySQL的设定支持
  • pcre - 为Lighttpd配置文件中正则表达式提供PCRE支持
  • php - 提供mod_fastcgi/php的设定支持
  • rrdtool - 为一些统计图形提供RRDtool支持
  • ssl - 为HTTPS提供OpenSSL支持
  • doc -提供安装文档支持
  • fam - 为减少stat()的响应数,提供fam/gamin支持
  • ldap - 提供LDAP支持
  • minimal - 最小化安装,不包括安装文档和不使用的模块
  • webdav - 提供WebDAV支持
  • xattr -提供额外的属性支持

和Apache有内嵌的Python解释器(mod_python)不同,Lighttpd只能通过CGI或者FastCGI方式执行Python脚本。CGI的先天不足导致它在大型Web应用上效率不佳。FastCGI在设计上继承了CGI不受操作系统、服务器和开发语言的限制,又利用一个类似常驻型CGI的设计大幅提高了执行效率。所以,我们在安装Lighttpd的时候,需要把fastcgi选入USE flags。至于其他选项,大家就各取所需了。


# echo "www-servers/lighttpd fastcgi ssl fam" >> /etc/portage/package.use
# emerge -av lighttpd

二、安装Django


接下来安装Django。本地安装Django很轻松,直接emerge就可以了:


# echo "dev-python/django examples sqlite" >> /etc/portage/package.use
# emerge -av django

emerge完以后,我们试试Django是否安装正确。运行Python,输入:


>>> from django.core.handlers.wsgi import WSGIHandler

也可以把其他Django的组件导入Python。如果没有出错信息,表示Django安装完成了。

三、安装flup


flup是一个基于Python的架设FastCGI服务器的模块,这也是需要安装的。


# emerge -av flup

然后,我们试试flup是否安装正确。运行python,输入:


>>> from flup.server.fcgi_fork import WSGIServer

如果没有出错信息,表示flup安装完成。

四、配置Lighttpd和FastCGI


Lighttpd自带的配置相当完整,只需要稍作修改就可以让Lighttpd支持FastCGI。
首先修改lighttpd.conf,自定义Lighttpd的主目录和日志目录等。


# {{{ variables
var.basedir = "/var/www/localhost"
var.logdir = "/var/log/lighttpd"
var.statedir = "/var/lib/lighttpd"
# }}}

你可以把所有的配置都塞到lighttpd.conf中,但是,Lighttpd默认你把CGI和FastCGI的配置文件分别放到了mod_cgi.conf和mod_fastcgi.conf文件中,并在lighttpd.conf中include它们。这样的结构更加简洁明白,本文也采用这种方法配置Lighttpd。


# {{{ includes
include "mime-types.conf"
include "mod_cgi.conf"
include "mod_fastcgi.conf"
# }}}

为2.6核心版本的Linux优化性能。


# for >= linux-2.6
server.event-handler = "linux-sysepoll"

指定用mod_fastcgi处理.py文件。


static-file.exclude-extensions = (".php", ".pl", ".py", ".cgi", ".fcgi")

在lighttpd.conf中,我们还可以设定一些其他参数,比如server.max-worker、server.max-keep-alive-requests、server.max-keep-alive-idle等,来优化Lighttpd的性能,让Lighttpd在大型的Web应用中表现更好。

接下来,修改mod_cgi.conf。


server.modules += ("mod_cgi")

alias.url = (
"/cgi-bin/" => var.basedir + "/cgi-bin/"
)

$HTTP["url"] =~ "^/cgi-bin/" {
    # disable directory listings
    dir-listing.activate = "disable"
    # only allow cgi's in this directory
    cgi.assign = (
        ".pl" => "/usr/bin/perl",
        ".py" => "/usr/bin/python",
        ".cgi" => "/usr/bin/perl"
    )
}

修改mod_fastcgi.conf。



server.modules += ("mod_fastcgi")
fastcgi.server = (
    "/main.fcgi" => (
        "main" => (
            "socket" => var.basedir + "/htdocs/tmp/django.socket",
        )
    ),
)

最后,我们重新启动Lighttpd,并把lighttpd加入启动项。


# /etc/init.d/lighttpd restart
# rc-update add lighttpd default

这样子就万事OK了,我们已经成功的把Django部署在支持FastCGI的Lighttpd服务器上了。接下去就可以开始设计自己的Django项目了。

订阅我的博客

搜索我的博客

正在加载...

我的豆瓣广播

分享阅读

豆瓣秀

休斯敦火箭

我的文章归档

版权申明