Rainfall’s CodeLife

C/C++/C#/VB/JavaScript/Python/XML

 

(转贴)被珠海的“数字”电视恶心了一回 December 31, 2006

Filed under: Miscellaneous — Rainfall @ 7:37 am

实在太精彩了,忍不住转贴了一下。现在科技发达了,什么东西都越来越复杂,可用户体验越来越差。回想一下,我们可能也做了不少这种东西,汗一个……

昨天我那小区整体进行“数字”电视的转换,强制性的签了3年的合同,有线月租从16元提到了25.5元,免费提供一个机顶盒,办完这个手续,感觉就是被“珠害”电视台强奸了一回,痛不欲生啊~~~

装“珠害”电视台的这个“数字”电视,真的很让人恶心,感觉就象是吃了只苍蝇,想吐还不让你吐出来,恶心在哪里,俺就给你列几条出来:

1、设备质量、电视清晰度:

拿到的机顶盒是深圳同洲、一个不知名的小厂做的,盒子做工本身毫无美感可言,跟我的netgear无线AP、XBOX放一起简直就象是一个土包子,做工也非常差劲(从外观上看的,当然我可不想惹事去拆那个盒子,合同上写明了,弄坏了要赔钱的,可不象我的XBOX,可让我随便拆),接口也极其简单,在小区现场所谓的“技术人员”(不知从哪请的一帮年轻後生)在给我演示使用方法时,提到接“色差线”,我当时感觉还挺好,我问他“如果旧的电视没有色差接口怎麼办?”,他象外星人一样的看著我,嘲笑似的告诉我,所有电视都有色差接口,我当时就愣了,仔细拿起他用来演示的那条所谓“色差线”一看,靠,这根本就是一条AV线,我强忍怒气跟他讲了AV线和色差分量线的差别,那小子就愣在那里了,(让我们来看看AV线和色差分量线的区别吧:AV线-用於传输模拟音视频信号,是最“最苦老”的连接线,就象文字里的“甲骨文”;色差分量线-传输质量最好的模拟视频连接线;两者的信号质量是没得比的),说到这里,各位大概也想到了,这盒子就只能用AV线,或者稍好一点的S端子(S-VIDEO),根本没有色差接口,当时我就挺郁闷的,拿回家接上一看,果不其然,什麼所谓的“数字”电视的高清晰度根本就没体现出来,广告上吹的DVD的效果根本就是瞎扯蛋,对比DVD+色差分量的视频质量,那“数字电视”机顶盒+AV的视频质量简直可以用惨不忍睹来形容。我家那台电视是康佳的数字高清电视(也就是电视台宣称的“假数字高清电视”),看模拟电视的效果绝对是一流啊,加上机顶盒後, TMD,电视清晰度根本就没有提高,有些台的收视效果还下去了,画面明显偏暗,真想揪几个“珠害”电视台的领导过来,煽他几个大嘴巴;

2、内容的“多样性”:我们再来看看多的都是些啥玩意-

在线电台:提供了7、8个广播给你听,家里有带收音头的功放的人家应该不少吧,收到的台绝对比这多了去了,而且那音质,比用电视机喇叭也好了去了,再不得不提的就是听收音机时,画面是静止的,如果你家用的是等离子,听久了那可是会烧屏的,其他液晶电视和普通电视机多多少少也会受到伤害,所以这个收音机,不听也罢;

邮件:刚看到遥控器上的“邮件”按钮,还挺开心噢,心想可以用电视机来收EMAIL了,MD,打开一看,原来是专门用来收电视台的通知的,象那些催费通知什麼的,这个功能,您觉得如何?

资讯:打开“资讯”这一栏,呵呵,里面的小栏目还挺多,可您要是有兴趣一个个打开看,那您又要失望了,我记不太清我昨天都看了哪些了,不是记忆不好,实在是被它气糊涂了,能记得起来的象天气预报,我打开一看,晕死,昨天是19号晚上了,显示的是17\18\19三天的天气情况,靠,什麼天气预报,就是一个天气晚报;还有一个人才市场,打开一看,整个一白板,一条内容也没有…这样的资讯,您用得著吗?

游戏:这一栏很搞笑,说明书上还特意写了“请注意身体,请勿沉迷…”的字句,我想这是啥游戏呢,这麼好玩?打开一看,就一个“俄罗斯方块”,比我的电视带的游戏还少,无语;

其他什麼“政务”什麼的,唉,我想聪明的您都能猜出是些什麼了,还有“视频点播”,全是些新闻,记录片,没啥吸引力;

3、操作性:

以前打开电视就能看到电视内容,现在可不行,每次我都先得打开电视,然後用电视遥控器选到我接的AV3,然後启动机顶盒等个5、6秒才能看到电视,那个机顶盒的接收能力非常之差,带的遥控器也非常的烂,经常按了没反应,另外致命的一点就是音量了,每个台的音量都不一样,你换台之後还得重新调整音量,昨天就因为换了一个台,音量突然变大,就被老婆骂了一回,唉;

