2010年8月25日星期三

两份文本

原文授权都是public domain

Bible (King James)/Genesis
Chapter 1

1 In the beginning God created the heaven and the earth.
2 And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.
3 And God said, Let there be light: and there was light.
4 And God saw the light, that it was good: and God divided the light from the darkness.
5 And God called the light Day, and the darkness he called Night. And the evening and the morning were the first day.
6 And God said, Let there be a firmament in the midst of the waters, and let it divide the waters from the waters.
7 And God made the firmament, and divided the waters which were under the firmament from the waters which were above the firmament: and it was so.
8 And God called the firmament Heaven. And the evening and the morning were the second day.
9 And God said, Let the waters under the heaven be gathered together unto one place, and let the dry land appear: and it was so.
10 And God called the dry land Earth; and the gathering together of the waters called he Seas: and God saw that it was good.
11 And God said, Let the earth bring forth grass, the herb yielding seed, and the fruit tree yielding fruit after his kind, whose seed is in itself, upon the earth: and it was so.
12 And the earth brought forth grass, and herb yielding seed after his kind, and the tree yielding fruit, whose seed was in itself, after his kind: and God saw that it was good.
13 And the evening and the morning were the third day.
14 And God said, Let there be lights in the firmament of the heaven to divide the day from the night; and let them be for signs, and for seasons, and for days, and years:
15 And let them be for lights in the firmament of the heaven to give light upon the earth: and it was so.
16 And God made two great lights; the greater light to rule the day, and the lesser light to rule the night: he made the stars also.
17 And God set them in the firmament of the heaven to give light upon the earth,
18 And to rule over the day and over the night, and to divide the light from the darkness: and God saw that it was good.
19 And the evening and the morning were the fourth day.
20 And God said, Let the waters bring forth abundantly the moving creature that hath life, and fowl that may fly above the earth in the open firmament of heaven.
21 And God created great whales, and every living creature that moveth, which the waters brought forth abundantly, after their kind, and every winged fowl after his kind: and God saw that it was good.
22 And God blessed them, saying, Be fruitful, and multiply, and fill the waters in the seas, and let fowl multiply in the earth.
23 And the evening and the morning were the fifth day.
24 And God said, Let the earth bring forth the living creature after his kind, cattle, and creeping thing, and beast of the earth after his kind: and it was so.
25 And God made the beast of the earth after his kind, and cattle after their kind, and every thing that creepeth upon the earth after his kind: and God saw that it was good.
26 And God said, Let us make man in our image, after our likeness: and let them have dominion over the fish of the sea, and over the fowl of the air, and over the cattle, and over all the earth, and over every creeping thing that creepeth upon the earth.
27 So God created man in his own image, in the image of God created he him; male and female created he them.
28 And God blessed them, and God said unto them, Be fruitful, and multiply, and replenish the earth, and subdue it: and have dominion over the fish of the sea, and over the fowl of the air, and over every living thing that moveth upon the earth.
29 And God said, Behold, I have given you every herb bearing seed, which is upon the face of all the earth, and every tree, in the which is the fruit of a tree yielding seed; to you it shall be for meat.
30 And to every beast of the earth, and to every fowl of the air, and to every thing that creepeth upon the earth, wherein there is life, I have given every green herb for meat: and it was so.
31 And God saw every thing that he had made, and, behold, it was very good. And the evening and the morning were the sixth day.


United States Declaration of Independence
In CONGRESS, July 4, 1776.
A DECLARATION
By the REPRESENTATIVES of the
UNITED STATES OF AMERICA,
In GENERAL CONGRESS assembled.
WHEN in the course of human Events, it becomes necessary for one People to dissolve the Political Bands which have connected them with another, and to assume among the Powers of the Earth, the separate and equal Station to which the Laws of Nature and of Nature’s God entitle them, a decent Respect to the Opinions of Mankind requires that they should declare the causes which impel them to the Separation.
We hold these Truths to be self-evident, that all Men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty, and the pursuit of Happiness—-That to secure these Rights, Governments are instituted among Men, deriving their just Powers from the Consent of the Governed, that whenever any Form of Government becomes destructive of these Ends, it is the Right of the People to alter or abolish it, and to institute a new Government, laying its Foundation on such Principles, and organizing its Powers in such Form, as to them shall seem most likely to effect their Safety and Happiness. Prudence, indeed, will dictate that Governments long established should not be changed for light and transient Causes; and accordingly all Experience hath shewn, that Mankind are more disposed to suffer, while Evils are sufferable, than to right themselves by abolishing the Forms to which they are accustomed. But when a long Train of Abuses and Usurpations, pursuing invariably the same Object, evinces a Design to reduce them under absolute Despotism, it is their Right, it is their Duty, to throw off such Government, and to provide new Guards for their future Security. Such has been the patient Sufferance of these Colonies; and such is now the Necessity which constrains them to alter their former Systems of Government. The History of the Present King of Great-Britain is a History of repeated Injuries and Usurpations, all having in direct Object the Establishment of an absolute Tyranny over these States. To prove this, let Facts be submitted to a candid World.
He has refused his Assent to Laws, the most wholesome and necessary for the public Good.
He has forbidden his Governors to pass Laws of immediate and pressing Importance, unless suspended in their Operation till his Assent should be obtained; and when so suspended, he has utterly neglected to attend to them.
He has refused to pass other Laws for the Accommodation of large Districts of People; unless those People would relinquish the Right of Representation in the Legislature, a Right inestimable to them, and formidable to Tyrants only.
He has called together Legislative Bodies at Places unusual, uncomfortable, and distant from the Depository of their public Records, for the sole Purpose of fatiguing them into Compliance with his Measures.
He has dissolved Representative Houses repeatedly, for opposing with manly Firmness his Invasions on the Rights of the People.
He has refused for a long Time, after such Dissolutions, to cause others to be elected; whereby the Legislative Powers, incapable of Annihilation, have returned to the People at large for their exercise; the State remaining in the mean time exposed to all the Dangers of Invasion from without, and Convulsions within.
He has endeavoured to prevent the Population of these States; for that Purpose obstructing the Laws for Naturalization of Foreigners; refusing to pass others to encourage their Migrations hither, and raising the Conditions of new Appropriations of Lands.
He has obstructed the Administration of Justice, by refusing his Assent to Laws for establishing Judiciary Powers.
He has made Judges dependent on his Will alone, for the Tenure of their Offices, and Amount and Payment of their Salaries.
He has erected a Multitude of new Offices, and sent hither Swarms of Officers to harass our People, and eat out their Substance.
He has kept among us, in Times of Peace, Standing Armies, without the consent of our Legislature.
He has affected to render the Military independent of and superior to the Civil Power.
He has combined with others to subject us to a Jurisdiction foreign to our Constitution, and unacknowledged by our Laws; giving his Assent to their Acts of pretended Legislation:
For quartering large Bodies of Armed Troops among us:
For protecting them, by a mock Trial, from Punishment for any Murders which they should commit on the Inhabitants of these States:
For cutting off our Trade with all Parts of the World:
For imposing taxes on us without our Consent:
For depriving us, in many Cases, of the Benefits of Trial by Jury:
For transporting us beyond Seas to be tried for pretended Offences:
For abolishing the free System of English Laws in a neighbouring Province, establishing therein an arbitrary Government, and enlarging its Boundaries, so as to render it at once an Example and fit Instrument for introducing the same absolute Rule in these Colonies:
For taking away our Charters, abolishing our most valuable Laws, and altering fundamentally the Forms of our Governments:
For suspending our own Legislatures, and declaring themselves invested with Powers to legislate for us in all Cases whatsoever.
He has abdicated Government here, by declaring us out of his Protection and waging War against us.
He has plundered our Seas, ravaged our Coasts, burnt our Towns, and destroyed the Lives of our People.
He is, at this Time, transporting large Armies of foreign Mercenaries to compleat the Works of Death, Desolation, and Tyranny, already begun with circumstances of Cruelty and Perfidy, scarcely paralleled in the most barbarous Ages, and totally unworthy the Head of a civilized Nation.
He has constrained our fellow Citizens taken Captive on the high Seas to bear Arms against their Country, to become the Executioners of their Friends and Brethren, or to fall themselves by their Hands.
He has excited domestic Insurrections among us, and has endeavoured to bring on the Inhabitants of our Frontiers, the merciless Indian Savages, whose known Rule of Warfare, is an undistinguished Destruction, of all Ages, Sexes and Conditions.
In every stage of these Oppressions we have Petitioned for Redress in the most humble Terms: Our repeated Petitions have been answered only by repeated Injury. A Prince, whose Character is thus marked by every act which may define a Tyrant, is unfit to be the Ruler of a free People.
Nor have we been wanting in Attentions to our British Brethren. We have warned them from Time to Time of Attempts by their Legislature to extend an unwarrantable Jurisdiction over us. We have reminded them of the Circumstances of our Emigration and Settlement here. We have appealed to their native Justice and Magnanimity, and we have conjured them by the Ties of our common Kindred to disavow these Usurpations, which, would inevitably interrupt our Connections and Correspondence. They too have been deaf to the Voice of Justice and of Consanguinity. We must, therefore, acquiesce in the Necessity, which denounces our Separation, and hold them, as we hold the rest of Mankind, Enemies in War, in Peace, Friends.
We, therefore, the Representatives of the UNITED STATES OF AMERICA, in General Congress, Assembled, appealing to the Supreme Judge of the World for the Rectitude of our Intentions, do, in the Name, and by the Authority of the good People of these Colonies, solemnly Publish and Declare, That these United Colonies are, and of Right ought to be, Free and Independent States; that they are absolved from all Allegiance to the British Crown, and that all political Connection between them and the State of Great-Britain, is and ought to be totally dissolved; and that as Free and Independent States, they have full Power to levy War, conclude Peace, contract Alliances, establish Commerce, and to do all other Acts and Things which Independent States may of right do. And for the support of this Declaration, with a firm Reliance on the Protection of the divine Providence, we mutually pledge to each other our Lives, our Fortunes, and our sacred Honor.
Signed by Order and in Behalf of the Congress,
JOHN HANCOCK, President.
Attest.
CHARLES THOMSON, Secretary.
Philadephia: Printed by John Dunlap.


