DragonPy -- 又一个新的webapp架构,它是面向python程序员的,包含了 Karrigell 和 Quixote,大家一起来造这个轮子(LiHui)。
- 龙的名字俺也不满意,太俗太没创造性,前两天陪女儿看挪吒,这个名字也不错,孙猴子也成,就是不知道英文怎么写?
::-- ZoomQuiet [DateTime(2005-07-13T02:41:38Z)] TableOfContents
现有 Web Application 点评
这么多Web Application,到底用什么好啊 俺以前一直用zope,后来感觉是越来越差,原因与Quixote和Cherrypy的作者差不多,网上有他们的文章,俺这里就不说了。后来找啊找啊,前前后后重点试过Quixote、Cherrypy、Karrigell,感觉都还不错,各有各的特点: Cherrypy:将对象机制引入web开发,完全的面向对象发布机制,底层数据、api函数使用cpg集中管理。缺点是底层api调用代码老在变,天啊,俺真的受不了,以前做了一个cherrypy小应用,前前后后重构了10来遍,每次只要用svn更新一下cherrypy代码,就得改一次俺的应用代码。俺就搞不明白,比如一个调用session,就用cpg.sessions就成了吧,为什么一会儿这个样式,一会儿另外个样式。 Quixote:不错,发布方式多,代码结构好,质量最佳,基本上没什么BUG。只是语法调用有点怪异,适应了半天,最后还是不适应。 Karrigell:最易用的系统,入门真简便,良好的debug框架,动态载入。只是代码结构不好,做个应用就得将Karrigell移过去。再有url不支持定制,太有局限性,比如我动态生成一个js文件,总不能扩展名是个py,hip...吧,而且作者象俺有点不务正业,不老老实实做好框架,却去设计什么dbstrange,还做了一堆没用的半成品,唉,有这功夫把自留地种好啊。感觉做做原型、快速开发还不错。 PS:这两天谈的比较多的是ruby的web架构,不太感冒,这东东也就是让java程序员见识一下:哦,这么这么简单啊,其实不用编码的框架是不存在的,功能实现与编码量本身就是一对对头。
需求
见识了这么多框架,俺理想的架构长得模样也自然产生了(主要性能): 1)具有多多的发布接口,象Quixote,就是俺以前提过的万能接口,接口越多,余地越大啊。 2)底层API与数据的集中管理与调用。比如dgn.get_request(),dgn.get_reponse(),这里的特性是Quixote与Cherrypy的结合和升华。 3)简便的发布方式。考虑web前端部份的python代码是最易变的,尽可能简单,易读。利用所有都是对象的性能,选择函数发布。 4)支持url定制,易url的default性能。这个需求好象现在很重要,总比使用apache的mod_write好多了。 =一个Hello world模块 还是用一个简便例子说明:
1 #--系统函数--
2 def _charsetGB2312(fn):
3 '''函数包装函数,将页面设为gb2312'''
4 def _init(dgn,*args):
5 dgn.set_charset('gb2312')
6 return fn(dgn,*args)
7 return _init
8 def _baseAuth(dgn,func,realm='Protected'):
9 '''权限函数,只有我能上'''
10 t=dgn.get_baseauth()
11 if t is not None and t==('lihui','password'):
12 result=None
13 else:
14 dgn.set_status(401)
15 dgn.set_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
16 result=True
17 return result
18 #--web前端--
19 def helloworld1(dgn):
20 '''发布一个函数,dgn为数据api集中调用入口,为第一个参数,dgn使用的singlon模式,除系统内置定义外,可自行加入定义,比如dbpool,可定义为dgn.db.dbpool
21 '''
22 return 'helloworld1'
23
24 def helloworld2(dgn):
25 '''发布一个函数,加个权限'''
26 return 'helloworld1'
27 helloworld2._q_access=_baseAuth
28
29 def helloworld3(dgn):
30 '''发布一个函数,gb2312发布'''
31 return 'helloworld1'
32 helloworld3=_charsetGB2312(helloworld3)
33
34 def helloworld4(dgn,*args):
35 '''有args的支持default,比如这个函数url为http:/***/p,这个http:/***/p/1/1/1.html,则转为args=['1','1','1.html']'''
36 return 'helloworld1'
37
38 def helloworld5(dgn):
39 '''发布一个图形文件'''
40 return 图文件
41
42
43 def images(dgn,*args):
44 '''发布images子目录,静态目录'''
45 return dgn.StaticDirectory(os.path.join(dgn.get_config().path_top,'images'),*args)
46
47 #--发布函数
48 def root():
49 result=helloworld1
50 result.images=images
51 result.hl2=helloworld2
52 result.hl3=helloworld3
53 result.hl4=helloworld4
54 result.hl5_jpg=helloworld5 #扩展名为jpg
55 return result
网站架构组合
原来一直用的是zope的zpt和dtml,cherrytemplate是两者的混合,使用很方便,模块就选它拉 sqlobject真不错,实行了关系数据库+逻辑+对象处理,zodb和存储过程俺再也不用拉。 俺的最佳组合就是dragon+cherrytemplate+sqlobject
sqlobject一段代码
谈sqlobject的好象不是很多,发一段俺的代码,见识一下sqlobject的必杀绝技
from sqlobject import * __connection__='mysql://root:123@localhost:3306/bm' class UserMark(SQLObject): class sqlmeta(sqlmeta): table = "user_mark" idName = "id" #style = Style() defaultOrder = 'id' _cacheValues = True username=StringCol(alternateID=True,length=30,notNone=True,default='') userpasswd=StringCol(length=20,notNone=True,default='') userdisplay=StringCol(length=60,notNone=True,default='') #---------------------------------- ip=StringCol(length=40,notNone=True,default='') #---------------------------------- sex=StringCol(length=2,notNone=True,default='') #本地库表 email=StringCol(length=60,notNone=True,default='') #本地库表 qq=StringCol(length=20,notNone=True,default='') #本地库表 web=StringCol(length=60,notNone=True,default='') #记录ID msn=StringCol(length=60,notNone=True,default='') #记录ID timeCreate=DateTimeCol(notNone=True,default=now) countAccess=IntCol(notNone=True,default=0) countReply=IntCol(notNone=True,default=0) countSubmit=IntCol(notNone=True,default=0) userType=IntCol(notNone=True,default=0) userMoney=IntCol(notNone=True,default=0) #---------------------------------- def addUser(cls,user,passwd1,passwd2,email): errorchar=['"',"'",'=','?','.','*','&','%','<','>',' ','!'] try: if passwd1<>passwd2: raise 'Password is not same' for v in errorchar: if v in user or v in passwd1: raise 'Error username or password' result=cls(username=user,userpasswd=passwd1,email=email) except: result=None return result addUser=classmethod(addUser) #---------------------------------- def checkUser(cls,user,passwd): try: t=cls.byUsername(user) if t.userpasswd==passwd: result=t else: result=None except: result=None return result checkUser=classmethod(checkUser) #----------------init db-------------- DBDict={ 'UserMark':'UserMark', } def proxyDB(tname): return globals()[tname] def clearDB(): for k,v in DBDict.items(): proxyDB(v).dropTable(True) proxyDB(v).createTable() if __name__=='__main__': clearDB()