4、其他方面:

你的电视柜无端端多了一个垃圾盒和一堆接线,占用了空间,而且其外观直接影响了您的客厅风格;

又多了一个电源接口,使您本不富裕的电源插板接口资源极度紧张;

该机顶盒标明功率15W,一户人家每天看3小时为例,一个月就要多消耗1.3度电(还不算待机时的消耗),这样下来,整个珠海电网的负担又会多出多少?

……

这个所谓的“数字”电视,是肥了电视台,坑了我们老百姓啊!

但我们又能做些什麼呢?啥也做不了,就好好在家享受您的“数字”生活吧!

 
 

Python对象转换REST、Json(下) December 22, 2006

Filed under: Python — Rainfall @ 11:47 pm

★ REST格式

 REST格式采用XML标准,这里我们采用xml.dom.minidom来构建REST数据。这里采用递归函数在父节点下转换当前数据:

def simple2rest(self, instance, pnode, force_child=False):
if isinstance(instance[1], (DictType)):

elif isinstance(instance[1], ListType):

elif instance[0][:-1].lower() == ’s’:

else:

instance是当前数据对象二元组(名称,值),其中pnode是上级数据结构对应的DomNode,force_child是标识强制在父节点下建立,这主要是满足list类型的需要,下面会逐个介绍。

A)字典类型:
建立字典节点,并遍历字典所有数据:

node = pnode.appendChild(pnode.ownerDocument.createElement(instance[0]))
for name in list(instance[1]):
  self.simple2rest((name, instance[1][name]), node)

B)列表类型:

过程和字典类型处理方式类似,但如果一个类中有下列情况:

class mybook:
   chapters[] = …

他对应的REST应该是:

<mybook>
  <chapters>
    <chapter … >…</chapter>
    <chapter … >…</chapter>
    …
  </chapters>
</mybook>

这样就需要根据字段名称生成REST中的集合名和集合项目名:

itemname, itemsname = instance[0], instance[0] + ’s’
if instance[0][-2:].lower() == ‘es’:
  itemname, itemsname = instance[0][:-2], instance[0]
elif instance[0][:-1].lower() == ’s’:
  itemname, itemsname = instance[0][:-1], instance[0]

其他基本和字典一样

C)值类型:

首先对于列表元素数据,将列表内容作为列表集合标签的属性处理,由于tag属性不能重名,因此必须先建立单独的节点:

if force_child:
  pnode = pnode.appendChild(pnode.ownerDocument.createElement(instance[0]))

同一个值可以有两种形式存在,如:

<tag some=”…” value=”…”>或
<tag some=”…”>…</tag>

一个Tag最多只可能有一个作为TextNode内容,因此这里的处理方式是将字段名为“value”的数据处理为textnode。

if force_child or instance[0].lower() == ‘value’:
  pnode.appendChild(pnode.ownerDocument.createTextNode(str(instance[1])))
else:
  pnode.setAttribute(instance[0], str(instance[1])

★ 值类型过滤

在Json中,布尔表示为“true”和“false”,而在REST中通常会表示“1”和“0”,另外枚举的处理也需要转换为字串,比如“read”比“1”要好的多。上面都是默认用Python的str函数转换值类型,为了满足上述要求,必须做针对类型的过滤器:

ins_value = self.fmtfilters.type2str(instance[1])

class ValueFilters:
  trans_filters = None

  def type2str(self, instance):
    if self.trans_filters and self.trans_filters.has_key(type(instance)):
      return self.trans_filters[type(instance)](instance)
  return str(instance)

在简单类型转换过程中,会先查找某个类成员是否有对应的转换函数,如果有则不调用默认的obj2simple:

fmtname = ‘format_’ + name + ‘_field’
if hasattr(instance, fmtname):
  clsdict[name] = getattr(instance, fmtname)()
else:
  clsdict[name] = obj2simple(field)

 
 

Python对象转换REST、Json(上)

Filed under: Python — Rainfall @ 11:42 pm

参考资料:

http://www.json.org/

http://www.peej.co.uk/articles/rest.html

★ 背景

Representational State Transfer(REST)和JavaScript Object Notation(Json)是现在互联网应用的常见数据表示规格。因此在服务器端开发网络应用时,服务端语言与REST和Json之间的互换非常重要,既内核对象存盘为REST/Json格式,以及REST/Json格式读盘重建内核对象。下面先介绍前者。

★ 简化对象数据模型

REST/Json共同的优点就是数据模型简单,其基本数据模型由三种组成:

A)字典:包括类在内,都采用{数据名:数据值},是唯一的数据命名方式

B)数组:作为一个字典的数据值保存同类数据集合