读起来都很有感觉的样子,比如说是从逻辑上坚信着什么的并将之表达出来,而成为构成人身体之外的若干要素,并使得以判断力去支持人去做出些什么。

2010年8月22日星期日

ORM & Serialization in Python

这篇依旧是导读性质的一篇,在看Python自带文档缺少头绪的时候可以参考,不然就是在被误导了哦。
而且其实呢,闲扯才是这篇的真正目的呢,虽然说貌似没什么想说的,唉还是在胡言乱语来着的。

之所以要****,是为了存储或传输。

Qt中使用的方式是通过流来实现的,提供x,x方法,并通过重载<<以及>>运算符来实现接口。

虽然标题什么的是已经确定了话题的方向,不过下面的内容只是在随意地跑跑而已。


* import

直接导入py文件或pyc文件,适合存放开发中的自定义数据。


* pickle

用于Python对象和数据流见的转化,用于数据持久化
最简单的示例是:
pickle.dump(data, open('save.p', 'wb'))
reader = pickle.load(open('save.p', 'rb'))
data是需要保存的类型,通常是个字典
可以往一个file对象dump/load多次
pickle的dumps和loads则是用字符串代替文件
对于自己定义类型的实例,可以提供__getstate__/__dict__以及__setstate__
其保存的数据格式会有不同版本,不过文件是保持向后兼容的
似乎pickle是Py的类似模块中最通用的选择,即处于比较暴露的接口
关联的模块marshal和shelve有不同的用法。

试验代码:
#+BEGIN_SRC python
import pickle
class MyObj1:
     def __init__(self,value):
        self.value=value
class MyObj2:
     def __init__(self,value):
        self.value=value
     def __getstate__(self):
        return self.__dict__.copy()
     def __setstate__(self,dict):
        self.__dict__.update(dict)
m1 = MyObj(6)
m2 = MyObj2(7)
data1 = {'str':"string",0:"zero",1:m1,2:m2}
save = pickle.dumps(data1)
data2 = pickle.loads(save)
print data1[2].value == data2[2].value
#+END_SRC


* json

Python中实现json编解码的模块提供了类似pickle的接口,提供load和dump方法。
具体的编解码操作由JSONEncoder/JSONDecoder提供,可以通过派生以cls参数使用或者添加hook的方式来添加对非内置对象实例的操作方法。

试验代码:
#+BEGIN_SRC python
import json
data1 = {0:1,1:2,2:3}
print json.dumps(data1,sort_keys=True)
def myhook(dct):
     return {"vector":"|".join(map(str,dct["vector"]))}
data2 = json.loads("""
{"vector":[1,2]}
""",object_hook=myhook)
print data2

class MyEncoder(json.JSONEncoder):
     def default(self, obj):
        if isinstance(obj, complex):
         return [obj.real, obj.imag]
        return json.JSONEncoder.default(self, obj)
print MyEncoder().encode(1+4j)
#+END_SRC


* XML

Python中和XML相关的实现且在文档中提供实例的有几个:
HTMLParser通过实现handle方法
xmlrpclib/SimpleXMLRPCServer这个用起来和XML感觉不大
xml.dom.minidom实现了简单的DOM接口
或者用SAX的接口的实现更好的解析性能
关于DOM与SAX的规范的内容并不在Py的范畴内
xml.parsers.expat亦有足够悠久的历史
xml.etree.ElementTree则是Py2.5中新增的
各自用法还算有明显的区别的,算是明显吧


* sqlite3

用sqlite3.connect创建Connection对象,可以文件可以":memory:",提供cursor方法获得Cursor对象,然后就找到我们的execute方法了。
可用“?”占位传入值,执行“select”后可对Cursor对象迭代,或者用Cursor对象的若干fetch方法来获取Row的数据。
事务方面的操作在Connection对象上。
可自定义converter来实现sqlite数据和Python对象的转化。

试验代码:
#+BEGIN_SRC python
import sqlite3
conn = sqlite3.connect(":memory:")
c = conn.cursor()
c.execute("create table name(key,value)")
c.execute('insert into name values (?,?)',("hello","world"))
conn.commit()
c.execute('select * from name order by key')
for row in c:
     print row
c.close()
#+END_SRC


* ConfigParser

用来读写ini格式的文件。
虽然是源于Windows的格式,Linux下还是有程序在用的。


* csv

用于读写CSV格式的文件,通过对文件建立reader和writer对象。


* codecs

用于读写UTF-8编码的文件,提供了open的同名函数


* StringIO

实现内存中的文件对象,接口按照对文件的方式使用,提供getvalue方法获取值。


* ZODB

Zope Object Database,就是Zope对象数据库的意思,不过可以作为独立的模块使用。
和Django开发中支持的MongoDB类似的,都属于NoSql的对象数据库,不过ZODB对Py有Native的感觉。
安装“easy_install ZODB3”,导入“import ZODB”,然后便可使用。
使用的接口类似于shelve模块(都默认仅在__setitem__之类的操作时探测到更新),
也就是说是依然dict对象提供的接口的方式使用一个root对象。
因为这里不去涉及ZOPE,所以也仅写一段just work的代码来试一试。

