2008年05月15日

这是一篇massimo张贴在raddit上的文章链接,引来许多的评论。不过许多评论是执不赞同意见。有些是对无导入有质疑,有些是对与django比较所使用的例子有质疑,有些则是认为web2py言过其实,有些还认为massimo不愿接受批评。我想不说存在不存在问题(至少我个人认为不导入对于单APP可能是方便的,但是正是由于这一点造成APP间不能互相调用,存在硬伤),但文章的题目就容易引人争论。

每个人都有自已的习惯,习惯接近的则形成部落,但仍然会有自已的想法。所以人群就是分分合合,框架也是一样,有些人喜欢这种,有些人喜欢那种,有些人是喜欢其中一部分。因此当进行比较时,自然要对自已喜欢的进行维护。

这种争论在语言的比较中最为常见。

不过习惯不同,很难说哪个是绝对的好。也许你认为不好,但是别人就能用它做出Cool的东西来,你说到底好不好。关键还是看使用者的水平。只不过不同的框架提供的思路和方便程序不同,可能找一个更符合个人性格的框架会让你学起来和用起来更舒服罢了。

2008年05月08日

前阵子我已经在GAE上申请成功的一个帐号,因此创建了一个 http://ulipad.appspot.com,不过它是我直接使用GAE的SDK开发的,其中改造了web2py的模板。现在Massimo已经声明web2py的最新版本已经可以支持GAE了,并且给出了一个视频。从视频上看,首先是在本地进行开发,然后部署到GAE上去。下面是我的一个实践的记录。

1. 先从svn中同步成最新的版本。如果你仔细观察,你会看到有app.yaml和cgihandler.py,它们将用来处理wsgi的请求。如果你打开app.yaml,可以看到:

application: web2py
version: 1
api_version: 1
runtime: Python

handlers:
- url: .*
  script: cgihandler.py

很明显,所以有处理都交给cgihandler.py了。因为web2py可以处理静态文件,因此也不需要对静态文件单独配置。

2. 在本地的GAE环境下创建一个开发目录,我起名为web2try。这个开发目录要看你的安装路径的,我是在windows下进行的,路径为C:\Program Files\Google\google_appengine。

3. 将web2py目录下的所有东西导出。我是使用svn的export命令。这样,只保留有用的源码,象.svn之类的东西就没有了。然后再将导出的内容拷贝到web2try目录下。这样看上去基本上就是:

applications/
    __init__.py
gluon/
app.yaml
cgihandler.py
web2py.py

是不是与你的有些不同,没关系,是因为我把认为无用的东西删除了。使用全部的内容,你是可以在本地GAE环境下进行开发的,但是上传时会发现文件非常多。因此我的做法是在另外的目录下进行开发,GAE中只保留上传的内容。在applications下,现在是空的,在我开发好app之后会拷贝过来。web2py在这一点还是好的,每个app是独立的。admin我没有保留,因为在GAE上不能进行写文件操作,所以你无法直接在GAE中使用admin进行开发,所以不要了。examples也不要了,太大了。经过精简后,文件数控制在80左右。

4. 本地开发。这块就不多做介绍了。只强调几点:

a)注意controller中:

response.session_id=None # comment if you want to store sessions

这句话很重要。因为GAE不能写文件,所以这样的话就可以暂时不使用session了。但如果你要写,可能目前还存在问题。

b)如果你需要使用数据库,可以:

from gluon.contrib import gql
db=gql.SQLDB()

其它的基本上就没有什么区别的。不过我还没有试过,所以也不清楚是否存在问题。而且GAE的数据库不是真正的关系数据库,所以有些功能没有,比如查询时没有不等于,like等。而且它还有一些其它的扩展,如Expando模型,可以动态设置属性,还有一些非关系数据的字段类型,如ListProperty等。所以数据库这块要仔细一些。

5. 在本地开发完毕后,将app拷贝到applications目录下。

6. 上传。上传之前请注意检查你的app.yaml中的application名字与你在GAE中创建的app的名字要相同。

不过因为我是在windows下开发,所以遇到了一个问题是关于回车符的。这个问题在邮件列表中得到了解决。

上面就是我的记录,测试的功能还很不完整,可能需要随着使用再慢慢发现。

http://web2try.appspot.com/Form/default/index 是我使用web2py搭建的一个例子,只是一个表格模块的测试,可以看一下。

2008年03月23日

地址在这里 http://fy.py3k.cn/p/web2py/cookbook/cookbook.html

平台是由VCC搭建,本人翻译完成。

对web2py感兴趣的可以先从这里入门了。

2008年03月12日

虽然web2py可以基本上在浏览器完成几乎所有的工作,但是就某些简单的代码测试来说还不是太方便,因此考虑到django中可以通过manage.py shell进入一个命令行,它可以预设一些工作,比如环境变量的设置,这样再进行测试会非常方便。于是研究了一下web2py的调用处理,模仿django写了这么一个工作,目前已经被web2py接受放在了svn中,不过我测试还不多,所以不知道有没有问题。

从svn中同步最新的源码,然后在web2py的文件夹下运行:

Python shell.py appname

因为web2py是以app为运行单位,不过这块我没有怎么测试,简单的示例目前没有问题,与app相关的测试还没有怎么试验。

这样你可以用它来测试controller代码。因为web2py的controller不是完整的python代码,它是在一个环境中运行的,所以直接在命令行下测试是不行的。现在有了这个工具,可以做一些简单的测试了。

2008/03/13