C)简单值:包括布尔,数值,字串等,实际的数据载体

因此将Python的数据类型简化为上述三种方式是存盘的第一步,首先将Python类型分类:

LIST_TYPES = (ListType, TupleType)
COMPLEX_TYPES = (InstanceType)
DICT_TYPES = (DictType)
VALUE_TYPES = (BooleanType, StringType, LongType, IntType, FloatType)

通过递归函数扫描对象数据成员:

def obj2simple(instance):
if isinstance(instance, LIST_TYPES):
  return [obj2simple(o) for o in list(instance)]
elif isinstance(instance, COMPLEX_TYPES):
  return cls2simple(instance)
elif isinstance(instance, DICT_TYPES):
  return dict2simple(instance)
elif isinstance(instance, VALUE_TYPES):
  return instance

dict2simple函数和list2sample一致,只是简单的转换字典数据并重建新字典。稍微麻烦点的就是复杂类类型转换,首先先建立有效类成员列表:

members = [(member, getattr(instance, member)) for member in dir(instance) if not callable(getattr(instance, member)) and not member.startswith(’_')]

这个成员列表将类函数以及以’_'开头的成员(私有)排除,剩下的做法其实就是一个dict2sample过程。

ok,建立一个复杂些的测试案例:

class scls:
def asdf(self):
  pass
arr = [3, 5, 5]
n = 9
b = True
s = ’sfafds’
value = ‘xxxx’

dic = {’a':3, ‘aa’:scls()}

class cls:
def asfl(self):
  pass
i = 2
s = ‘asf’
clsarr = [2, ‘af’, scls(), scls()]
clsdic = {’a':34, ‘aa’:scls(), ‘d’:dic}
t = (4, ’ss’, dic)

转换结果:

{’i': ‘iii2′, ’s’: ‘asf’, ‘clsarr’: [2, ‘af’, {’arr’: [3, 5, 5], ‘b’: True, ’s’: ’sfafds’, ‘value’: ‘xxxx’, ‘n’: 9}, {’arr’: [3, 5, 5], ‘b’: True, ’s’: ’sfafds’, ‘value’: ‘xxxx’, ‘n’: 9}], ‘t’: [4, ’ss’, {’a': 3, ‘aa’: {’arr’: [3, 5, 5], ‘b’: True, ’s’: ’sfafds’, ‘value’: ‘xxxx’, ‘n’: 9}}], ‘clsdic’: {’a': 34, ‘aa’: {’arr’: [3, 5, 5], ‘b’: True, ’s’: ’sfafds’, ‘value’: ‘xxxx’, ‘n’: 9}, ‘d’: {’a': 3, ‘aa’: {’arr’: [3, 5, 5], ‘b’: True, ’s’: ’sfafds’, ‘value’: ‘xxxx’, ‘n’: 9}}}}

★ Json格式

通过Sample数据模型转换Json格式就简单多了,其实从前面的测试结果(str(sampleobj))看到的结果就是一个Json字串了,但是我们必须重写,因为Json对Boolean的标识和Python情况不同,另外,也不能满足对某些特殊返回形式的需要。

def simple2json(self, instance):
  if isinstance(instance, (DictType)):
    return ‘{’ + ‘,’.join([’”%s”=%s’ % (o, self.simple2json(instance[o])) for o in list(instance)]) + ‘}’
  elif isinstance(instance, ListType):
    return ‘[’ + ‘,’.join([self.simple2json(o) for o in instance]) + ‘]’
  else:
    return str(instance)

 
 

网站认证方法

Filed under: Miscellaneous — Rainfall @ 6:52 am

参考资料:
http://www.yupoo.com/apidoc/
http://code.google.com/apis/accounts/Authentication.html

网站的登录认证方式从单一网站到认证系统基本可以分为三种形式:

  • 单网站单服务器登录
  • 单网站多服务器登录
  • 以及多网站多服务器认证

传统网站由于其体系相对的封闭特性,通常都只采用前两种登录方式,但随着Web2.0对网站的开发性要求,很久以来本地软件就已经成熟的API体系也逐渐被网站应用采用。

而网站应用最重要解决的问题就是安全认证,在保证网站内部关键数据不外泄的前提下提供信息。认证登陆则是解决这个问题的门锁。

★ 单网站单服务器登录

这是最简单的认证形态,登录信息不存在共享,只被一个应用服务器使用。因此,无论采用何种方式记录登录信息都可以,session数据可以放在服务器内存中,或者服务器数据库。用户在浏览器中输入帐号密码,发送到服务器后,由服务端通过用户数据库进行认证后将用户标识记录在服务器内的SessionData中。而客户端只记录Session的标识。

Account&Password <==|==>Session<==|==>KeyData
         (Client)                         (Server)               (BackDB)

用数据库记录Session数据有两个目的,其一是为了实现永久性登录,需要在Client端利用Cookie记录SessionID,以便服务器恢复登录状态(Session)。所以对于CookieDisable的浏览器是无法实现永久登录的。这就需要在数据库中保留Session数据。Django的Session数据库字段非常简单(SessionID,Data,Expire),通常Session数据都很简单,只记录标识,不记录大数据内容。

数据库维持Session另一个原因就是下面的多服务器登录。

★ 单网站多服务器登录

由于内存数据无法跨进程共享,更不可能做到跨服务器共享。但是现在大部分网站都由多台主机同时提供服务,用户在前后多个页面时,很有可能是有分别不同的主机提供的内容。如果内容和具体用户无关还好,但如果提供的个性化页面情况就不同了,这些服务器必须保存状态数据。

现在的互联网应用基本遵从“无状态服务”架构,这也是现在互联网能够服务海量服务请求的一个重要前提,所以SessionData应该尽量简单,其根本目的就是为了标识,而不是存储。
现在越来越多的网站都不再遵守这个框架原则,这主要是现在互联网对个性化服务的追求带来的变化,因此在设计具体网站时,要认真把握好有无状态的平衡。

状态数据必须利用数据库实现跨服务器访问,通常内容服务器只需要读取Session数据,而需专门的认证服务器来创建(或恢复)登录信息到Session中。

Account@Psw <==|==> LoginServer <==|==> UserDatabase
                        |
                     Session
                        |
    Browser <==|==> ContentServer(s) <==|==> ContentDatabase

由于大型网站对登录的需求多种多样,主要是登录完成后的处理方式的不同:

A)单独登录页面