试验代码:
#+BEGIN_SRC python
from ZODB.FileStorage import FileStorage
from ZODB.DB import DB
from persistent import Persistent
class One(Persistent):
     def __init__(self):
        self.todo=[]
     def add(self,task):
        self.todo.append(task)
        self._p_changed = 1
storage = FileStorage('Data.fs')
db = DB(storage)
connection = db.open()
root = connection.root()
root['main']=["hello","world"]
import transaction
transaction.commit()
print root.items()
connection.close()
db.close()
#+END_SRC


* Django-norel

是一个让Django在NoSql数据库上模拟出Django原本的数据Model的查询的Django分支,在Google App Engine上写应用时在用。



* SQLAlchemy

对象关系映射,也就是通常缩写为ORM(Object-relational mapping)的东西。
使用了ORM可以通过OO的方式方便使用数据库,并可方便的切换所使用的数据库后端。
SQLAlchemy虽然才在2006年发布了他的第一个版本,但是很快成为Python社区使用最广泛的ORM模块。
和Django的ORM模块作用是一样的,只不过Django的Model类一般只在Django应用里使用,而且不及SQLAlchemy复杂(至少不是只倾向暴露给用户一个最高层的接口)。
TurboGears的话,是目前尝试使用SQLAlchemy很好的实例,以及应用的价值所在。
如果利用好缓存的话,ORM的性能表现并没有对应用太大的影响。

安装“easy_install SQLAlchemy”,导入“import sqlalchemy”,
然后“sqlalchemy.__version__ ”便可以查看版本。

先来整理一下概念上的东西,并假定已有了简单的SQL和ORM的基础:
对象Engine,表示一个数据库,用create_engine创建。
对象MetaData表示对数据的操作,提供方法create_all/drop_all在Engine上创建或删除表
对象Table用以表示数据表,构造器可用于构造表,可在表实例上构建查询并以execute方法执行查询
对象Column表示数据表的列,列的类型定义在sqlalchemy.types。
declarative_base的实例Base是以申明的方式来创建对象,相比用mapper关联表和对象要直观,两者等价所以通常就以派生Base的方式使用了。有属性__table__表示关联的Table对象和属性metadata表示拥有的MetaData对象。
类型Session表示对数据库操作的会话,通过sessionmaker(bind=engine)创建,然后用来创建session实例。实例session有方法add/add_all和delete用于添加删除实例,方法query用以创建Query对象,方法commit和方法rollback用于事务的提交与回滚,方法from_statement直接执行SQL语句。
对象Query用于构建查询,在session实例上以所查询对象为参数创建,提供方法filter,order_by以及序列操作进行查询,提供方法all,one,firs以及count获得查询结果,方法join可以实现表的join查询,简单的查询操作便都在此进行。
aliased用于解决多表查询时的命名冲突问题。
外键列ForeignKey用来指向另一个表的主键,用以建立表间关系。数据实例的API通过relationship函数给模型添加属性。

当前手头没有打算写实际的例子,所以依然是试验目的的代码,目的仅是演示一下代码的流程:
#+BEGIN_SRC python
print "create_engine"
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:', echo=True)

print "declare_schema"
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
Base = declarative_base()
class Entry(Base):
     __tablename__ = 'entry'
     id = Column(Integer, primary_key=True)
     name = Column(String(50))
     category_id = Column(Integer, ForeignKey('category.id'))
     category = relationship("Category", backref="entries")
     def __init__(self,name,category):
        self.name = name
        self.category = category
     def __repr__(self):
        return "<Entry %s>"%self.id
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
class Category(Base):
     __tablename__ = 'category'
     id = Column(Integer, primary_key=True)
     name = Column(String)
     #entry_id = Column(Integer, ForeignKey('entry.id'))
     def __init__(self,name):
         self.name = name
     def __repr__(self):
         return "<Category %s>"%self.id
Base.metadata.create_all(engine)

print "use_db"
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
c = Category("hello_world")
session.add(c)
session.add(Category("hello_earth"))
session.commit()
for i in range(1,10):
     item = Entry(str(i),c)
     session.add(item)
session.commit()
for i in session.query(Entry).filter(Entry.id==5).all():
     print i
     print i.category
print session.query(Category).count()

print "END"
#+END_SRC


* Elixir

Elixir构建在SQLAlchemy之上,提供直观易用的声明式接口。
在使用方式上Elixir类似于SQLAlchemy的declarative扩展,而且在API上有所简化以方便使用,也隐藏了一些映射的实现细节。

安装“easy_install Elixir”,导入“from elixir import *”,之后便可使用。
值得注意的是对于Elixir未包装的功能,SQLAlchemy的模块依然是需要且肯定会使用到的,特别是查询方面的Query对象。

然后直接写一段试验用的代码了:
#+BEGIN_SRC python
from elixir import *

metadata.bind = "sqlite:///:memory:"
metadata.bind.echo = True

class Post(Entity):
     using_options(tablename='post')
     title = Field(Unicode(30))
     content = Field(UnicodeText)
     comments = OneToMany('Comment',order_by='-id')
     def __repr__(self):
        return "<Post %s>"%self.title
class Comment(Entity):
     content = Field(UnicodeText)
     post = ManyToOne('Post')
     def __repr__(self):
        return "<Comment %s>"%(self.content[:5])

#from model import *
setup_all()
create_all()

p = Post(title=u"demo",content=u"This is a test post.")
for i in range(1,10):
     p.comments.append(Comment(content=u"#%s comment"%i))
session.commit()

for i in Post.query.all():
     print i,i.comments
p.delete()
print Post.query.count()

cleanup_all(True)
#+END_SRC


* Camelot