修改shell.py,增加对ipython的支持。缺省会自动查找是否安装了ipython。如果想强制使用缺省python命令行,可以在运行时加上-p参数。同时自动导入app的model文件,可以直接在命令行命令model中定义的内容了。

今天在web2py的邮件列表中看到的,挺有意思,地址如下:

http://mdp.cti.depaul.edu/examples/static/web2py_comics.pdf

Massimo Di Pierro欢迎人转载。

2008年03月11日

json是很方便的web数据格式,特别是用在ajax的数据处理上。许多Python的框架都是使用 simplejson 包来进行处理。不过最近发现我有这样的一个需求,比如我想把一个Python的数据结构转为javascript数据结构,也就是将python的数据结构转为json格式,然后用在模板中使用。如,我有一个模板,其中有javascript代码:

<html>
<head>
<title>Title</title>
<script type="text/javascript">
s = {{=data}}
</script>
</head>
<body></body>
</html>

上面的data我可以这样处理:

def index():
    data = {‘name’:'limodou’};
    return dict(data=simplejson.dumps(data))

这样,通过simplejson.dumps可以将一个python的数据结构转为json格式,结果为:

{"name": "limodou"}

那么,它可以正确处理基本的数据类型。使用它,要求你传入的数据应该是基本的。但是这里可能有问题:

  1. 如何处理非基本类型数据。比如从数据库中读出的日期,一般都是datetime类型,它不是基本类型,直接传入simplejson会报错
  2. 如果我不希望是"limodou",而是limodou能不能呢?为什么会有这个需求,因为在我生成的js代码中,有些可能是函数名,或执行语句,因此不是字符串,不希望自动加引号

对于第一个问题,simplejson的主页和代码中都有例子,方法就是从JSONEncoder中派生子类,然后覆盖default方法,对于特殊的类型进行处理就可以了。

对于第二个,simplejson好象没有什么支持,于是我做了扩展:

import simplejson as sj

class ComplexEncoder(sj.JSONEncoder):
    def __init__(self, classes=[], **kwargs):
        sj.JSONEncoder.__init__(self, **kwargs)
        if not isinstance(classes, (tuple, list)):
            self.classes = [classes]
        else:
            self.classes = list(classes)
       
    def _iterencode_default(self, o, markers=None):
        for _cls in self.classes:
            if isinstance(o, _cls):
                return o()
        newobj = self.default(o)
        return self._iterencode(newobj, markers)
   
class R:
    def __init__(self, obj):
        self.obj = obj
       
    def __call__(self):
        return self.obj

def dumps(obj, classes=R):
    return sj.dumps(obj, cls=ComplexEncoder, classes=classes)

我定义了一个自已的Encoder类,然后覆盖了__init__()方法和_iterencode_default()方法。这样允许对特殊类调用类的转换方法,这样ComplexEncoder就不会对特殊类进行特殊处理了,其结果为特殊类的输出。举例:

>>> print dumps({‘a’:'dddddd’, ‘b’:R(’sssssss’)})
{"a": "dddddd", "b": sssssss}

可以看出sssssss前后就没有双引号了。

因为要在web2py中生成js代码,目前采用的方式是先写一个模板,然后进行处理后进行填充。我采用的是有命的占位符方式,如:

"%(name)s"

那么在处理时有这样的情况:当某些值存在时,生成某些内容;当不存在时不生成。比如:

"hello, %(name)s"

如果name存在,则打印"hello, name"的样式,如果不存在,则不打印。那么一种做法就是把整个"hello, %(name)s"做成一个占位符,如:

"%(hellomessage)s"

if name:
    hellomessage = "hello, " + name
else:
    hellomessage = ""

这样替换,不过从模板角度就不好看了。于是我做了点扩展,可以支持%if和%else的处理。举例为:

text = """class %(classname)s:
%if init:def __init__(self):
    self.name = ‘name’
%else:pass
%fi
"""

你可以定义%if :, %else:(可选) %fi(结束)。它的处理原理是先找到%if, %else, %fi,然后进行if语句的处理,根据条件值,直接输出后面的内容,此时并不处理。然后处理完毕后,再统一进行变量的替换。所以是两步处理。它不支持嵌套,只是为了方便。%if后面的变量需要用户提供,并且可以提供一个环境。代码很简单:

import re
r_if = re.compile(r’%if\s+(?P<condition>.*?):(?P<true>.*?)(?:%fi|%else:(?P<false>.*?)%fi)’, re.DOTALL)
def template(text, vars, env={}):
    def _sub(m):
        c = m.groupdict()
        condition = c['condition']
        t_t = c['true']
        t_f = c['false']
        if eval(condition, env, vars):
            return t_t
        else:
            return t_f
    text = re.sub(r_if, _sub, text)
    return text % vars

举例如下:

>>> print template(text, {‘classname’:'A’, ‘init’:True})
class A:
    def __init__(self):
        self.name = ‘name’

>>> print template(text, {‘classname’:'A’, ‘init’:False})
class A:
    pass

其实已经不能算是开始了,学习有段时间了,不过不长。也许 django 弄得时间长了,新东西不多,有些审美疲劳了,所以想换一个试试口味。感觉 web2py 还的确很有意思,只不过它创建的时间短,而且开发者就一个人Massimo Di Pierro ,一位德国人意大利人,不过感觉很有激情。最近他要参加pycon08大会,还制作了几顶帽子,说他只能支付这么多了。他好象是一位教师,原来讲过 django ,不过现在改讲web2py了。原来这个框架不叫这个名字,原来叫gluon。

我还是初学乍练,所以许多东西还在学习中。不过已经向Massimo提交了几个patch,修复了几个bug,也提了一些建议。我会努力学习。