这种方式需要在登录成功后进行重定向,比如网站首页,登录前的页面。最简单的方式就是通过Url传递返回地址,如:

http://somesite.com/login/?back=http://somesite.com/index

前段时间微软发布了一种“PageFunction”概念,这样就可以把登录页面作为一个函数来调用,这样可以想函数调用一样更方便的解决返回地址问题。

Caller       Page Func
——       ———
Call page =>
             login page
Call page <=

B)嵌入式登录页面

在网站中对于登录需求有两点很强烈:第一就是返回登录前地点,第二就是随时随处登录。嵌入式登录页面可以很好的解决这个问题,甚至可以做到登录时不跳转当前页面。

+————+——————–+
|            | login page(frame)  |
|            +——————–+
|             内容页面             |
|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

通过Frame嵌入登录服务器提供的登录页面,登录页面通过对当前用户状态的判断,决定显示登录内容还是注销、用户信息。而内容页面可以通过Session或DOM对登录进行控制。这种方式其实本质就是利用frame解决了前种方案的跳转问题,而且实行方式不需要内容页面做特殊处理,甚至可以通过master page将登录小页面实现到所有内容页面上,解决随时随处登录的问题。

但是随着网络应用的复杂度增加,内容服务器不可能都用同一个平台开发,甚至不可能运行在一个局域网内,更谈不上访问Session数据库。解决这个问题,就必须采用开放式的认证系统。

★ 多网站多服务器认证

对于不能运行在同一个局域网的服务器组,我们可以认为这些服务器是互不信任,甚至真的就是第三方的服务器,比如我要做一个专题图片展览,而应用的图片都来自于Flickr或者Yupoo,这就需要获取Flickr和Yupoo的认证。当然,Flickr不可能公开他的UserDatabase,其实我也不需要,我只要能让Flickr知道我能访问权限即可。

Client     专题展览应用      Flickr
——     ———-      ——
  –图片请求–>    —请求登录—>
  <——————登录页面—
  –输入账户密码—————->
  <–SessionId–  <–认证标识—
  –SessionId–>  —认证标识–>
  <——————图片数据—

从这个交互图,我们可以看出“认证标识”其实可以认为就是Flickr的SessionId,但实际并不是这么简单。原因有几个:

A)账户和密码只能由Client直接在Flickr上输入(不能让专题应用知道用户隐私)

B)flickr的SessionId只能保留在Client中,专题应用是拿不到的

C)不可能应用每做一次flickr调用之前都让用户输入密码

因此上图中的认证标识必然是一个“登录用户标识”和“应用标识”的对应体,而且是稳定存在的。因此登录流程改变成:

Client         应用          Flickr
——         —          ——
   –登录–>     –登录(+key)–>
  <–SessionId-  <–Token—–(根据账户,应用Key创建Token)

key就是一个第三方应用的标识,而Token则对应了唯一的UserAccount+AppKey。Token是一个比session更持久的认证标识,为了使过程更可控,这个过程会优化为:

Client         应用          Flickr
——         —          ——
   –登录–>      –请求frob—>
                 <–frob(一段时间内有效)–
                 –登录(+key+frob)–>
                 <–frob—–(根据账户,应用Key创建临时Token)
                 –获取token—->
