<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>年华转瞬 &#187; Django开发</title>
	<atom:link href="http://blog.xiaket.org/tag/django/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.xiaket.org</link>
	<description>xiaket 的网志</description>
	<lastBuildDate>Sat, 21 Aug 2010 02:31:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>自定义python类方法实现加减比较操作</title>
		<link>http://blog.xiaket.org/2010/06/19/python-class-methods-customize/</link>
		<comments>http://blog.xiaket.org/2010/06/19/python-class-methods-customize/#comments</comments>
		<pubDate>Sat, 19 Jun 2010 03:34:04 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Python开发]]></category>
		<category><![CDATA[Django开发]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=429</guid>
		<description><![CDATA[最近在写一个组内项目, 需要把一群一群的类似字典的对象实现类似相加的逻辑. 为了让代码更好看逻辑性更强, 偶玩了下python的自定义类方法.

首先更详细地介绍下背景: 我们需要在Django中记录一个模型变化的历史, 我们有需求会将这个模型的某个实例的现在的状态和它的若干个变化历史相加. 为此, 我们用Django的序列化方法把这个实例导出成一个json字符串, 然后用这个字符串实例化一个类似字典的类. 变化历史的各种相加相减操作就是通过自定义这个类似字典的类的特殊的类方法而实现的.

既然这个类是用一个json字符串来实例化的, 我们简单地将这个类的名字定为Json, 其初始化方法类似:

<span class="readmore"><a href="http://blog.xiaket.org/2010/06/19/python-class-methods-customize/" title="自定义python类方法实现加减比较操作">阅读全文——共3611字</a></span>]]></description>
			<content:encoded><![CDATA[<p>最近在写一个组内项目, 需要把一群一群的类似字典的对象实现类似相加的逻辑. 为了让代码更好看逻辑性更强, 偶玩了下python的自定义类方法.</p>
<p>首先更详细地介绍下背景: 我们需要在Django中记录一个模型变化的历史, 我们有需求会将这个模型的某个实例的现在的状态和它的若干个变化历史相加. 为此, 我们用Django的序列化方法把这个实例导出成一个json字符串, 然后用这个字符串实例化一个类似字典的类. 变化历史的各种相加相减操作就是通过自定义这个类似字典的类的特殊的类方法而实现的.</p>
<p>既然这个类是用一个json字符串来实例化的, 我们简单地将这个类的名字定为Json, 其初始化方法类似:</p>
<pre class="brush: python;">
class Json(object):
    &quot;&quot;&quot;
    This class is used to hold a model instance's information.
    &quot;&quot;&quot;
    def __init__(self, _json_string):
        &quot;&quot;&quot;
        This class is initialized with a json formatted string.

        The json formatted string is provided using django's serialization
        tool, which accept a QuerySet or a list as an object. So here we have
        to retrieve the first(and the only) object in the list.
        &quot;&quot;&quot;
        self.init_string = _json_string
        _dictionary = json.loads(_json_string)[0]
        self.model = _dictionary['model']
        self.object_id = self.pk = _dictionary['pk']
        self.fields = _dictionary['fields']
</pre>
<p>看起来很长一段代码, 实际上只是定义了若干基本类属性而已. 这些属性包括初始化字符串, 被序列化的实例的模型和主键, 以及一个存放原实例的属性的self.fields字典. 如果对这些名字不太熟悉可以自行去围观下django的序列化工具的json输出, <a href="http://docs.djangoproject.com/en/dev/topics/serialization/#topics-serialization">这个页面</a>上有例子.</p>
<p>Json这个类只是存放了类的序列化的内容, 为了记录历史, 我们需要将两个这样的类相减以得出变化. 在定义相减这个操作之前, 我们需要定义一个用来存放历史变化的类:</p>
<pre class="brush: python;">
class Jsondiff(object):
    &quot;&quot;&quot;
    This object is used to save substraction results from two Json objects.
    &quot;&quot;&quot;
    def __init__(self, _diffdict):
        &quot;&quot;&quot;
        Input format specification
        --------------------

        the input json string represents a dictionary of differences. Like:
            {
                'fieldname': (newvalue, oldvalue),
            }
        It also contains a special field named '_model', containing the name of
        the model and the id of the object.
        A realworld example is provided below:
            {
                u'_object': (u'gsdb.server', 1),
                u'tag': (u'12535', u'12537'),
            }
        &quot;&quot;&quot;
        self.fields = _diffdict
        self.model = self.fields['_object'][0]
        self.object_id = self.fields['_object'][1]
        del self.fields['_object']
</pre>
<p>同样的, 看上去很长的代码只是干了很有限的几件事情: 从类初始化参数中拿到实例的模型和主键, 作为类属性保存起来, 并从初始化参数中删除这个用于存放元信息的特殊键值, 这样我们再后面进行各种操作时就不需要再讨论这个特殊的键值了.</p>
<p>OK, 两个类已经定义完了, 我们开始定义一些特殊的类方法. 首先, 我们需要定义一个方法能够比较两个Json实例的区别以得到一个Jsondiff实例. 我们需要实现下面的代码:</p>
<pre class="brush: python;">
jsondiff0 = json1 - json2
</pre>
<p>为此, 我们需要自定义Json的__sub__方法:</p>
<pre class="brush: python;">
    def __sub__(self, _json):
        &quot;&quot;&quot;
        This method would substract two Json objects and give a Jsondiff
        object.

        The object represented by _json is the old object, 'new - old' would
        make more sense than 'old - new'.
        &quot;&quot;&quot;
        _diffdict = {
            '_object': (self.model, self.object_id),
        }
        for _field in self.fields:
            if self.fields[_field] != _json.fields[_field]:
                _diffdict[_field] = (self.fields[_field], _json.fields[_field])
        return Jsondiff(_diffdict)
</pre>
<p>这段代码很容易理解. 你不是要比较两个Json对象吗? 既然所有相关的值都在self.fields里面, 那我们就一个个地进行比较, 如果有不同的地方就按照一个预设的格式记录下来. 我们这儿将这些记录放到一个字典里面, 处理完每个键后就直接将这个字典作为初始化Jsondiff类的参数实例化一个Jsondiff对象, 然后就可以直接返回这个对象了. 到这儿, 我们已经实现了前面写的json对象相减的代码. 下一步, 既然已经写了减法, 不写加法就不合理了:</p>
<pre class="brush: python;">
# Since we can do:
jsondiff0 = json1 - json2
# we wanna have this, too:
json1 = json2 + jsondiff0
</pre>
<p>好吧, 我们现在来定义加法:</p>
<pre class="brush: python;">
    def __add__(self, _jsondiff):
        &quot;&quot;&quot;
        This method would add a Json to a Jsondiff object.
        &quot;&quot;&quot;
        _sum = copy.deepcopy(self)
        for _field in _jsondiff.fields:
            _new, _old = _jsondiff.fields[_field]
            if _sum.fields[_field] != _old:
                raise RuntimeError
            else:
                _sum.fields[_field] = _new
        return _sum
</pre>
<p>此处, 为了避免执行加法后改变已有实例的属性, 我们用deepcopy将原来的Json对象复制了一份. 然后从Jsondiff里面读键值, 对于每个键值看看diff里的内容和当前的实例的属性有没有冲突, 以确定我们要进行的操作是不是合法的. 如果一切都没问题, 返回一个Json对象.</p>
<p>为了让这一切更和谐, 我们还定义了一个Jsondiff的__add__方法, 这样我们哪天把前后顺序弄反了也不会有多大的事情:</p>
<pre class="brush: python;">
    def __add__(self, _json):
        &quot;&quot;&quot;
        This method would add a Jsondiff to a Json object.
        &quot;&quot;&quot;
        return _json + self
</pre>
<p>有了这段代码, 我们在进行Json对象和Jsondiff对象的加法时可以肆无忌惮地将Jsondiff放在前面或者后面了. 而如果没有这个对象, 我们只能老老实实地将Jsondiff放在后面.</p>
<p>后来在使用中, 我发现我经常会需要比较两个Json对象的创建时间来比较哪个更新一点. 为了简化这些操作, 我给Json对象添加了一个date属性, 记录原模型的修改时间. 这样我每次只需要比较Json对象的date属性即可. 既然已经有了这个属性, 我们可以更进一步的定义__cmp__方法来使Json对象的比较更美观更和谐:</p>
<pre class="brush: python;">
    def __cmp__(self, _json):
        &quot;&quot;&quot;
        This method would compare two Json object's date.
        &quot;&quot;&quot;
        return cmp(self.date, _json.date)
</pre>
<p>定义了这个方法后, 我们能做类型下面的操作:</p>
<pre class="brush: python;">
&gt;&gt;&gt; json1 &gt; json2
True
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2010/06/19/python-class-methods-customize/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>新年Checklist</title>
		<link>http://blog.xiaket.org/2010/02/10/lunar-new-year-checklist/</link>
		<comments>http://blog.xiaket.org/2010/02/10/lunar-new-year-checklist/#comments</comments>
		<pubDate>Tue, 09 Feb 2010 16:56:26 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[其它]]></category>
		<category><![CDATA[Django开发]]></category>
		<category><![CDATA[旅游]]></category>
		<category><![CDATA[电影]]></category>
		<category><![CDATA[电视]]></category>
		<category><![CDATA[读书笔记]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=318</guid>
		<description><![CDATA[下面是截止到02.21的计划任务(psedo-crontab)

手机电子书



<span class="readmore"><a href="http://blog.xiaket.org/2010/02/10/lunar-new-year-checklist/" title="新年Checklist">阅读全文——共436字</a></span>]]></description>
			<content:encoded><![CDATA[<p>下面是截止到02.21的计划任务(psedo-crontab)</p>
<h3>手机电子书</h3>
<ol>
<li><span style="text-decoration: line-through;">英雄志看完第二遍</span></li>
<li><span style="text-decoration: line-through;">白夜行看完</span></li>
</ol>
<h3>实体书</h3>
<ol>
<li>Pro Django看完两章</li>
<li>Treasure Island看完</li>
</ol>
<h3>电影电视</h3>
<ol>
<li><span style="text-decoration: line-through;">Mary and Max</span>(看了一半, 觉得没意思.)</li>
<li><span style="text-decoration: line-through;">我们无处安放的青春(看完前12集)</span>(看完了< 媳妇的美好时代>)</li>
<li><span style="text-decoration: line-through;">The Big Bang Theory S03EP16(如果到时候有播&#8230;)</span></li>
<li><span style="text-decoration: line-through;">武汉环艺iMax版Avatar(如果买得到票&#8230;)</span>(买不到票&#8230;)</li>
</ol>
<h3>音乐</h3>
<ol>
<li><span style="text-decoration: line-through;">泛听完移动硬盘里的10G音乐</span></li>
<li><span style="text-decoration: line-through;">音乐库里已有的35G音乐压缩到30G</span></li>
<li><span style="text-decoration: line-through;">正在下载的12张专辑转为alac编码的m4a文件</span></li>
<li><span style="text-decoration: line-through;">整理音乐库, 每张专辑要有封面</span></li>
</ol>
<h3>游戏</h3>
<ol>
<li><span style="text-decoration: line-through;">Spore硬盘版下载到, 试着玩玩看</span></li>
</ol>
<h3>出游</h3>
<ol>
<li><span style="text-decoration: line-through;">去武大转转</span>(去了洪山广场, 阅马场, 武广, 江汉路)</li>
</ol>
<h3>Blog</h3>
<ol>
<li>Treasure Island读后感</li>
<li>Pro Django阅读笔记</li>
<li><span style="text-decoration: line-through;">随感</span></li>
</ol>
<p>写完后发现为什么这么像寒假作业呢&#8230;   汗&#8230;</p>
<p>诚挚希望到时候上面17条能完成12条左右&#8230;</p>
<p>截至到今天(0328), 终于完成了大多数春节任务了&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2010/02/10/lunar-new-year-checklist/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>基于Django/jQuery的多文件上传实现 — 第五天</title>
		<link>http://blog.xiaket.org/2010/01/17/django-jquery-file-upload-day5/</link>
		<comments>http://blog.xiaket.org/2010/01/17/django-jquery-file-upload-day5/#comments</comments>
		<pubDate>Sat, 16 Jan 2010 16:50:27 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Django开发]]></category>
		<category><![CDATA[Web开发]]></category>
		<category><![CDATA[Uploadify]]></category>
		<category><![CDATA[编码]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=271</guid>
		<description><![CDATA[隔了几天, 接着做这个项目.

首先是上传, 前几天js, 视图, 文件句柄这块的代码比较乱, 今天首先整理了下. 保证代码中只有最简单的逻辑, 包括文件上传前js提示上传了什么文件, 视图函数里不保存这个文件, 而是直接返回字符串.

今天碰到的第一个问题是上传后js里面的onComplete没有执行. 我首先猜测是视图函数处理有错误. 看Apache的log发现返回的状态值是200, 这证明swf已经post过来数据而且拿到正确的返回值了. 进一步的debug发现视图函数的逻辑已经全部正确地处理完了. 我猜测是由于我的视图函数太简单, 没有读取和处理上传的数据, 导致swf客户端有问题, 于是用python的logging模块记录了request的具体信息. 用Django文件处理的样例函数保存了上传的数据. 测试发现上传的数据能够成功地保存, 因此服务器端是没有问题的. 这个应该是比较容易理解的, 因为无论服务器端是否处理上传的数据, 这个数据在swf客户端的眼里已经上传到服务器了. 那么我们接下来应该做的事情就比较简单了: 处理客户端js脚本的函数调用, 看到底是什么地方出了问题.

<span class="readmore"><a href="http://blog.xiaket.org/2010/01/17/django-jquery-file-upload-day5/" title="基于Django/jQuery的多文件上传实现 — 第五天">阅读全文——共1455字</a></span>]]></description>
			<content:encoded><![CDATA[<p>隔了几天, 接着做这个项目.</p>
<p>首先是上传, 前几天js, 视图, 文件句柄这块的代码比较乱, 今天首先整理了下. 保证代码中只有最简单的逻辑, 包括文件上传前js提示上传了什么文件, 视图函数里不保存这个文件, 而是直接返回字符串.</p>
<p>今天碰到的第一个问题是上传后js里面的onComplete没有执行. 我首先猜测是视图函数处理有错误. 看Apache的log发现返回的状态值是200, 这证明swf已经post过来数据而且拿到正确的返回值了. 进一步的debug发现视图函数的逻辑已经全部正确地处理完了. 我猜测是由于我的视图函数太简单, 没有读取和处理上传的数据, 导致swf客户端有问题, 于是用python的logging模块记录了request的具体信息. 用Django文件处理的样例函数保存了上传的数据. 测试发现上传的数据能够成功地保存, 因此服务器端是没有问题的. 这个应该是比较容易理解的, 因为无论服务器端是否处理上传的数据, 这个数据在swf客户端的眼里已经上传到服务器了. 那么我们接下来应该做的事情就比较简单了: 处理客户端js脚本的函数调用, 看到底是什么地方出了问题.</p>
<p>要debug, 首先将这个函数的内部逻辑减到最少, 我写了一个简单的函数, 里面只有一个alert语句, 然后在uploadify初始化的调用中, 写下:</p>
<pre class="brush: jscript;">
        'onSelect'       : function (event, ID, fileObj){ onSelect(ID, fileObj);},
        //'onComplete'     : function (event, ID, fileObj, response, data){ onComplete(queueID, fileObj,          response, data);},
        'onComplete'     : function (event){ simplealert();},
</pre>
<p>点击, 执行上传, 发现alert语句能够正确执行.</p>
<p>我特意在视图函数里面加了一个sleep. 测试发现这个alert语句是在视图函数处理完(sleep完)之后才执行的, 因此, 目前我们的代码都还是按照我们的思路在运行的.</p>
<p>接下来的debug让我发现我在前面犯了一个很低级的错误, 我上次为了简单将有些函数调用里的queueID简写成了ID, 而今天在我写onComplete这个函数的时候又使用了旧的queueID这个名字. 真囧. 修改好以后onComplete就能在正常的视图函数返回后显示服务器端返回的内容了.</p>
<p>接下来测试是否能正常保存一个中文文件. 我前面是把post过来的内容直接存到一个固定的文件里面去. 而现在我尝试根据客户端传过来的文件创建一个新文件. 当上传一个英文名的文件时, 系统运行是正常的, 当上传一个中文名的文件时, 却有问题, 日志记录下的错误是UnicodeEncodeError, 又遇到编码问题了.</p>
<p>现在我的目的很简单, 就是上传的文件能够全部放到/home/apache/bookshop/media/temp/这个文件夹下. 几番尝试后, 发现适当地encode所涉及到的字符串就能解决中文文件上传的编码问题了:</p>
<pre class="brush: python;">
    name = file.name.encode(&quot;utf8&quot;)
    dir = u'/home/apache/bookshop/media/temp/'.encode(&quot;utf8&quot;)
    filename = dir + name
    dest = open(filename, 'wb+')
</pre>
<p>下次该考虑进度条相关的逻辑了~  今天已经晚了, 就这样吧~</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2010/01/17/django-jquery-file-upload-day5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django中的两个命令行输出添加ASCII颜色的函数</title>
		<link>http://blog.xiaket.org/2010/01/03/python-ascii-color-functions/</link>
		<comments>http://blog.xiaket.org/2010/01/03/python-ascii-color-functions/#comments</comments>
		<pubDate>Sat, 02 Jan 2010 16:09:50 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Python开发]]></category>
		<category><![CDATA[Django开发]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=234</guid>
		<description><![CDATA[代码在django/utils/termcolors.py中, 相关应用的例子在下面:



        colorize('hello', fg='red', bg='blue', opts=('blink',))

<span class="readmore"><a href="http://blog.xiaket.org/2010/01/03/python-ascii-color-functions/" title="Django中的两个命令行输出添加ASCII颜色的函数">阅读全文——共712字</a></span>]]></description>
			<content:encoded><![CDATA[<p>代码在django/utils/termcolors.py中, 相关应用的例子在下面:</p>
<pre class="brush: python;">
        colorize('hello', fg='red', bg='blue', opts=('blink',))
        print colorize('first line', fg='red', opts=('noreset',))
        print 'this should be red too'
        print colorize('and so should this')
        print 'this should not be red'
</pre>
<p>支持常见的ASCII效果:</p>
<pre>
        'bold'
        'underscore'
        'blink'
        'reverse'
        'conceal'
        'noreset'
</pre>
<p>这个文件里面的另一个make_style函数更赞~</p>
<pre class="brush: python;">
def make_style(opts=(), **kwargs):
    &quot;&quot;&quot;
    Returns a function with default parameters for colorize()

    Example:
        bold_red = make_style(opts=('bold',), fg='red')
        print bold_red('hello')
        KEYWORD = make_style(fg='yellow')
        COMMENT = make_style(fg='blue', opts=('bold',))
    &quot;&quot;&quot;
    return lambda text: colorize(text, opts, **kwargs)
</pre>
<p>这两个东东原理都很简单, 代码也是很易懂, 不再多解释了~ 以后项目中有需要直接输出彩色文字的需求直接调用这两个函数就行了~</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2010/01/03/python-ascii-color-functions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>一天idea散记</title>
		<link>http://blog.xiaket.org/2009/12/31/todays-ideas/</link>
		<comments>http://blog.xiaket.org/2009/12/31/todays-ideas/#comments</comments>
		<pubDate>Wed, 30 Dec 2009 17:04:56 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Django开发]]></category>
		<category><![CDATA[Python开发]]></category>
		<category><![CDATA[其它]]></category>
		<category><![CDATA[django-admin]]></category>
		<category><![CDATA[django-orm]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=226</guid>
		<description><![CDATA[Django相关

问: 一个models.py文件里面定义了一群模型(不知道多少个), 我想要得到一个字典, 键为模型名, 值为模型的类.

答: 这是个比较丑的办法, 暂时想不到更漂亮的了:

<span class="readmore"><a href="http://blog.xiaket.org/2009/12/31/todays-ideas/" title="一天idea散记">阅读全文——共1549字</a></span>]]></description>
			<content:encoded><![CDATA[<h3>Django相关</h3>
<p>问: 一个models.py文件里面定义了一群模型(不知道多少个), 我想要得到一个字典, 键为模型名, 值为模型的类.</p>
<p>答: 这是个比较丑的办法, 暂时想不到更漂亮的了:</p>
<pre class="brush: python;">
from django.db import models
import msgcenter.models
class_dict = {}
for name in dir(msgcenter.models):
    try:
        status = eval(&quot;issubclass(msgcenter.models.%s, models.Model)&quot; % name)
        class_dict[name] = eval(&quot;msgcenter.models.%s&quot; % name)
    except:
        pass
</pre>
<p>问: 一个模型里面有很多的field, 怎么拿到这个field的列表?</p>
<p>答: 这个是一个内置的属性:</p>
<pre class="brush: python;">
&gt;&gt;&gt; from msgcenter.models import Project
&gt;&gt;&gt; Project._meta.object_name
'Project'
&gt;&gt;&gt; Project._meta.get_all_field_names()
['admins',
 'fullname',
 'id',
 'popo_acount',
 'shortname']
</pre>
<p>不过这个似乎还包含了一些外键之类的东西, 有时间再细细研究下.</p>
<p>问: 我定义了一个模型, 里面有个field用来记录作者的ip, 普通视图函数还好说, 但是怎么样能够在admin界面下也能正确保存这个ip信息呢?</p>
<p>答: 几种办法. 最丑的: 改urls.py, 让我们匹配的规则在admin的规则的前面, 然后自己写视图函数, 外观做得很像很像admin界面&#8230;. 最剑走偏锋的: 用中间件, 对于到某个特殊的url的POST, 将POST的内容改掉, 添加用户的ip. 最正统的: 改admin.py里面的save_model方法&#8230;</p>
<p>第二种方法是今天和LD讨论出来的, 很有意思, 中间件这样用虽然很非主流, 但也不失为一个极端情况下的投机取巧之道. 用中间件你可以任意地修改request/response对象, 一切尽在掌握~</p>
<h3>工作相关</h3>
<p>遇到一个诡异的问题, 某个服务用户的crontab里运行的脚本ls输出和通过sudo来以这个服务用户的身份运行ls的输出结果不一样, 时间格式不同, 后来强制指定&#8211;time-style搞定&#8230;</p>
<p>今天开始写脚本处理<a href="http://mirrors.163.com/">开源镜像服务器</a>上Web服务器的日志. 今天主要解决的问题是一个很大的日志文件如何得到每个小时的访问次数. grep -c肯定是可以的, 但是效率很差. 目前想到的方法是利用日志里面的时间戳, 在python里用file对象的seek方法来处理, 而不是逐行处理, 不过这样效率到底能提高多少, 目前脚本没写出来我还没底, 目标是一个2G的文件一秒能搞定, 这样比逐行处理效率要高不少了. 直接将文件cat到/dev/null都会要几十秒~</p>
<h3>其他</h3>
<p>晚上在Windows平台下一个Debian虚拟机里面配mysqld, 怎么都起不来, 报错是无法绑定端口. 但是显然即使我起了两个mysqld也不会这么费事地排错&#8230;  后来改掉my.cnf里面的bind-address, 将127.0.0.1改为0.0.0.0就正常了&#8230;  都是VM惹的祸&#8230;   </p>
<p>新闻里广汉高铁上有人吸烟导致列车停运. 论坛上一群人骂铁道部胡乱找借口掩盖问题, 另有一群人辩护, 说, 有烟的话列车本来就应该停下来处理, 否则万一火灾就挂了. 这件事我倒有另一个看法, 目前广汉高铁的安检要求太低. 打火机是不能带上飞机的. 既然高铁的速度和飞机也差不了太多, 那么高铁上用打火机我想也应该禁止. 每秒一百米的速度, 如果着火, 绝对响起了通往天国的倒计时了&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2009/12/31/todays-ideas/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django中的中间件</title>
		<link>http://blog.xiaket.org/2009/12/27/django-middleware/</link>
		<comments>http://blog.xiaket.org/2009/12/27/django-middleware/#comments</comments>
		<pubDate>Sun, 27 Dec 2009 09:00:31 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Django开发]]></category>
		<category><![CDATA[Python开发]]></category>
		<category><![CDATA[WSGI]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=214</guid>
		<description><![CDATA[中间件的调用

如果你看了我写的前一篇讨论Django处理HTTP请求的文章, 我在文中基本没提到中间件. 好吧, 我故意遗漏了这些, 因为中间件相对比较独立. 而且每个中间件之间也相对比较独立. 顺口说句, 松耦合就这个好处&#8230;

言归正传, 中间件的调用是在WSGIHandler被调用的开头进行的:

<span class="readmore"><a href="http://blog.xiaket.org/2009/12/27/django-middleware/" title="Django中的中间件">阅读全文——共4376字</a></span>]]></description>
			<content:encoded><![CDATA[<h3>中间件的调用</h3>
<p>如果你看了我写的前一篇<a href="/2009/12/25/django-httpquest-procession/">讨论Django处理HTTP请求的文章</a>, 我在文中基本没提到中间件. 好吧, 我故意遗漏了这些, 因为中间件相对比较独立. 而且每个中间件之间也相对比较独立. 顺口说句, 松耦合就这个好处&#8230;</p>
<p>言归正传, 中间件的调用是在WSGIHandler被调用的开头进行的:</p>
<pre class="brush: python;">
# django/core/handlers/wsgi.py
class WSGIHandler(base.BaseHandler):
    initLock = Lock()
    request_class = WSGIRequest

    def __call__(self, environ, start_response):
        from django.conf import settings

        # Set up middleware if needed. We couldn't do this earlier, because
        # settings weren't available.
        if self._request_middleware is None:
            self.initLock.acquire()
            # Check that middleware is still uninitialised.
            if self._request_middleware is None:
                self.load_middleware()
            self.initLock.release()
</pre>
</p>
<p>显然, 关键的语句是倒数第二行里面的load_middleware方法, 我们继续跟进去看:</p>
<pre class="brush: python;">
# django/core/handlers/base.py
class BaseHandler(object):
    def load_middleware(self):
        &quot;&quot;&quot; some code omitted. &quot;&quot;&quot;
        self._view_middleware = []
        self._response_middleware = []
        self._exception_middleware = []

        request_middleware = []
        for middleware_path in settings.MIDDLEWARE_CLASSES:
            &quot;&quot;&quot; Exception handling is removed from these code. &quot;&quot;&quot;
            dot = middleware_path.rindex('.')
            mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:]
            mod = import_module(mw_module)
            mw_class = getattr(mod, mw_classname)
            mw_instance = mw_class()

            if hasattr(mw_instance, 'process_request'):
                request_middleware.append(mw_instance.process_request)
            if hasattr(mw_instance, 'process_view'):
                self._view_middleware.append(mw_instance.process_view)
            if hasattr(mw_instance, 'process_response'):
                self._response_middleware.insert(0, mw_instance.process_response)
            if hasattr(mw_instance, 'process_exception'):
                self._exception_middleware.insert(0, mw_instance.process_exception)

        # We only assign to this when initialization is complete as it is used
        # as a flag for initialization being complete.
        self._request_middleware = request_middleware
</pre>
<p>整个这段代码的核心意思是对于每个settings.MIDDLEWARE_CLASSES里指定的中间件, 我们探测里面有什么方法. 如果有process_request, process_view, process_response, process_exception这四个方法, 我们将其添加到BaseHandler的四个列表里去. 按照Django处理HTTP请求的顺序, 前两个列表都是在django.core.handlers.base.BaseHandler模块的get_response方法中应用的, 第三个process_response方法是在WSGIHandler的结尾处应用的, 最后一个方法是在视图函数处理HTTP请求出现异常时才被应用的.</p>
<p>用django-admin.py startproject创建的新项目里, 默认添加了下面这些中间件:</p>
<pre class="brush: python;">
MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
)
</pre>
<p>csrf这篇文章就不管了, 我们接下来分析剩余三个中间件到底执行了哪些操作. 我们主要分析process_request和process_response这两个字典.</p>
<h3>process_request</h3>
<p>CommonMiddleware里面没啥新鲜的东西, 主要做了三件事情:</p>
<ul>
<li>禁止某些User-Agent的访问(我瞬间很邪恶地想起可以禁止IE的访问&#8230;).</li>
<li>URL重写, 在URL后面添加/和在前面添加www, 这个是可以配置的.</li>
<li>ETag处理.</li>
</ul>
<p>这个不细分析了, 有兴趣的童鞋可以自行围观代码(django/middleware/common.py), 我们接下来看SessionMiddleware. 这个中间件的process_request方法里面只有几行代码:</p>
<pre class="brush: python;">
class SessionMiddleware(object):
    def process_request(self, request):
        engine = import_module(settings.SESSION_ENGINE)
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
        request.session = engine.SessionStore(session_key)
</pre>
<p>这一段详细的理解可以参考<a href="http://docs.djangoproject.com/en/dev/topics/http/sessions/#topics-http-sessions">Django文档中有关会话的部分</a>. 这部分在我手头的项目中应用不多, 于是很不厚道地也不准备细看了.</p>
<p>AuthenticationMiddleware中的代码依然简洁:</p>
<pre class="brush: python;">
# django/contrib.auth.middleware.py
class AuthenticationMiddleware(object):
    def process_request(self, request):
        assert hasattr(request, 'session'), &quot;The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'.&quot;
        request.__class__.user = LazyUser()
        return None
</pre>
<p>除掉判断会话中间件是否已被载入的那段, 关键的语句是第5行, 这句设置了request这个WSGIRequest实例的类属性user, 将其设置为LazyUser, 而这个东西的定义为:</p>
<pre class="brush: python;">
class LazyUser(object):
    def __get__(self, request, obj_type=None):
        if not hasattr(request, '_cached_user'):
            from django.contrib.auth import get_user
            request._cached_user = get_user(request)
        return request._cached_user
</pre>
<p>这段代码使用了描述器(descriptor), 和上面那段代码的作用和起来所实现的功能是, 如果用request.user来请求user这个对象, 实际上请求的是_cached_user这个对象. 如果这个对象为空, 那么用get_user方法来获得当前请求的user:</p>
<pre class="brush: python;">
# django/contrib/auth/__init__.py
def get_user(request):
    from django.contrib.auth.models import AnonymousUser
    try:
        user_id = request.session[SESSION_KEY]
        backend_path = request.session[BACKEND_SESSION_KEY]
        backend = load_backend(backend_path)
        user = backend.get_user(user_id) or AnonymousUser()
    except KeyError:
        user = AnonymousUser()
    return user
</pre>
<p>这个函数的逻辑是, 从会话信息里面查找到当前用户信息, 查不到的话就将当前用户设置为匿名用户(未注册用户). 至此, 这个纠结的AuthenticationMiddleware处理request的部分就告一段落了.</p>
<h3>process_response</h3>
<p>CommonMiddleware中的这个方法是在处理ETag; SessionMiddleware的这个方法只是用来处理会话信息的保存, 谢天谢地. 而AuthenticationMiddleware更是很厚道地没有定义这个方法. 好吧, 那就这样结束吧~</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2009/12/27/django-middleware/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django处理HTTP请求的过程</title>
		<link>http://blog.xiaket.org/2009/12/25/django-httpquest-procession/</link>
		<comments>http://blog.xiaket.org/2009/12/25/django-httpquest-procession/#comments</comments>
		<pubDate>Fri, 25 Dec 2009 08:07:28 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Django开发]]></category>
		<category><![CDATA[Python开发]]></category>
		<category><![CDATA[REQUEST]]></category>
		<category><![CDATA[WSGI]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=204</guid>
		<description><![CDATA[WSGIHandler

HTTP请求的处理是由wsgi或mod_python脚本处理的. 在我的机器上使用的WSGI, 这个脚本里面配置好Django运行环境后, 初始化了一个WSGI句柄的实例:



<span class="readmore"><a href="http://blog.xiaket.org/2009/12/25/django-httpquest-procession/" title="Django处理HTTP请求的过程">阅读全文——共4456字</a></span>]]></description>
			<content:encoded><![CDATA[<h3>WSGIHandler</h3>
<p>HTTP请求的处理是由wsgi或mod_python脚本处理的. 在我的机器上使用的WSGI, 这个脚本里面配置好Django运行环境后, 初始化了一个WSGI句柄的实例:</p>
<pre class="brush: python;">
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
</pre>
<p>这个句柄将处理HTTP请求, 我们跟进去看这个WSGI句柄的初始化. WSGIHandler这个类没有__init__方法, 因此使用的是base.BaseHandler的初始化方法:</p>
<pre class="brush: python;">
# django/core/handlers/base.py
class BaseHandler(object):
    # Changes that are always applied to a response (in this order).
    response_fixes = [
        http.fix_location_header,
        http.conditional_content_removal,
        http.fix_IE_for_attach,
        http.fix_IE_for_vary,
    ]

    def __init__(self):
        self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
</pre>
<p>好吧, 这个里面貌似也没干啥&#8230; 我们继续看WSGIHandler的__call__方法. 这个方法会被调用上面的wsgi脚本中的语句调用.</p>
<p>根据WSGI的文档:</p>
<pre>
The Web framework/app is represented to the server as a Python callable. It can be
a class, an object, or a function. The arguments to __init__, __call__, or the function
must be as above: an environ object and a start_response callable.
</pre>
<p>参考看下WSGIHandler的__call__方法, 很符合这个要求, 也是两个外部参数.</p>
<pre class="brush: python;">
# django/core/handlers/wsgi.py
class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest
    def __call__(self, environ, start_response):
        &quot;&quot;&quot; some code omitted here. &quot;&quot;&quot;
        signals.request_started.send(sender=self.__class__)
        try:
            try:
                request = self.request_class(environ)
            except UnicodeDecodeError:
                response = http.HttpResponseBadRequest()
            else:
                response = self.get_response(request)

            &quot;&quot;&quot; some more code omitted. &quot;&quot;&quot;
        finally:
            signals.request_finished.send(sender=self.__class__)

        try:
            status_text = STATUS_CODE_TEXT[response.status_code]
        except KeyError:
            status_text = 'UNKNOWN STATUS CODE'
        status = '%s %s' % (response.status_code, status_text)
        response_headers = [(str(k), str(v)) for k, v in response.items()]
        for c in response.cookies.values():
            response_headers.append(('Set-Cookie', str(c.output(header=''))))
        start_response(status, response_headers)
        return response
</pre>
<p>这个方法一开始还做了些和中间件相关的事情, 这儿就省略了. 我们从信号这一段开始, 在处理完中间件相关的逻辑后, 此处放出一个request_started的信号, 我们可以利用这个信号来处理一些对每个HTTP请求都要处理的逻辑. 接下来我们开始处理请求. 这儿用了一个try-except-else语句块. 首先我们尝试将environ作为参数来生成一个WSGIRequest类, 如果这一步出了问题, 我们返回400, 否则我们用get_response(request)来获得一个HttpRequest对象. 接下来Django放出了一个request_finished信号, 然后返回这个HTTP请求. 我们继续看处理请求的这部分.</p>
<h3>WSGIRequest</h3>
<p>前面的代码中, 我们已经在WSGIHandler中将其request_class属性设为一个WSGIRequest的实例, 并用一个装有环境变量的字典environ初始化了一个WSGIRequest. 我们接下来看Django是怎么处理这个WSGIRequest的.</p>
<p>首先看这个类的初始化. 相关代码如下:</p>
<pre class="brush: python;">
class WSGIRequest(http.HttpRequest):
    def __init__(self, environ):
        script_name = base.get_script_name(environ)
        path_info = force_unicode(environ.get('PATH_INFO', u'/'))
        if not path_info or path_info == script_name:
            &quot;&quot;&quot; some comments omitted here. &quot;&quot;&quot;
            path_info = u'/'
        self.environ = environ
        self.path_info = path_info
        self.path = '%s%s' % (script_name, path_info)
        self.META = environ
        self.META['PATH_INFO'] = path_info
        self.META['SCRIPT_NAME'] = script_name
        self.method = environ['REQUEST_METHOD'].upper()
        self._post_parse_error = False
</pre>
<p>这段初始化基本上也是比较平淡的, 主要做了两件事, 一个是设置了request.META这个字典, 另外设置了request.method. 如果这一段成功了, 那么我们将用get_response来处理这个WSGIRequest.</p>
<h3>get_response</h3>
<p>这个函数的定义在django/core/handlers/base.py中, 是BaseHandler的一个方法, 代码如下:</p>
<pre class="brush: python;">
class BaseHandler(object):
    # Changes that are always applied to a response (in this order).
    def get_response(self, request):
        &quot;Returns an HttpResponse object for the given HttpRequest&quot;
        &quot;&quot;&quot; Some code omitted here. &quot;&quot;&quot;
        # Get urlconf from request object, if available.  Otherwise use default.
        urlconf = getattr(request, &quot;urlconf&quot;, settings.ROOT_URLCONF)
        resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
        try:
            callback, callback_args, callback_kwargs = resolver.resolve(
                    request.path_info)
            &quot;&quot;&quot; Some code omitted here. &quot;&quot;&quot;
            # Actually, this line was in a try-except thing...   by xiaket.
            response = callback(request, *callback_args, **callback_kwargs)
            &quot;&quot;&quot; Some code omitted here. &quot;&quot;&quot;
            return response
        except http.Http404, e:
            if settings.DEBUG:
                from django.views import debug
                return debug.technical_404_response(request, e)
            else:
                try:
                    callback, param_dict = resolver.resolve404()
                    return callback(request, **param_dict)
                except:
                    try:
                        return self.handle_uncaught_exception(request, resolver, sys.exc_info())
                    finally:
                        receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
        &quot;&quot;&quot; Some code omitted here. &quot;&quot;&quot;
</pre>
<p>原来的代码很长, 仔细分析, 除掉了一些我不太关心的异常处理后就变成了现在这个样子. 这个函数接受一个HttpRequest实例, 返回一个HttpRequest实例. 函数一开始就来匹配URL. 第7行这句很强大, 因为它提供了很多可能性. 例如, 你可以通过中间件来对于request中的IP做判断, 然后通过设置urlconf来决定使用什么样的URL匹配方式. 当然, 这个时候已经经过了中间件, request.user已经设置好了, 你也可以根据用户的不同来提供不同的urlconf. 从settings.ROOT_URLCONF中拿到了URL配置后, 我们尝试匹配URL, 然后在第11行拿第10行匹配得到的适当的view函数来处理这个URL. 到视图函数以后就没有太多好说了. 中间那层我们略过, 返回了response之后也没啥了, 到这儿来就交给WSGI了. Django处理HTTP请求的逻辑也就结束了.</p>
<p>挖个坑, 实际上Django有很多HTTP请求的细节是在中间件里面完成的, 这篇文章完全没有提及这一点, 我有时间再仔细分析下中间件的作用, 为强大的中间件们正名~</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2009/12/25/django-httpquest-procession/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>讨论一个纠结的Django编码问题</title>
		<link>http://blog.xiaket.org/2009/12/22/django-encoding-problem/</link>
		<comments>http://blog.xiaket.org/2009/12/22/django-encoding-problem/#comments</comments>
		<pubDate>Tue, 22 Dec 2009 13:54:08 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Django开发]]></category>
		<category><![CDATA[GBK]]></category>
		<category><![CDATA[GET]]></category>
		<category><![CDATA[REQUEST]]></category>
		<category><![CDATA[WSGI]]></category>
		<category><![CDATA[编码]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=199</guid>
		<description><![CDATA[简介

本文简单记录了我在项目中遇到的一个Django编码问题. 通过Django源码分析, 我们找到了问题的根源, 并提出了解决问题的方法.

问题总结: 用WSGI方式使用Django, 请求的URL是GBK编码, 而且在视图函数中使用了request.REQUEST字典的话, 可能会有编码问题.

<span class="readmore"><a href="http://blog.xiaket.org/2009/12/22/django-encoding-problem/" title="讨论一个纠结的Django编码问题">阅读全文——共4454字</a></span>]]></description>
			<content:encoded><![CDATA[<h3>简介</h3>
<p>本文简单记录了我在项目中遇到的一个Django编码问题. 通过Django源码分析, 我们找到了问题的根源, 并提出了解决问题的方法.</p>
<p>问题总结: 用WSGI方式使用Django, 请求的URL是GBK编码, 而且在视图函数中使用了request.REQUEST字典的话, 可能会有编码问题.</p>
<h3>问题背景</h3>
<p>问题的背景是这样的, 在我接手的报警项目中, 报警服务器提交到服务器的报警信息内容是GBK编码的, 例如下面的URL是一个典型的报警请求:</p>
<pre>

http://192.168.24.133/msggw.php?from_ip=1.2.3.4&#038;project_name=blah&#038;msg=blah+427%28%D0%A1%C3%B7%C9%B3%29+1.2.3.4+has+new+debug+log%2Ccheck%21%21%21%21%0ATue+Dec+22+08%3A01%3A18+2009%2822172096%29%28539088508%29%09shutdownOS%280%29
</pre>
<p>为了处理GBK编码, 我们要在视图函数里面设置request的编码, 例如:</p>
<pre class="brush: python;">
def receiving_message(request):
    request.encoding = &quot;gbk&quot;
</pre>
<p>这样, 在这个语句之后, 我们理论上应该不会再遇到编码问题. 但是不幸的是, 我还是发现有编码问题, 问题似乎来自我在视图函数的后半部分使用了request.REQUEST这个字典.</p>
<h3>代码背后的故事</h3>
<p>今天, 我在确定这个问题来自Django处理request后, 仔细读了下相关的源码. 在Django产生一个request对象到在视图函数里面使用request.GET/request.POST的具体细节包括:</p>
<h4>由WSGI脚本生成了一个request对象.</h4>
<p>相关代码在django/core/handlers/wsgi.py文件中, 生成的request对象是WSGIRequest的一个实例. 这个实例的初始化基本只是设置了request.META这个字典的内容. 接下来这个对象就被传给视图函数了.(不清楚中间具体还发生了什么事情, 哪天有机会再去仔细研究下&#8230;)</p>
<h4>在视图函数里面使用GET/POST字典</h4>
<p>这个过程看起来很简单, 实际上却比较复杂. 首先, 在WSGIRequest这个对象定义的结尾处, Django设置了使用GET/POST/REQUEST这三个字典的方法. 我们使用GET/POST字典实际上是通过执行这些内置的方法来实现的. 例如, 我们需要知道request.GET['msg']的内容, 此时, Django是通过_get_get方法来拿到GET字典的内容的:</p>
<pre class="brush: python;">
    def _get_get(self):
        if not hasattr(self, '_get'):
            # The WSGI spec says 'QUERY_STRING' may be absent.
            self._get = http.QueryDict(self.environ.get('QUERY_STRING', ''), encoding=self._encoding)
        return self._get
</pre>
<p>Django文档中说, Django在处理GET/POST时是很懒惰的, 如果没有明确要GET/POST中的内容, Django是不会浪费时间生成这两个字典的. 上面这个函数里面的if就是一个明确的证据. 如果没有_get属性, 则实例化一个QueryDict(代码在django/http/__init__.py中), 并将其作为_get的内容, 也即是GET的内容. 在实例化的过程中给出了两个参数, 一个是QUERY_STRING, 一个是_encoding. 如果你还没有在视图函数里面设置编码, 那么此时_encoding是默认的值(utf-8). 在QueryDict的__init__方法里面, 我们看到这样的语句:</p>
<pre class="brush: python;">
        self.encoding = encoding
        for key, value in parse_qsl((query_string or ''), True): # keep_blank_values=True
            self.appendlist(force_unicode(key, encoding, errors='replace'),
                            force_unicode(value, encoding, errors='replace'))
</pre>
<p>这一段代码负责生成GET字典的内容. 其中那个parse_qsl函数来自python默认的cgi模块, 作用是将QUERY_STRING分解为一个字典. 对于字典里面的每个值, Django使用force_unicode方法(代码在django/utils/encoding.py文件中)将其编码解码. 顺便说一句, 这个force_unicode函数会做一些诡异的事情, 例如将一个GBK字符串转码不成功且不论, 它会生成类似u&#8217;\u68a6\u5e7b:\u0421\xf7\u0273&#8242;的字符串, 很乱很杂交.</p>
<p>上面说了这么多, 总结成一句话: Django处理视图函数时, 直到你明确要求GET字典中的键值时, 这个字典才会被生成, 生成字典时按照编码的配置做了编码转码等工作.</p>
<h4>设置编码</h4>
<p>我们在文章的一开头就给出了设置Django处理QUERY_STRING的编码的方法, 即直接指定request的encoding. 这句话看起来简单, 实际上却在背后做了一些额外的工作, 这是因为在Django对HttpRequest对象(代码在django/http/__init__.py中)的定义中有这么一句:</p>
<pre class="brush: python;">
encoding = property(_get_encoding, _set_encoding)
</pre>
<p>即, 要取得编码的值, 实际上是调用了_get_encoding方法, 要设置编码的值, 实际上调用了_set_encoding方法. 我们关注的是后一种方法, 它的定义为:</p>
<pre class="brush: python;">
    def _set_encoding(self, val):
        &quot;&quot;&quot;
        Sets the encoding used for GET/POST accesses. If the GET or POST
        dictionary has already been created, it is removed and recreated on the
        next access (so that it is decoded correctly).
        &quot;&quot;&quot;
        self._encoding = val
        if hasattr(self, '_get'):
            del self._get
        if hasattr(self, '_post'):
            del self._post
</pre>
<p>很聪明很合乎逻辑的做法, 如果用户设置了编码, 那么我们把_get和_post都删除掉, 这样也就对应地删除了GET/POST这两个字典. 下次用户再想要拿这两个字典里面的内容时, 再用新的编码再生成一次GET/POST就是了.</p>
<h4>request.REQUEST字典</h4>
<p>这个字典是模仿php中的对应快捷方式而建立的, 实际上只是将GET和POST合并起来, 代码在django/core/handlers/wsgi.py的WSGIRequest定义中:</p>
<pre class="brush: python;">
    def _get_request(self):
        if not hasattr(self, '_request'):
            self._request = datastructures.MergeDict(self.POST, self.GET)
        return self._request
</pre>
<h3>哪段代码有问题?</h3>
<p>上面洋洋洒洒一大篇描述了Django处理GET/POST字典的原理, 我接下来要说的是这种处理过程中有哪些问题. 我们看看下面这段视图函数中的代码:</p>
<pre class="brush: python;">
def receiving_message(request):
    msg1 = request.REQUEST['msg']
    request.encoding = &quot;gbk&quot;
    msg2 = request.REQUEST['msg']
</pre>
<p>首先, 第一次拿到的msg1显然是乱码, 我们还没设置处理URL的编码, 因此URL没有被正确地解码. 但是不幸地是, 第二次拿到的msg2仍然是乱码. 这是因为设置编码的过程中仅仅删除了可能存在的GET和POST字典, 而没有删除REQUEST字典. 因此第二次我们从REQUEST字典里取东西时, 所拿到的结果仍然是按照不正确的编码解出来的结果.</p>
<p>这个问题出在哪儿? 虽然看起来应该认为上面的_set_encoding方法是有问题的, 应该额外加两行, 删除self._request才对. 不过问题不是那么简单的, HttpRequest对象里面根本没有request.REQUEST这个东东. 这个东东是在WSGIRequest中定义的. 我们应该在WSGIRequest的定义中override掉HttpRequest的默认_set_encoding方法才对.</p>
<p>再顺口提一句, modpython里面也没有override这个方法, 因此理论上也存在同样的问题.</p>
<h3>问题回顾</h3>
<p>上面说了很多, 但是为什么只有在很偶然的情况下才会出现编码问题呢? 在大多数情况下, request.REQUEST字典都没有生成. 因此在视图函数中, 设置编码后从request.REQUEST字典中拿东西出来是没有问题的. 但是在一定几率下(我只能这么说了&#8230;), 后面的视图函数代码中从request.REQUEST字典里拿到的数据可能是有编码问题的.</p>
<p>我在前面曾经推测这个问题的出现可能和request.REQUEST这个字典相关, 不过我在测试中发现, 即使这个字典没有出现, 也可能会出现编码问题. 哎哎, 无论如何, 这个是一个诡异的问题&#8230;</p>
<p>关于这个一定几率, 根据我的经验, 不是指某一段时间可以, 某一段时间不行; 而是某一次重启后可以, 某一次重启后不行&#8230; 也许这个问题在更深层次上和会话相关, 不过暂时还是不要这么钻牛角尖的好, 因为我们有一个比较放心的解决方案．</p>
<h3>解决方案</h3>
<p>比较放心也比较累赘的解决方案是在设置编码后, 将GET/POST/REQUEST都删掉:</p>
<pre class="brush: python;">
def receiving_message(request):
    request.encoding = &quot;gbk&quot;
    if hasattr(request, '_request'):
        del request._request
    if hasattr(request, '_get'):
        del request._get
    if hasattr(request, '_post'):
        del request._post
</pre>
<p>这样我想, 应该是不会再有问题了.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2009/12/22/django-encoding-problem/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>最近个人项目的设想</title>
		<link>http://blog.xiaket.org/2009/12/13/recent-personal-project-plan/</link>
		<comments>http://blog.xiaket.org/2009/12/13/recent-personal-project-plan/#comments</comments>
		<pubDate>Sat, 12 Dec 2009 16:39:57 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Django开发]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=171</guid>
		<description><![CDATA[自己手头积攒了很多电子书, 有些书虽然很少读, 但留下收藏毕竟还是不错的. 这些书虽然下载很方便, 但是整理却很是麻烦. 最大的问题是经常一本书会涉及到多个话题, 电子书分类时不知如何是好, 找起来也是要靠估摸. 于是想自己写一个有Web界面的个人图书管理系统. 好吧, 这个Web系统只有一个人用肯定是浪费了, 靠谱一点的想法是干脆做成一个图书交流系统. 这两天的空闲时间, 除了在写Django的中文localflavor, 就是在写这个. 有点点想法也就顺便记下来, 放在这儿. 用什么写? 嘿嘿, 自然是Django啦~

<span class="readmore"><a href="http://blog.xiaket.org/2009/12/13/recent-personal-project-plan/" title="最近个人项目的设想">阅读全文——共1113字</a></span>]]></description>
			<content:encoded><![CDATA[<p>自己手头积攒了很多电子书, 有些书虽然很少读, 但留下收藏毕竟还是不错的. 这些书虽然下载很方便, 但是整理却很是麻烦. 最大的问题是经常一本书会涉及到多个话题, 电子书分类时不知如何是好, 找起来也是要靠估摸. 于是想自己写一个有Web界面的个人图书管理系统. 好吧, 这个Web系统只有一个人用肯定是浪费了, 靠谱一点的想法是干脆做成一个图书交流系统. 这两天的空闲时间, 除了在写<a href="http://code.google.com/p/django-localflavor-zhcn/">Django的中文localflavor</a>, 就是在写这个. 有点点想法也就顺便记下来, 放在这儿. 用什么写? 嘿嘿, 自然是Django啦~</p>
<h3>用户注册和权限分配</h3>
<p>用户注册这个步骤对于文件共享网站来说是必要的, 很正统的做法是生成一个随机字符串作为密钥, 往用户的邮箱发一封包含密钥的注册邮件. 用户通过点击链接而使用这个密钥注册. 这种做法对于小范围的文件共享网站却不是很合适. 填一个注册表单未免太生分太正式了, 毕竟大家都是同事, 低头不见抬头见, 弄这些繁文缛节倒彷佛是在捉弄人了. 另一个办法就是不限制用户对网站的访问. 如果仅仅开发下载权限也就算了, 最多多转转硬盘罢了. 但是无限制开发上传权限就不太好了&#8230; 于是我的想法是利用IP来判断用户. 利用Django放出的request_started信号来确定用户身份, 完成登录的工作. 基本逻辑是从信号里面拿到request对象. 这个对象里面的用户如果已登录, 则继续, 如果未登录, 则从request里面拿到用户的IP, 然后根据IP地址判断是否已知用户, 如果是已知用户, 则用login函数让其登录. 否则认为这个用户是受限用户, 不对其显示某些页面. 受限用户如何变成已知用户? 让他在泡泡上发个消息给我, 告诉我他的IP地址就行了~</p>
<h3>智能化地识别图书信息</h3>
<p>我们所上传的图书文件名里面一般包含了图书相关的信息. 例如书名, 出版社等等. 如果是pdf文件和chm文件, 我们还可以通过相应的python库来拿到电子书的作者/出版社等等信息. 为此, 我希望我能写出一个完美的, 省心地, 傻瓜地书籍上传页面, 你先上传一个文件, 这个文件名里面也许包含了一些信息, 我可以尝试使用re把这些信息抓出来. 然后, 我还可以从文件里面或者网络搜索而拿到书籍信息, 通过ajax显示在页面上. 这样书籍的信息也会更全面, 用户也更省心.</p>
<h3>批量上传</h3>
<p>写一个接口, 支持批量上传, 否则一本一本上传会很累的&#8230;</p>
<h3>用jQuery-UI写界面</h3>
<p>这个项目里面可以肆无忌惮地用比较大的js库了, 于是基本不用自己操心一堆乱七八糟的效果了, jQuery-UI这个库还是不错的, 写了几行代码就生成了一个很pp的界面~ 赞!</p>
<p>原本打算用ExtJS, 不过这个库据说版权限制很有问题, 还是用更流行而我也更熟悉的jQuery的UI效果库吧~</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2009/12/13/recent-personal-project-plan/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>一个很有用的Django装饰器: render_to</title>
		<link>http://blog.xiaket.org/2009/12/04/django-decorator-render_to/</link>
		<comments>http://blog.xiaket.org/2009/12/04/django-decorator-render_to/#comments</comments>
		<pubDate>Fri, 04 Dec 2009 09:52:14 +0000</pubDate>
		<dc:creator>xiaket</dc:creator>
				<category><![CDATA[Django开发]]></category>
		<category><![CDATA[decorator]]></category>
		<category><![CDATA[Python开发]]></category>

		<guid isPermaLink="false">http://blog.xiaket.org/?p=156</guid>
		<description><![CDATA[今天在pypi里面乱逛时看到一个装饰器, 觉得比较赞:



def render_to(template=None):

<span class="readmore"><a href="http://blog.xiaket.org/2009/12/04/django-decorator-render_to/" title="一个很有用的Django装饰器: render_to">阅读全文——共1487字</a></span>]]></description>
			<content:encoded><![CDATA[<p>今天在pypi里面乱逛时看到一个装饰器, 觉得比较赞:</p>
<pre class="brush: python;">
def render_to(template=None):
    &quot;&quot;&quot;
    Decorator for Django views that sends returned dict to render_to_response function.

    Template name can be decorator parameter or TEMPLATE item in returned dictionary.
    RequestContext always added as context instance.
    If view doesn't return dict then decorator simply returns output.

    Parameters:
     - template: template name to use

    Examples:
    # 1. Template name in decorator parameters

    @render_to('template.html')
    def foo(request):
        bar = Bar.object.all()
        return {'bar': bar}

    # equals to
    def foo(request):
        bar = Bar.object.all()
        return render_to_response('template.html',
                                  {'bar': bar},
                                  context_instance=RequestContext(request))
    # 2. Template name as TEMPLATE item value in return dictionary

    @render_to()
    def foo(request, category):
        template_name = '%s.html' % category
        return {'bar': bar, 'TEMPLATE': template_name}

    #equals to
    def foo(request, category):
        template_name = '%s.html' % category
        return render_to_response(template_name,
                                  {'bar': bar},
                                  context_instance=RequestContext(request))

    &quot;&quot;&quot;
    def renderer(function):
        def wrapper(request, *args, **kwargs):
            output = function(request, *args, **kwargs)
            if not isinstance(output, dict):
                return output
            tmpl = output.pop('TEMPLATE', template)
            return render_to_response(tmpl, output, context_instance=RequestContext(request))
        return wrapper
    return renderer
</pre>
<p>这个装饰器出自<a href="http://pypi.python.org/pypi/django-annoying/0.7.4">django-annoying</a>. 在annoying目录下的decorators.py文件中.</p>
<p>优点: 写视图函数的时候, 总是写一个很长的render_to_response函数, 这个里面的东西都是很固定的. 于是每次都是y来p去, 很不爽. 如果用这个装饰器就可以很一目了然地在视图函数中用docstring里面的格式来写视图函数了. 这个时候, 视图函数返回一个字典就可以了.</p>
<p>缺点: 对于熟悉Django的用户, 这个可读性稍差了一点点. 不过我想习惯了这个装饰器的人都会更爱这个吧, 毕竟它更pythonic, 更DRY.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.xiaket.org/2009/12/04/django-decorator-render_to/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