一个基于PyQt和Elixir依照Django的Model和Admin接口用于开发类似access桌面MIS的RAD框架。
内含一个类似[[http://code.google.com/p/formlayout/]]的form窗口生成工具。

这个就纯观赏用。


* Python

语言的解析树本身就是一个静态的数据,不过不会去直接使用它:
#+BEGIN_SRC python
import parser
st = parser.expr('a + 5')
print parser.st2tuple(st)
a = 5
code = st.compile()#same as 'parser.compilest(st)'
import pprint
pprint.pprint(eval(code))
#+END_SRC
较通常的做法呢是实现对象的__getattribute__/__getattr__方法,以及hasattr/getattr这样的方法,或者类似__class__/__dict__获得中对象所关心的属性。
不过通常也只是这样来获取数据,而没有把数据写成py文件的习惯(不然就等同于使用了罪恶的eval函数了)。


* 闲扯

因为目的是对想法的提及,所以依然还是想在行文逻辑上有连续的感觉。
(上面这句就有问题来着...)
在Django中,模板中数据的来源有两个方面,一是由视图传入的字典提供哦,一是在模板渲染时调用所编写的标签或过滤器返回的结果。对于单页面的网页在浏览器中缓存的是最终显示的结构,在访问新的数据时,仅在样式和脚本上有所缓存。
如果需要把未渲染的模板提前保存在本地的话,就需要在渲染时执行对服务器的全程调用,这就是当前Ajax技术所实现的事情。服务器返回的不再是完整的页面,而是html/text片段,或xml/json数据,或js可执行代码。
将本地的页面更多的作为显示的容器,还是将远程的调用仅涉及数据相关的API,是把功能实现在客户端还是服务器的两种倾向。
对于PyQt这样Api较封闭且脚本类的东西来说,利用漂亮的QWidget及其派生类树形结构。可以在本地程序中下载全程的代码,然后无缝地作为本地程序来执行。
Firefox的xul技术就是这样一种程序环境,程序以扩展的性质安装到本地,通过Market来分发,通过签名来保障安全。而且界面和Qt一样是本地的,而不仅作为嵌入而渲染。如今Eclipse,Notepad++都有各自的扩展Market,而IM之类的软件也可做这样的平台。
现在觉得Chrome的Market更好用一点,应为扩展更接近单纯的网页。虽然功能有些限制,不过填删与开发似乎觉得更方便一些。
不过FX的扩展有沙箱like的权限机制,相比在PyQt中去执行远端的代码就是一件危险的事情了(QML的事属于下面一行)。
说到浏览器,HTML5可用来构建富客户端了(代码执行环境放在本机,而数据的储存放在服务器上,代码可以根据需要动态的下载),和即使Debian的软件包可以做到高速的分发以及虚拟机一样快速且底副作用的部署的话,依然是可以感受的其中的区别的。
不过,区别只是出于观察的视角,在给予用户的体验上,之间又有相关联的地方。


闲扯居然也能坑这么久...也没质量什么的...


Sep 27 2010

2010年8月21日星期六

遥远的记忆之海

来试一试麻枝大魔王风格的

----

这里是死后的世界,而年轻的我是记忆之海上的渡船人。
每天都会有亡灵抵达这里,来在这片汹涌的波涛下探寻往日的回忆。
我将用这片小舟载着他们,驶向掩埋于他们意识的幻境之中。

不知道自己是如何开始这项职业的,也不知道是否在遥远的未来会有某个终结点。
大概我便会这么一直以这样让自己感到幸福的方式生活下去吧——每天都可以期待不同的故事。
而自己就做好一个船夫好了,不必因好奇心让记忆为自己增加多余的烦恼。

“坐稳呦~要出发了~”我向船头上来的一位老奶奶说道。
然后熟练而悠闲地推动起身旁的桨,让船缓缓却高速地飞向某个预定的水域。
海水在炫目却冰凉的天空下闪着光芒,一眼望去只能看见浅浅的海面。

不知不觉中,我已经让船平稳地停下而随意地顺着波浪起伏,然后向着船的那一头:
“你的记忆便保存在这里,在这片浑浊的水面之下。不过还有若干事项要知道——
在波浪起来的时候,要努力地往海底望,凭自己的坚定的意志去努力地望。
让自己的身躯仿佛为回忆包围、洗涤、沉浸,直到某一刻海水骤然变得清晰。
这样你便能看见深深的海底,看见那片保存着你的记忆的地方。
那是只属于你的地方,只有凭自己的力量才能看见,而别人的帮助对此无能为力。”

我程序化地说完后,便顾着自己的悠闲在船头躺下了。
看着老者在船的另一头有些吃力地站起身子,然后视线眺望着周围这片广阔的水域。
无边的海面上的这幅小舟飘荡的景象就这样持续的好久,仿佛静止了一般。

老人只是静静的望向远方,一直没有把头低向海面。我出于本职提醒道:
“天黑之前就要返航了,不抓紧时间就无法看到你想寻找的东西了。”
老人似乎没理会我的话,我也无趣再说,看着老人依旧静静地站在船头。

老人最终还是放弃了,没有再去努力尝试,转过身来在船头坐下。
“就真的这样放弃了吗,这可是一个难得的机会。”老人的行为多少让我不解。
“恩”老人轻轻地哼了声,然后坦然地点了点头,颤动着的嘴唇似乎有话要说。

于是我也安下心来,在这水天包裹的空间里躺在船的一角,听着老人传来的言语:
“随着人生漫长的时光,我已经是一个年迈的人了。记忆在一天天流逝,身体也一天天薄弱。
变得不再有力气去触摸身边的世界,对周围事物的意识也越渐模糊。
活着,自己却越来越难以感受到自己是存在着。于是生命的最终一刻,便在完全不知觉中抵达了。

“过完了一生,纵然有坎坷或遗憾,可终究还是幸福的一生,可以让我安心于死后了。
可是消失的记忆依旧是留下的残缺,对于自己最后的时光,竟然不能在心里珍藏着这份幸福。
所以我便来到了这片记忆之海,想弥补这份残缺,想为自己寻找一份安慰。
不过果然我还是有这份幸福感便足够了,这种感受纵然被遗忘,也毕竟曾经存在过。”

我打量着对面的老者,觉得她是在害怕由记忆去触及内心的某些,不禁叹气感到遗憾。
于是等老人平静下略带激动的言语后,我起身撑起船驶上回程之路。
伴着海平面上柔和而巨大的夕阳,身后留下了那片曾经抵达的水域,
也同时告别了那里那些或许将永远被掩埋于海底的对往日的记忆与情感。

这便是一个普通的工作日,随着一天天的经历,我不久也就会把这天淡忘。
因为这毕竟只是我不断往复着的类似的日子,一个没有源头也没有终点的记忆所背负的时光。
接下来一天的客户是一位中年男子,身体健壮没有疾病的样子。
他是意外而死去的吧,我简单地这样判断着。撑起船,又一起踏上一个新的旅途。

船平稳地向预定的某地飞去,今天的海面的波浪依旧缓和,我在船头悠闲地把着方向。
而他在船的另一头显出好奇,往四周那片行船偶然路过的水天之痕,不断地张望。
“这附近不知是埋藏着谁的记忆,在无尽的记忆之海上,恐怕连我也可能不再会抵达这里。”

我静静地坐在船的这一头,以船夫的身份这样说着,然后依旧向着遥远的目光不能触及的地方。
“好了,我是有点兴奋,毕竟是第一次来到记忆之海。如果这样做对航船有影响的话,我会注意的。”
我眼角的余光中能够感受到他的视线,那似乎是一种期待在寻求着言语上的联络。

“我想知道,你为什么要到这里探寻自己的记忆呢?你还没有到一个健忘的年纪。”
“我来寻找记忆,并不是因为它们被我遗忘,而是因为它们已经离我而远去。
我是想寻找一丝安慰——即使将来记忆变得淡去,它们也曾经凭借着自我的存在而存在过。

“这也是那个生者的世界所留给我的痕迹,告诉自己往日的努力的意义所在。
纵然不能在物质上于现在我的建立起沟通,但是分明就是我在这里可以依旧延续下的一种财富。
这也就是我来到记忆之海缘由,想在这里挖掘到的心灵的一份宝藏。”

船速渐渐变缓,即将到达预定的海域,我向他做着说明引着他往船外沿的地方走去。
然后留下他一人等待海风的气息掀起翻滚的浪花,祝福他的思绪将能够触及到脚下海水的彼端。
风与浪下船头的他在海天间映出一个厚实的身影,仿佛是一扇凭空为世界展开的门。

他的声音在一层层的展开着他的过去:“这是我的出生,这是我的父母,这是我的学校,
我的成年,我的工作,我的老婆,我的女儿,我的事业,我的生活……”
海水的蔚蓝色里翻滚着幸福的味道,一丝腥味,又一丝甘甜。

“美好的期待一直在延续着,直到有一天,那天的一场意外,让我只能告别那一切。
回忆是以痛苦终结的,我想把这最后的时刻忘掉,可是又害怕失去了想守护的东西。
我便这样犹豫着在这个死后的世界,后来想法开始有些坦然——

“记忆的存在并不是因为它们被记住,而是它们被经历过。
记忆的痕迹即使随着时光淡去而似乎不曾有过,它们也不会消失依然存在。
不经意间看到自己身体上留下的微小残缺,知道这便是一种以不可弥补的痕迹来保存着记忆。”

“我想我脑海中的记忆也是一样的,是因为我曾今为着他们的存在而做出过什么。
这便足够了,它们就是我在记忆之海下埋藏的一份财富,将永远为海天守候。”
直到回程的航线上,他也一直在向我分享着他的那份满足。

“记忆里的东西无法改变,也不需要改变了,在现在这个世界里我也依然能为自己创造记忆。”
他似乎不在意我这样漫不经心地听着,能否记住他说出的话。
看着周围海面似乎永恒不变的夕阳景色,我想——
他把现在的感受说出来,这样便是让这种感受以与人共同的记忆,让自己能够保留下去。

这也便又是我不知过去与未来的时光里的一天,在永恒的时间轴上的一个渺小痕迹。
接下来一天的客人是位年轻的学生,年龄似乎与我接近,我依然职业地将他引上船。
今天的天空似乎有些异样,不过不会影响到我们的航行,于是又将开始一个新的旅程。

我控着方向,让船往预定的地点飞去,在有些阴沉的天空下划过淡淡的线条。
他和来到记忆之海的大多数人一样,在船的那头打量周围,以未知抚慰着未知。
回忆的味道就是这样的吧,我也顺着他的视线向远方望去,心中划过这样的想法。

如今的船不需要人过多的操作,加之今天格外低的天空,航行显得有些沉闷。
不过如果天空晴朗,这就是个同往日一样的航行了,充满生活的平淡的温馨。
我沉浸在自己的思绪中,忽然感到船身一个摇晃,我看见船对面的那个身影站起身子。

然后他向我走来,我赶忙先稳固下船的航行,听见他说话的声音:
“您比我大吧,我可以叫你哥哥吗?”“我已经不在意自己的年龄了。”
“那我就叫你哥哥吧!”“这随便你。”“嗯,哥哥好!”“……”

他在我一旁坐下,没有把刚才的热情继续下去,视线转向远方,有些顾虑的样子。
“来说你为什么要来到这里,是想寻找到怎样的记忆吧。”我以平静的语气想挑起对话。
“不知道,”他把头向着我,带着年轻人的活力,“其实我确实不知道,我没有想寻找的东西。”

“这里有记忆的宝藏,会有让人感到幸福的东西,不要为让自己可以努力的机会而犹豫哦。”
“可是那些记忆是我主动抛开的,它们让我感到难受,我却又为一种缺失感来到这里。”
“好了,既然来到这里了,即使意外,也会找到些东西吧。
它们都曾今属于你,今后也依然是。想着抛弃,就可能会让自己失去更多的东西。”

他看着我的眼睛,像在寻找一种希望。而航行也在这不知觉中驶近了目的地。
“去吧,相信自己心中小小的愿望,在海浪之间寻找能够寻找到的东西。”
“恩,谢谢哥哥。”他答应了,然后走向船的那头。站立在船的边沿上,在阴沉的海天间显得波澜壮阔。

我在船的这头放松下身子,视线漫无目的地望向天空,意识到居然第一次对客人说了自己的想法。
我这是说给谁听的呢。一个陌生人?一个在这死后世界里再也不会相遇的人?还是说……我自己?
我在怀疑着一件事情的意义,是否就代表着自己会做出一些多余的事情呢。

此刻我感到我的思绪与情感被他联系着,从未有过的被一个人所联系着。
想着能为别人的困难添上自己的帮助,同时也敞开心扉地能够去接受别人的帮助。
此刻他转过头来看着我,脸上露出一个微笑,我看着也在自己脸上也露出一个微笑。
这个微笑,一个是第一次在这死后世界里自己希望永远记住的东西。

忽然,一个浪打在了船身上,他侧转的身子没站稳,向记忆之海里跌落下去。
瞬间海风也变得巨大了,阴沉的天空似乎要发展往一个糟糕的方向。
此刻顾不上太多,我得救他。跑向他所在的船头,往波浪翻滚的海水里跳了下去。

我在下沉,身边被黑暗包围着。那是一片无尽的黑暗,我感到瞬间失去了重力,努力划着水却寻找不到方向。
周围也异样的安静,这是一个海面下无声的世界,时间的流淌似乎漫长,甚至凝结。
渐渐地我的心也平静下来,我能感受到自己的呼吸,呼吸在这个海底并无异样。
恐惧之后这我才意识到,这是在一个死后的世界,一个已经不会再死去而离开的世界,
一个欣慰却又孤独的世界,一个没有失去也没有保留的世界,一个让记忆永恒封存的世界。

在这个黑暗无声的世界里,我顺着水流漂浮着,让意识随着身体漂浮着。
在记忆的海洋中以心灵去触摸着曾今,以及那些孕育着现在和未来的过去时刻。
我能体会到在深海之下的幽幽的光芒,像一个指引者在前方对我轻轻的召唤。

我凭借着意志向光芒游去,让光点在眼前接近在身边扩散,觉得这样才能够拯救到什么。
这样能够实现自己对他人的一个守候,一个哪怕在这死后世界已经显得多余的举动。
一种哪怕只是出于内心原始的善良的淳朴的冲动,也要去带着落海的少年顺利的回来。

光芒弥散于海底,如同思绪飘荡在无尽的水中接受着温柔而永恒的抚慰,包裹在我的周围。
渐渐地,光点构成的屏障上清晰出一个影像,在向我讲述着什么。
在向我呈现着一个“生”所在的世界,一个在海底无尽的黑暗里“心”所体会的“光”所组成的世界。

“我站在一个空旷的操场上。”少年的声音在耳边的告诉我,然后飘去了。
我想这是少年的记忆传递给我的信息吧。在他的视角上,我可以以第一人称在看到他的双手。
在这个记忆构建的世界里,体会着他的过去。可是,我却感觉不到他的意识,以及他的存在。

身体开始活动起来,由机械变得自然地走动着,向着教学楼那里去寻找人的踪影。
我的意识仅仅是随着身体漂浮着,在这个世界里不能左右什么,也不会有想去左右什么的愿望。
在阳光下曝露的身体,接触着这里的空气与水分,仿佛在由陌生不断变得亲近着。

与空旷的操场相比,教学楼因为人的踪迹,而显出格外不同的氛围。
身体继续往教室里走去,人的气息的存在让它移动着的脚步学会了自信。
同时身体也是在渴望着阳光之外的什么,让自己来到这人群中去寻找着。

身体在经历着成长,可是这样似乎仍缺少着什么,它所见到的人群都在它面前散开了。
当身体带着疑惑去走进下一个教室,带着或许会有改观的期望,依旧得到的是一个失望的结果。
“果然人们是讨厌我的,这个世界里已经不需要我的存在了。”

此刻身体忽然开始传递出它的意识,让我清晰的感受到意识的存在,而随即痕迹又消失得更加完全。
之后,身体便失去了他本身的驱动,光芒远离而去了。交付于我,而我只是依然麻木地站在教室中央。
刚才跑去教室外的人群开始渐渐返回,他们仿佛无视了我的存在,又回到了他们的日常。

我就这样站在教室里,近距离的带着身体逝去的愿望。去接触人群的气息,去沉浸于海洋的梦境之中。
可是,梦境最终将迎来黎明。“刚才真是一个讨厌的人啊,就这么像无视我们的跑近教室来。”
是“讨厌”吗?一句声音之后,周围的声音也纷纷附和起来,成为这个狭小的教室里共通的话题。

我能感受到一种意志,似乎是身体在传递给我的。
让我出于内心的害怕,从这个空间里,从这个人群中向外跑去,跑向一个与此隔绝的世界中。
我在遥远的地方大口的喘着气。驻足后望,那个教室,那片学校已经变成渺小的身影。

这已经足够远了,这已经是喊声不能再被传递到的距离了。我的心开始变得空荡荡的。
这是一种安心吗?我看着自己缓慢起落的脚步,在坚硬的水泥地面没有留下任何的痕迹。
我再去望那片远去的世界,一个希望着阻隔开的,一个永远只保留着不起眼的存在的世界。

我漂浮在这个梦境的世界,和一个已经属于我的身体,因为躲避着什么而肆意游荡着。
只到有一天,也是偶然而特别的一天,我似乎是感到厌倦。
也似乎是出于好奇,决定再去一次那所学校,再次重拾起曾今想寻找的什么。

校门在眼前清晰了,操场在眼前清晰了,教学楼在眼前清晰了,曾今去过的那所教室在眼前清晰了。
自己的心愿,去寻找一个未知到重要的东西的心愿也这路途上变得清晰了。
这或许就是在凭借着海的梦境的经历,来记忆里所未填的空缺吧。

在那间教室的前,我敲了敲门走了进去,心里已经做好了接受各种意外的准备。
而且不仅仅是躲闪着视线,而是在寻找他们的帮助,寻找着一种重拾遗失的启示。
我站在教室门前,努力地睁开眼,去接受着人群的目光。

身体在人群中肆意的走动着,人们并没有做出异样的反应。
身体想在人群中挑起话题,人们的意志中已经淡忘了他的痕迹了。
这我才发现,身体做出决定已经无法改变,关于那个希望消失于此的决定。
它已经不再属于这个世界了,一个它曾想抛开却一直未割舍的世界。

那我又将飘向何方呢,向着一个新的永恒的世界吗?
在意识在这个身体已经在远离我而去的时候,我又似乎不愿回到那片记忆之海上。
因为在我海的梦境中,在这死后的世界里,我是第一次意识到了我所希望寻找的东西。

我的意识回到的我的身体上,我闭着眼漂浮在黑暗的无尽的记忆之海的深处。
随着水流飘向我一个不知为何方的地方,一个永远将背负着记忆的地方。
在漫长的飘荡中,终于,我决定,睁开我的眼睛,去看看周围的世界。
去看看身边的世界,哪怕看到的依旧只是黑暗,去依旧能在世界里发现着什么。

这是一片天空柔和的色彩,西斜的阳光透过玻璃照射在我面前的床铺上。
双手是棉被的触觉,鼻尖是医药水的味道,身边有机器在运转的声响。
这是一个空荡荡的病房,我孤单的一个人躺在中央。身体不能动,只能望着粉白的屋顶。

我感到内心的一阵寂寞与痛苦,因为孤单而在对外界恐惧而害怕着。
害怕着我的记忆我的所知我的经历,会成为自己的一种负担,会让自己面对着未知却只会守护着丝丝迷茫。
我闭上眼,想象自己正被水包围着,想象就在海底,在那片无尽的黑暗中。
希望再次抵达那个世界每天航行在记忆之海上,一直地去陪同着别人寻找着别人在寻找的东西。

在这样的孤单中,我的意识又变得模糊开始轻浮,再向一个遥远的地方飘去。
直到忽然间清脆的敲门声让我回过神来,可是我仍害怕着不敢睁开我的眼睛。
我听出来是同班的同学来了,他们的话语声在翻滚着我的记忆,清晰着我的心跳。

那片无尽的大海中也有属于自己的记忆吗?他们一直埋藏在那里而我却没有在意。
可是现在我的有份小小的心愿,想去保留那份记忆。
那是一份需要继续延续的记忆,还尚未到一个被海水所掩埋的时刻。
让它独自在无尽深海之下,忍受着永恒的没有终结的孤单。

此刻,对于刚才的心头那份抛弃“生”的世界的想法,自己觉得可笑。
活着的自己,才是对那个死后世界无尽时光最好的终结。
况且人们一直在为着“生”而努力呢,让我能够再次的活过来。
来让我能够有机会去真正地体会到自己内心的希望,同时也是他人给予的希望。

“真希望你能够早点醒过来哦,和我们一起去看班级篮球赛。”熟悉的声音在面前传递过来。
我的内心出于一种固执,依然让自己的双眼紧紧闭着,用拒绝去回避心头残余的害怕。
“今天我们先走了,明天放学的时候还会来看你的。”然后是一个轻轻的关门声。

病房里恢复了先前的安静,又只剩下我孤单的一个人在这狭小的房间里。
此刻安静得又如同深海的气息,我心头的另一种害怕又让自己猛地设法睁开自己的眼睛。
可是我发现自己的眼睛没能睁开,只是忽然有泪水的热度在脸颊上流淌开来。

——“只是一个旅途,这几天里我去了一个遥远的地方,现在我已经回来了……”

而旅途,却依然将在一个遥远的地方延续下去。

100821-29

2010年8月8日星期日

基于Django的Web应用设计

先呼吸一口气放松一下,把Django的文档在生成一份离线版本,然后去官方的wiki页逛逛:
http://code.djangoproject.com/wiki/DjangoResources。
本来打算是严肃的继续说明Django的功能的,不过现在又觉得来闲扯一会儿会更好。

之所以这么觉得,原因可以参看这篇《偶对django的rant》
http://www.javaeye.com/topic/361310
这篇文字表达对Django繁琐的url调度,晕人的import,别致的template等方面的不满。

其中的观点可以说是有理有据,确实如实反映了Django的设计,
所以才会流传一种类似于“Django只适合写Django风格的站点”的话,upadate
也有人因此就尝试通过替换路由和模板的方式来使用Django的功能。

个人对Django是刚有涉及的地步,并抱着一直的能黑则黑的态度,来表达不悦。
不过既然来使用Django了,也就有必要来体验一下Django方式的应用设计了。
下文便将从这个角度来尽量不跑题地,来扯一点点轻松的话题。

上面便是无趣的开场。


IDE

原本是在用Emacs写Python代码的,在Emacs里输入文本有一种洋洋洒洒行云流水畅畅快快的感觉,补全工具什么的也是安装好了随叫随到

不成问题。
不过仅以文本的方式来对待一个Django项目则显得视野不够开阔,因此就还是动用了启动更慢Eclipse。
按键选择Emacs绑定,快捷键什么的依旧好使。
安装好WebTools和PyDev,这样便可以建立Django项目了。

Eclipse的补全和重构工具总是让人用起来感觉一直不错。
补全除了用来补全对象的成员和方法,更重要的可以自动补上缺少的import(泪目啊~)。
而重构的话,对于Python代码Inline/ExtractLocalVariable可以让局部变量的存在变得清晰。

此外,Explorer支持了多级目录和py文件内部层次的展开,以及调用manage.py的菜单入口。

有好的工具让后面的事情变得舒心一些,毕竟算是好事吧。


History

Django最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的。
对这句的话的理解可以参考Guido van Rossum曾对Django发表的一段评价:
Guido:我是一个对 django 非常满意的用户,

并在项目中使用了一些 django 。我将 django 称为“第二代” python web 框架, 第一代是指 zope 和 twisted, django 是由两个

在堪萨斯新闻报社工作的小伙开发的,并非是一个很有名气的地方。
Chris:很奇怪,Zope, plone 也是来自报纸网站的
Leo :他们要流程化他们的工作流,这对他们可是很重要的事情。
Guido:也许是这个原因吧,堪萨斯的这家报社希望建立一个给当地人提供信息的本地网站,该网站必须对读者的响应非常及时,必须很

快地发布内容,并不是简单把文章发布到网站上这样谁都可以做的事情,它必须很容易更换整个网站的外观,添加一些新的创意,一些新

功能,增加一些新的应用。例如,发布本地体育赛事新闻,提供关于球队链接和照片等各种感兴趣的信息,他们希望这东西能很快运作。

我想他们做这个有两年了吧,这两个小伙子和一群编辑在一起工作,编辑为他们提供内容。在工作的同时,他们觉得有必要做一个框架,

他们从他们的第一个网站应用中提取了框架。 通过编辑对他们不断提出的对网站修改需求,他们对框架增加更好的灵活性,后来他们决

定说“我们开源吧”,他们的想法得到了报社的支持。
简单的说,Django的设计目的为了实现简化便捷的开发流程,通过

这样来快速地进行Web应用的设计与开发。
而它设计的原则是DRY,这代表Don't Repeat Yourself这一法则。
这意味着相比于Django已提供的功能而言,提供一个支持设计与模块重用的框架,是让Django的价值体现得更为显著的事情。


Model

在创建project和一个app之后,首先来进行的是Models部分设计。

参考一下django-simplecms,一个简单的内容管理系统的模型图:

href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifEroQXUBXJXK4jkOI44emzQ3Je8qhhdXPMHf_U-AP8eAAzku9WVNUpxXuOWdeaj_1NnHBGmlAqhyphenhyphen-jXrW1tzd27CpWRTem9NAdUwxsPG5W7v94-k61RZozYfv9Pfm-dzI89qqPfJM44g/s1600/cms.png">
src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifEroQXUBXJXK4jkOI44emzQ3Je8qhhdXPMHf_U-AP8eAAzku9WVNUpxXuOWdeaj_1NnHBGmlAqhyphenhyphen-jXrW1tzd27CpWRTem9NAdUwxsPG5W7v94-k61RZozYfv9Pfm-dzI89qqPfJM44g/s400/cms.png" alt=""

id="BLOGGER_PHOTO_ID_5503062974169040690" border="0" />


我这里只是一个helloworld般的例子"posts":
class Post(models.Model):
....title = models.CharField(max_length=255)
....author = models.ForeignKey(User)
....content = models.TextField()
....pub_date = models.DateTimeField(auto_now_add=True)
....last_update = models.DateTimeField(auto_now=True)
....def __unicode__(self):
........return self.title
....class Meta:
........ordering = ['-pub_date']
这部分代码放在models.py文件中或者建立一个名为model目录。
此外这里依照习惯,引用名使用复数小写,模型名为首字母大写的单数形式。
Django中对变量的命名采用小写字母加下划线分割的风格。

模型设置好后,便可将所创建的应用添加到INSTALLED_APPS中并执行syncdb。
如果设置好Django的admin模块的话,就已经可以正确地来编辑模型的数据了。


more about Model

这部来对Model进行一些额外的说明,阅读时可以暂时跳过。

建立模型需要而模型添加字段,用于表示数据,而Django中也提供了字段用以建立模型之间的关系:
Many-to-one关系,使用ForeignKey字段,
例如从属关系一个投票对多个选项,选项有指向投票的ForeignKey。
Many-to-many关系,使用ManyToManyField,会额外建立一张关系模型(含两个ForeignKey),
例如小组和成员之间,一个小组拥有若干成员,每个成员属于若干小组。
One-to-one关系 类似于unique=True的ForeignKey但为两个模型都添加了成员,
例如用于表示模型的派生关系,每个餐厅都对应一个地点,某些地点对应一个餐厅。

声明完模型的字段有,将可以使用Django已经提供了的属性,例如objects,save()。
也可按需要添加自己的方法供调用,或者使用修饰模式来扩展已有的方法。

Django的Authentication系统中,在django.contrib.auth.models有名为User的模型,用来表示站点的一个用户。
使用系统提供的模型的好处是可以在不同的Apps中有一个一致使用的对象,使得一个用户可以在不同的第三方组件中使用。
User的字段有username等,还有groups和user_permissions两个many-to-many字段。
为给用户增加信息,会建立AUTH_PROFILE_MODULE,用一个OneToOne指向User的Profile模型。

ContentType是Django中表示应用和模型的一个模型,并以此提供了GenericForeignKey,可以用来任意模型的某个数据。
一个实例便是Comments模块,用以给任意模型添加评论。
有时Bookmark模型也以这样的机制来实现。


URL

根据上面完成的Models部分,便可对应的进行RESTful URLs的设计。
参照http://microformats.org/wiki/rest/urls,并参照Django的自身模块的设计,来针对模型展开CRUD操作。

例如pages有一个叫Page的简单Model,那么对应的URL可以是:
/pages/ #page_list
/pages/new/ #page_create
/pages/1/ #page_detail
/pages/1/edit #page_upadate
/pages/1/del #page_delete
对有副作用的URL,GET方式为显示编辑框,POST才确认提交。
对于模型中的Relationship,如Comment有ForeignKey指向Page:
/pages/1/comments/ #page_comment_list
/pages/1/comments/new/ #page_comment_create
/pages/1/comments/1/del/ #page_comment_delete
在上面URL中对应模型的名称,使用小写的复数形式。
并以"APP_MODEL_VIEW"的形式(要避免撞车)为每则URL设定一个name以方便调用。
查询条目可以用object_id或者slug,当条目不存在时需要正确的返回404状态码。

通过模型来设计URL,看起来是件很自然称心的事情。

为了使上面的URL可以工作,在项目的urls.py中添加:
(r'^pages/', include('demo.pages.urls')),
然后在

我们的App中建立一个urls.py的副本供编辑。

这里为使用方便,可以上面名为Page的Model中添加:
@models.permalink
def get_absolute_url(self):
....return ('page_detail', [str(self.id)])
另一种更通用与广泛的做法是使用
reverse('page_detail',{"object_id": self.pk})
或者模

板中的
{% url page_detail object_id %}
AND
<a href="{{ object.get_absolute_url }}">{{ object.name
}}</a>
这里要注意的是,Django的URL调度有序列和字典两种形式。

在其他的Web框架中还有一种常见的URL形式:
/{controller}/{action}/{id}
其中controller支持多层的结构


所以有时也会使用到
/pages/
/pages/new/
/pages/view/1/
/pages/edit/1/
/pages/del/1/
这样的URL模式。

在用户接触站点的界面中,URL是一个首先会接触到的内容。
这也是为下一步设计站点的显示效果的设计,做一个基础。

通过使用urls.py文件,就像一个.h一样,可以为整个站点提供一个清晰的结构。


View

URL调度的作用是以形式:
url(r'^posts/$', views.index,name="post_index"),
url(r'^posts/(?P\d+)/$', views.detial,name="post_detial"),
为每个URL模式指定对应的View。

View是一个形为:
def detail(request, object_id):
return HttpResponse("You're looking at object %s." % object_id)
的函数,其中的object_id便是URL的正则表达

式中捕获的部分。

依据习惯,我们会将所编写的View从urls.py中取出,放在App的views.py文件或view目录下。
而且在Django已经提供了若干实用的generic views,这就可以根据需要直接使用,或者只做一下简单的修饰。

继续以一个简单的CRUD为例:
info_dict = {
  'queryset': Post.objects.all(),
}
info_dict2 = {
  'post_save_redirect':'/posts/%(id)s',
#通常不设,默认用get_absolute_url的值
  'model':Post,
}
info_dict3 = {
  'post_delete_redirect':'/posts/',
  'model':Post,
}

urlpatterns = patterns('',
url(r'^posts/$',
'django.views.generic.list_detail.object_list',
info_dict,name="post_list"),

url(r'^posts/(?P<object_id>\d+)/$',
'django.views.generic.list_detail.object_detail',
info_dict,name="post_detail"),

url(r'^(?P<topic_id>\d+)/new/$',
'django.views.generic.create_update.create_object',
info_dict2,name="post_create"),

url(r'^(?P<topic_id>\d+)/edit/(?P<object_id>\d+)/$',
'django.views.generic.create_update.update_object',
info_dict2,name="post_update"),

url(r'^(?P<topic_id>\d+)/del/(?P<object_id>\d+)/$',
'django.views.generic.create_update.delete_object',
info_dict3,name="post_delete"),

)
这里的info_dict中可以再放名为extra_context的字典,用于传入额外需要的值或函数。
url也可以简单的只按照顺序使用正则的捕获来向View传入参数。

对于不能直接使用generic views的,可以来自己编写view:
url(r'^(?P\d+)/$',views.list,

name=""),
然后在views,py中添加
def list(request,user_id):
    author = get_object_or_404(User, pk=user_id)
    queryset = author.post_set.all()
    return list_detail.object_list(request,queryset,
paginate_by=3,extra_context={'author':author})
或者就用更加直接的写法:
def index(request):
  latest_post_list = Post.objects.all().order_by('-pub_date')[:5]
  return render_to_response('posts/index.html', {'latest_post_list': latest_post_list},

context_instance=RequestContext(request)))
自行调用模板来渲染出返回的结果。
generic views的代码也是为自行编写View一个很好的参考


Template

从分工的角度说,模板的设计属于设计师的职责。
Django给它的模板系统很大的限制,使得其中蕴含的应用的逻辑依然能够保留到View部分的代码中。
对于上节末的render_to_response代码,模板里可以使用的内容包括模板地址和传入的dict,
还包括RequestContext中的信息,例如变量 user, messages, perms供使用。
也可自行编写tags and filters供{% load **** %}。

所以说呢,设计模板的内容是和对应的View相承接的,直接由Views所传入的内容所决定。
在创建模板文件时会先考虑其了路径和名称,以及其中可以使用的变量或函数。

下面为一组CRUD的GenericView简单的template示例:
这里应用名为posts,有一个叫Post的模型,模板名为默认。

post_list.html
<a href="./new">new</a>
{% if object_list %}
<table>
  {% for post in object_list %}
  <tr>
    <td><a href="{{post.get_absolute_url }}">
{{ post.title }}</a></td>
  </tr>
  {% endfor %}
</table>
{% else %}
<p>no post</p>
{% endif %}
post_detail.html
<div>
  <div>title:{{ object.title }}</div>
  <div>{{ object.pub_data }}</div>
  <div>{{ object.content }}</div>
<a href="../{{ object.id }}/edit/">edit</a>
<a href="../{{ object.id }}/del/">del</a>
<a href="../">back</a>
post_form.html
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
post_confirm_delete.html
title:{{ object.title }}
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Delete" />
</form>
这里只是一个让CRUD可以工作的示例。

通常的模板文件中,会使用到继承结构
{% extends "base_site.html" %}
{% block content %}
...
{% endblock %}
也会再有base.html建立两层关系,为让页面继承栏目的再继承站点的模板。

为显示messages,可在base.html中添加:
{{% if messages %}<ul class="messagelist">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}</ul>
{% endif %}
以及为user不同的登录状态显示login或logout。

tag 中有linebreaksbr换行转br,也有markup可使用markdown来HTML化文本。


Html & Css

由于Template的继承结构,根据Django的理念在编写View直接使用的模板代码时,
是直接进行网页代码的编写,以分割开显示效果相关以关注其中的逻辑。
所以这一段选择性地说一说站点的前端代码。

xhtml
<h1>标题</h1>
<span>行文本</span>
<strong>加粗<strong>
<em>斜体</em>

<a href="url">链接</a>
<img src="url" alt="text" />图像
<br />换行
<hr />水平线

<p>段落 </p>
<pre>预格式文本</pre>
<div>层</div>

<table border="0">
<tr>
<td>1,1</td>
<td>1,2</td>
</tr>
<tr>
<td>2,1</td>
<td>2,2</td>
</tr>
</table>
表格

<ul>
<li>first</li>
<li>second</li>
</ul>
无序列表
这些代码可以用来组织页面结构。

然后是css
<link rel="stylesheet" type="text/css" href="style.css" /> 引入css文件

strong {font-weight:bold;} 标签选择器
#red {color:red;} id选择器
.center {text-align: center} class选择器

Box
* {
margin: 0;
padding: 0;
}
#box {
border-style: none;
padding: 10px;
}