<–SessionId-    <–Token—-(将临时Token转化为正式Token)

frob就是一个请求token的临时入场券,token生成后必须在要求的时间内确认获取才会生效。

Token是一个很可怕的东西,他会通过Http的Uri或者Form明文传递,而且始终有效,一旦被别人掌握,这实在太可怕了!解决方法很简单,只需为每个创建的AppKey都同时随机创建一个ShareSecret,这个secret只被“应用”和“flickr”掌握,需要传递Token的时候,只需要把secret和调用参数合在一起,利用md5等不可逆算法计算出“签名”一并发送即可。

 
 

势不可挡的互联网大潮 December 17, 2006

Filed under: Miscellaneous — Rainfall @ 8:49 pm

酷橘在线-FrontPage即将死亡

在服役了9年之后的Office悍将FrontPage,将于今年下半年正式退役。微软是这么说的:

作为一种获奖的 Web 创作工具,FrontPage 已服务客户九年多了,它准备在 2006 年下半年退出市场。

FrontPage,这个互联网洪荒时代便开始拓荒的产品。在经历了9年的风雨之后,不得不承认自己已经不再适应这个时代。譬如博客的兴起,网页的创建变得极为简单,可以说博客在一定程度上加剧了FrontPage的死亡。

互联网终结的第一个Office组件,正如预料的那样,导致这一结果的并不是“功能不能满足用户”,“易用性不好”这些功能,而是互联网改变了用户的生活、习惯,改变了用户的需要,从根本上掐死了FP的生存之路。这就是潮流的力量。

Web2.0对互联网最大的影响莫过于改变了用户的互联网生活方式,更多的人以参与者的身份加入互联网,Digg.com, YouTube.com, Del.icio.us这些都是趋势的标杆。现在一个用户坐在电脑前面做的最多的事情是什么?用什么“服务”(不是软件)做这些事情?还需要什么样的“服务”?这些问题都和我们之前大不一样了。比如我的Del.icio.us今天的收藏数量是498条,可以想想Del.icio.us掌握的这些被整理的互联网资源有多少,而反过来讲,我现在已经形成了看到不错的网页收藏下来,今后我还会去查么?几乎没有。我收藏的动机就是让我的朋友知道我今天又看了什么,这是我价值的体现手段之一,这也是我的互联网生活的一部分。

所以,这种趋势对传统软件带来的冲击是巨大、且不可逆转的。

 
 

Django系列-搭建简单WebApp与EclipseIDE调试 December 14, 2006

Filed under: Python — Rainfall @ 8:44 am

★ Django简介

Django是基于Apache,使用Python语言搭建的Web服务框架,它提倡DRY原则,因此整个架构具备很高的定制能力和扩展性,提倡Web开发的组件化,关于组件化这点,随着下面更深入的介绍Model,View以及filter和tag,你一定能更深刻的理解这点。

调试是严重影响开发效率的因素之一,我一直都不能理解大把的人做了那么久的JavaScript开发,竟然从来没有用过Debug,最多就是alert。哦,跑题了 :P 所以工欲善其事,必先利其器,最后在补充一下在Eclipse中调试Django的方法。

★ 最简单的Django网站

在次之前请先确保你已经安装了所有Django所需的组件,请参考之前的Django安装介绍《Apache+Python+Django+MySql搭建应用》。

首先我们要先建立一个网站工程,比如我们在c:\pyweb目录下建立一个pysite网站,请在pyweb目录下运行:

python django-admin.py startproject pysite

django-admin.py会在此目录下建立下列文件:

pysite
   __init__.py(python的package)
   manage.py(对django-admin.py的简单封装)
   settings.py(Web网站的参数设置)
   urls.py(网站Url的映射管理)

这样最简单的一个Web网站就完成了,你可以使用runserver命令启动这个网站:

python manage.py runserver 

除非你用runserver参数指定端口,默认是8000端口。当然你只能看到一个Django缺省页面。Django-admin.py是django最强悍的军刀,它存在与“PythonInstall/Lib/site-package/DjangoInstall/bin/”下面,建议你设置到PATH环境变量中。startproject命令只是建立了网站所必须的三个文件:

manage:其实就是django-admin的封装,封装也非常简单,就是设置了本网站对应配置文件。
settings:网站运行所需的所有配置参数,它其实是override了django的默认设置“DjangoInstall/conf/global-settings.py”。
urls:django采用了正则表达式作为url匹配的条件,最先匹配就返回,因此要注意url条目的顺序。正则表达式中的捕获(Capture)则会直接传入对应的view(就是和RegExp对应的函数)中。

★ 建立网站数据(model)

一句话就可以搭建网站很方便,但用django建立数据模型更快捷。首先在pysite目录下建立一个新的应用(WebApp)。

python.exe manage.py startapp polls

django-admin会建立下列文件:

polls(应用包,可复用单元)
   __init__.py
   models.py(数据模块)
   views.py(显示视图)

打开models文件,编写下面代码:

from django.db import models
class Poll(models.Model):
    question = models.CharField(maxlength=200)
    pub_date = models.DateTimeField(’date published’)
class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(maxlength=200)
    votes = models.IntegerField()

这样就建立了polls应用的数据模型,规定了数据模型的关系,数据类型,数据名称。django会根据这些代码自动建立数据库表。建立数据库的方法就是打开settings文件,填写数据库的地址、名称、类型。在INSTALLED_APPS中增加“pysite.polls”。然后运行syncdb命令即可:

python.exe manage.py syncdb

当然数据库中是否自动建立了所有的表,包括Polls和Choices。数据模型建立就完成了,你可以运行manage.py shell来对数据模型进行测试,比如新建,修改等等,有兴趣可参考django文档。

★ 建立后台数据管理。

建立数据管理的简洁程度最让我惊讶。只需两步:

1、修改settings.py中的INSTALLED_APP,去掉那几个被注释的应用,再次运行syncdb。django会建立认证用户数据,以便进行后台管理。
2、在Poll和Choice类中增加空的Admin子类。

再次访问“http://localhost:8000/admin/”即可看到登陆界面,登陆后就可以在管理界面进行数据管理,django甚至帮你自动做了数据关联,历史记录等等。后台管理也完全可定制,有兴趣可参考django文档。

★ 建立视图(views)

写了很多,但其实到此为止我们只用了5步就建立了完整的网站数据模型,甚至包含了一个非常美观的后台管理站点。最后,就是要把数据展现出来。还记得建立应用时说的urls文件么?你可以增加下列代码:

    urlpatterns = patterns('’,
        (r’^polls/$’, ‘mysite.polls.views.index’),
        (r’^polls/(?P《poll_id》\d+)/$’, ‘mysite.polls.views.detail’),
        (r’^polls/(?P《poll_id》\d+)/results/$’, ‘mysite.polls.views.results’),
        (r’^polls/(?P《poll_id》\d+)/vote/$’, ‘mysite.polls.views.vote’),
    )

然后在views.py编写视图代码:

    def index(request):
        return HttpResponse(”Hello, world. You’re at the poll index.”)
    def detail(request, poll_id):
        return HttpResponse(”You’re looking at poll %s.” % poll_id)

这样就完成了视图映射和视图实现。很巧妙的是,匹配url时的capture会作为视图函数参数传入,这样url地址就变成了函数调用,url中就自然包含了调用参数。

★ 用简单模版改善视图

首先在pysite目录下建立templates/polls目录,并建立一个detail.html文件:

《h1》{{ poll.question }}《/h1》
{% if error_message %}{{ error_message }}{% endif %}
{% for choice in poll.choice_set.all %}
    《p》{{ forloop.counter }} {{ choice.choice }}《/p》
{% endfor %}

修改views.py的detail实现:

from django.shortcuts import render_to_response
def detail(request, poll_id):
   p = Poll.objects.get(pk=poll_id)
   return render_to_response(’polls/detail.html’, {’poll’: p})

运行一下就可以看到效果了。django通过模版实现了页面设计与开发的紧密结合,不仅如此,template是django的核心,它还可以实现模版继承、tag定制、filter定制等等,这在稍后会专门做介绍。

至此一个较完整的网站就完成了,最后再看看调试问题。

★ Eclipse调试Django

调试之前确保安装的Pydev。首先建立一个Pydev工程,默认会建立一个src目录。完成后通过import导入pysite所有文件:

src
   pysite
      polls

在Project-》Preperties中添加pysite目录为PYTHONPATH。另外,确保pydev设置了“DjangoInstall/bin”目录为Path(Windows-》Preferences)。

这样你选择manage.py文件,然后设置Debug参数为runserver即可。

不过你会发现console里面没有熟悉的django启动信息,你只要在参数中增加noreload即可:

runserver –noreload

如果没有以外,就可以设置断点进行调试了。

 
 

推荐三个Django文档 December 12, 2006

Filed under: Python — Rainfall @ 12:06 am

★ 官方文档

http://www.djangoproject.com/documentation

英文的,但读起来很顺畅,强烈建议通读一下,建议先从《Tutorial: Writing your first Django app》开始,他会叫你做一个最简单的Web应用,例子虽然简单,却涵盖了Django最重要的内容,甚至可以用一行代码搭建起后台管理系统。

Django很多设计想法确实很巧妙Model,View,Filter,Tag,Template继承等等,是一个高定制性体系,无论是否使用Django,他的很多设计思路都非常值得学习。

 

★ IBM文档

http://www.ibm.com/developerworks/cn/linux/l-django/

如果你实在不愿意阅读英文文档,这里有一个内容相似的文章。

 