定位
.box {
float: left;
}
.clear {
clear: both;
}
网页同时使用div的浮动来布局,样式也在css中设定。

可以嵌入JavaScript
<script type="text/javascript"

src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
这里以Jquery为例。

可以使用浏览器中的开发工具来调试站点的各种代码。

然后再考虑一下配色(声音渐低中...


App

根据以上的步骤,以及可以建立一个简单的站点了,比如说一个私人博客。
不过对于有一定复杂度的站点,在设计模型之前,需要为项目进行App的划分。

多App的方式可以使站点的结构变得清晰,也使得App的复用变得方便。

例如:

Pinax是一组用以建立SNS的Apps。
可以创建Pinax项目,或者使用其中的应用,或者阅读其中的代码。
需要注意的是其中Apps之间存在依赖关系。
django-notification就被其中大大多数App可选的使用,
用以向用户发送通知。
django-groups被tribes以及projects使用,
用以建立用户小组。

在Debian的包中,也含了一些Django的Apps
http://packages.debian.org/search?keywords=django
如django-registration django-tagging django-tinymce app-plugins
使用这些组件,除了可以减少重复,以为增加了Apps间相一致的地方。

为了复用方便(Be Portable),需要在编写App时尊崇一些习惯
http://ericholscher.com/projects/reusable-app-docs/

还有一个叫GoFlow的工作流App看起来很有趣。

除了App的复用,也有提供整个项目来进行开发的。
例如Satchmo一个WebShop的App,
django-cms一个CMS的App。

最后说明一下,虽然Django有较好的向后兼容,
但随着版本变化会有不同的内置Apps,所以在第三方代码的实现上也会有不同。
依旧来提官方Wiki,那里始终是个很全的资料收集之处。

这些第三方Apps的存在也是为自己的项目的Apps划分提供一个参照。

像PIL那样的Python库是也会在Django项目中使用的,
更多的可复用apps也可以在Pypi找到若干。

在Django的文件中使用import可以使用Python中通用的相对路径,
也可用以当前应用所在项目名称为开始的绝对路径来引用。


Csrf

这是与安全相关的一个模块,是用于对抗Cross Site Request Forgeries的保护机制。

MIDDLEWARE_CLASSES中添加
'django.middleware.csrf.CsrfResponseMiddleware',
render_to_response时添

context_instance=RequestContext(request))
模板中
<form action=""

method="post">{% csrf_token %}
Form依旧按照
def contact(request):
    if request.method == 'POST': # If the form has been

 submitted...
        form = ContactForm(request.POST)

 # A form bound to the POST data
        if form.is_valid

(): # All validation rules pass
            # Process the data in f

orm.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/')

 # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render_to_response('contact.html', {
        'form': form,
    })
另一种form的形式是从ModelForm派生并在Meta中指定model属性。
在构造时指定instance属性,并提供save(commit=True)用于将form的属性赋予模型对象。

其他

django-admin.py inspectdb用于从已有的db生成model.py代码

若干模块可以在不影响应用设计的情况下被添加,例如:
Admin
i18n
Cache
Syndication
以及一些第三方的分页插件机制。


End
练手中...


http://www.indexofire.com/blog/?p=774
http://komunitasweb.com/2010/02/django-tutorial-simple-notes-application/
http://github.com/vicalloy/LBForum