★ 中文SBS

http://www.woodpecker.org.cn/obp/django/django-stepbystep/newtest/doc/

好文章,译者一直在跟进这个工作,甚至翻译了历史文档。

这里给“啄木鸟社区”做个免费的广告,一群很有理想的人们在做一件很有意义的事情。

 
 

Apache+Python+Django+MySql搭建应用 December 11, 2006

Filed under: Python — Rainfall @ 11:51 pm

前段时间一直是用的“唐吉柯德”在Python上做WebApplication,最近想做一个Web应用,由于唐吉柯德没有很好的支持Html模版,想换一个学习学习。到网上搜了一下后才知道最近Django很火,而且他的特性之一就是很好的支持模版,所以搭建了一个,这里先介绍一下搭建过程。

★ Apache (www.apache.org)

由于mod_Python对Apache最新的2.2支持有些问题,因此建议安装Apache 2.0.x。Django会基于Apache建立新的监听端口,因此安装Apache时可以不用选择默认的80端口,也不用自动启动WindowService。

★ Python (www.python.org)

没什么好说的,就是要安装Python2.4,因为mod_python现在还不支持2.5版本的Python。
需要注意,不要忘记增加Path环境变量PythonInstall/目录。

★ mod_Python (http://www.modpython.org/)

mod_Python是一个Apache的Python封装,可以让Python访问Apache,他本身是一个Apache的模块,安装完成后,会在Python/Lib/Site-Package下建立一个目录。
mod_Python最新版本是3.2.10,但是现在还没有提供WinInstall,如果你有兴趣自己build,可以用一下,我这里用的是3.2.8,安装起来很方便。只有两点需要注意:
1、选择Apache目录时,一定要选择Apache2目录,否则安装会失败
2、别忘了安装完成后,要在Apache的conf (ApacheInstall/conf/httpd.conf)中添加下面的代码:

LoadModule python_module modules/mod_python.so

mod_python.so已经安装到了ApacheInstall/modules/下面了。

★ MySql (www.mysql.org)

MySql的安装很简单,就是记得必须安装Python调用封装MySql for Python(http://sourceforge.net/project/showfiles.php?groupid=22307)。

★ Django (www.djangoproject.com)

最后一步了,Django的安装需要先把发布包下载到本地后,调用

python setup.py install

安装过程需要安装工具“setuptools-0.6c1-py2.4.egg”,这个文件并不在安装包中,你有两种方法获取:
1、调用setup.py后等待15秒后,安装过程会自动下载并安装
2、单独下载后放在和setup.py同级目录后,再运行setup.py

如果一切正常,很快就可以结束安装。但还有后续工作要做,到PythonInstall目录下,还是刚才那个Lib/Site-Package里面多了一个Django目录(版本不同可能目录略有变化,我的就是Django-0.95-py2.4.egg。把他的bin目录追加到系统Path环境变量下,因为这里面有下一步经常会用到的管理工具”Django-admin.py”。

好了,这样就万事具备了,下一步就可以开始用Django搭建简单应用了。

 
 

事业,还是工作 December 9, 2006

Filed under: Miscellaneous — Rainfall @ 8:12 am

   很多人都在扪心自问:“我是在做一份事业,还是仅仅为糊口?”大多数人做事的确是迫不得已而为之,仅仅为养家,这样的做事方式,无论挣多少钱都不能称之工作。
   在高度发达的经济社会,人们的很多价值观在改变,更多的事情以金钱来衡量,人们日益相信有钱能使鬼推磨,赚钱多少成哼衡量事业成功与否的标志。哪怕他做的是最无聊、不鄙劣的事情,比如卖淫、赌博……..。社会发展到今天,不知道这种观念的进步,还是思想的倒退。
    鲁迅先生说过:“我们从古以来,就有埋头苦干的人,有拼命硬干的人,有为民请命的人,有舍身求法的人……虽是等于为帝王将相作家谱的所谓‘正史’,也往往掩不住他们的光耀,这就是中国的脊梁!”忆往日,岳鹏举“驾长车,踏破贺兰山缺”,拯救黎民于水火;辛弃疾“求田问舍,怕应羞见,刘郎才气”,始终心系国家命运;曹植“闲居非吾志,甘心赴国忧”,这些忧国忧民的铮铮汉子,以一段人生“留取丹心照汗青”。这些先天下忧而忧,后天下乐而乐的汉子所从事的工作才能成为事业。
    事业与钱无关,它仅仅是一种个人的追求和爱好,并在这个过程为人民谋福芷;如果你现在正在从事一份工作,它能给你带来愉悦和自信,且对社会有意义,就是事业。也许回报不高,但凡想作出点明堂的人,必须得忍受住清贫和寂寞…….

 老婆可能是晚上去莲花路溜达了一圈,受了刺激后写了这篇慷慨的文字,一定要我贴出来。不过做事业要忍受的住寂寞这句话好像是说给我听的,呵呵。

 
 

Atlas XML-Script原理分析 December 8, 2006

Filed under: JavaScript — Rainfall @ 8:26 pm

★背景:

Atlas最新的社区技术预览版发布了强悍的Client Kit Controls,其中允许用户使用一种号称XML描述来开发客户端逻辑,而且从例子上看也非常简单。大家有兴趣可以看看这个网站:

http://dflying.cnblogs.com/archive/2006/04/08/370331.html 也可以访问http://192.168.50.72/AtlasWebSite/ListViewDemo.aspx看看这个demo。

script type="text/xml-sc ript">
  page xmlns:script=" http://schemas.microsoft.com/xml-script/2005">
    components>
      dataSource id="listDataSource" serviceURL="MyService.asmx"/>
    components />
    ...
  page />
script />

简单的通过XML配置就可以实现一个名为MyService的WebService调用(严格来说是微软的一个新的DataService,但原理就是WS)。我只看过text/javascript或者text/vbscript,这个xml-script是什么东西?网上一坨坨的都是介绍这个东西多好用,但是什么东西却不明不白。搞清楚它,就是Atlas的核心。也对我们的JS理解会大有帮助(事后感言)。让我们开始探索之旅:

★XML-SCRIPT运行在哪里?

无非Client或Server。用Fiddler(强烈推荐,微软的一个人写的http调试工具,免费)看一下我上面的ListViewDemo的例子,前两个http请求是atlas被着用户搞得事情,做的事情很简单,把Atlas.js和本地语言特征下载到本地,这就是为什么开发Atlas时,一定要写ScriptManager标签的原因。sorry,有点跑题。跳过这两个包,第三个很有意思:

POST /AtlasWebSite/MyService.asmx?mn=GetData HTTP/1.1
{"parameters":{},"loadMethod":""}
返回:
new Sys.Data.DataTable(
[new Sys.Data.DataColumn("Name",String,null,false,false),
new Sys.Data.DataColumn("Email",String,null,false,false)],
[{"Name":"Dflying 0","Email":" Dflying0@dflying.net"},
{"Name":"Dflying 1","Email":"Dflying1@dflying.net "},
{"Name":"Dflying 2","Email":"Dflying2@dflying.net"},
{"Name":"Dflying 3","Email":" Dflying3@dflying.net"},
{"Name":"Dflying 4","Email":"Dflying4@dflying.net "}])

什么嘛,怎么就调用MyService这个WebService了!太Magic了吧。不过这说明一个问题,IE并没有把XML-Script发给Server去解释执行,而是在Client的某个见不得人的地方把他解释并执行了。

★客户端的阴暗角落:

既然是在Client端执行的XML-Script,那还有两种情况,Brower 支持,还是前面说的Atlas.js支持。开始我怀疑是前者,因为我的Opera不能运行这个例子。后来证明我开始猜错了,看来Opera对JS的支持还要提高亚。我就就告诉你吧,是JS做的事情。我是从Atlas的ReleaseNote的一句话看到的。但他到底怎么做到呢?

★再次深入Atlas.js

既然Atlas.js要解析XML,那Atlas.js里面一定有Tag名称,这时我才发现恐怖的事情,Atlas所有的Control类都实现了一个GetDecription的函数,这个函数就做了一件事,就是往一个td (TypeDescriptor)里面加Tag名字和对应类型(又是NOP)。一定有哪里通过这些NOP来解析XML。搜索一把,在 Sys.TypeDescriptor.getTypeDescriptor函数中调用了他,而这个函数调用的地方好多,总算被我找到了:!!!

Sys.MarkupParser

从名字上看,我知道有门了。这个函数的原理不用讲了,想也知道怎么做的,但这个函数是怎么被调用的,怎么就和Html里面的xml-script联系起来了。

★豁然开朗:

MarkupParser是在Sys._Application的一个OnRuntimeLoad函数中调用的,一看就是一个EventHandle。它就是通过下列代码绑定消息的:

Sys.Runtime.load.add(onRuntimeLoad);
Sys.Runtime.unload.add(onRuntimeUnload);

这样,整个xml-script的解析和运行时架构就全部清楚了。

★最后补充一句:

如果你认为有个一个Sys.Runtime.load/unload消息还不够,想知道这两个消息是怎么回事,下面看:

Sys.Runtime =new function()
{
  window.attachEvent('onload',onWindowLoad);
  window.attachEvent ('onunload',onWindowUnload);
  this.initialize =function() {
    Sys.Runtime.load.invoke(Sys.Runtime,null);
  }
  function onWindowLoad() {
    Sys.Runtime.initialize();
  }
}

看到了吧,XML-Script的跟源就是DOM的window的onload消息和onunload消息,要再问我这是什么,hoho,我真没有招数了。