<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Read Sean

    Read me, read Sean.
    posts - 508, comments - 655, trackbacks - 9, articles - 4


    周末花了一個下午和一個晚上把Scott Rosenberg的Dreaming in Code從頭到尾看了一遍。最直接的感受是這本書把我之前很多記憶碎片串在了一起,成為一個生動而"完整"的故事,雖然書的內容本身組織的多少還是有點散。

    dreaming_in_code.jpg

    我本人對Chandler這個項目并不陌生:之前出于對Python/wxWidget和開源本身的興趣,陸續用過幾個0.x的版本,一開始并不是十分友好,性能上也有問題,甚至會莫名的吃掉我機器上的數百兆(或者上G?)空間。后來的版本在性能和可用性上確實提高了不少,但一直感覺這個項目缺少必要的、以及許多開源項目應有的momentum。Phillip J. Eby對Chandler開發人員不懂Python的批評,當時我的印象也很深。而項目中出現的人物,包括Mitchell Kapor、Ted Leung,也都在Chandler這個范疇之外follow過。其他細節包括:Chandler和Cosmo這兩個名稱的由來、Chandler項目組中女性成員相對高的比例、一些熟悉的人物及其觀點(Alan Kay, Bill Joy, Frederick Brooks, Donald Knuth、Linus Torvalds, Ward Cunningham, Larry Wall, Guido van Rossum, Joel Spolsky, etc.)、一些公司的分分和和以及人員流動等等。感覺挺親切的。

    可能更重要、也更深刻的原因是:盡管書中一再提到"There's no such thing as a typical software project, every project is different",我仍然深深的感覺到,Chandler遇到的這些問題,其實也是我親歷的很多項目的種種問題的一個縮影。對這些問題,作者并沒有給出解決方案,其實誰也沒有標準答案。軟件開發是一項非常具有挑戰性的工作,也正是像我們這樣有熱情、有涵養的專業人士生存的空間和價值所在。

    posted @ 2009-03-02 00:44 laogao 閱讀(703) | 評論 (0)編輯 收藏


    以下是一段視頻,Ward Cunningham針對Debt Metaphor這個隱喻的由來和人們對它的一些誤解進行了澄清:



    我最感興趣的是Burden這一段:Cunningham解釋說,經常看到有些開發團隊,他們快速的開發出軟件產品推向市場,不斷的往里面添加新的特性,在這個過程中,不斷的學習,但從不把學到的東西、總結的經驗教訓應用回去,這就像是從銀行借貸,但從不想著有一天需要償還(是不是有點像是在說引發這次次貸危機的美國人的消費習慣和觀念?),到最后,你所有的收入都將用于償還利息,你的購買力也將降為零。映射回軟件開發的語境,如果我們在一個較長的時間跨度內,開發一個軟件,不斷的增加feature,但從不回過頭去整理和重構,把對這個軟件和這些特性的新的理解和認知寫回去,那么最終這個軟件、這些feature將不再會有任何實際的價值,對它們做任何事,都將花費越來越多的時間和精力,開發進度也就因此下降為零。


    posted @ 2009-02-22 17:50 laogao 閱讀(386) | 評論 (0)編輯 收藏


    經歷了有史以來最忙碌的一周,當然要好好放松一下,除了聽上乘的古典音樂,沏上一壺上等的烏龍細細品味,也是一種享受。烏龍茶和紫砂壺可是絕配,如果是安溪的鐵觀音,加上做工精良的宜興紫砂壺,那滋味,唇齒留香,別提多愜意了。

    好的紫砂壺是需要"養"的,今天專程去茶城敗了一只回來,開始"喂"鐵觀音,哈哈。

    DSC_1768s.JPG


    posted @ 2009-02-21 22:49 laogao 閱讀(589) | 評論 (5)編輯 收藏


    先簡單介紹一下問題的語境。

    手頭有個開發了3年的Spring+iBATIS應用(經典三層架構),最近嘗試用Hibernate實現原有SQLMap版的部分CRUD操作。除開混合事務和其他一些底層配置細節(如TransactionAwareDataSource、禁用lazy-load等)之外,最大的一個"pattern-mismatch"是:Model層和持久層采用了dirty flag機制,即每次INSERT和UPDATE操作,都會根據每個字段的dirty與否決定是否參加INSERT/UPDATE,而這些dirty flag可以被外部重置,所以業務層的代碼,經常可以看到類似這樣的pattern:

    1- new一個model類并setId() (或者在已有的model上重置dirty flag)
    2- set需要update的字段(通常都只是部分字段)
    3- 丟給DAO層去update

    最終的效果是某張表某個ID的某條記錄的某些字段被更新了,而其他字段不受影響,這就是我在標題中提到的所謂"暴力"update,雖不elegant,但卻也簡單實用,至少很"直接"。

    為了讓Hibernate版的DAO(默認除Trasient之外全體字段參加INSERT和UPDATE)繼續支持這樣的"use-pattern",除了按照Hibernate的習慣去配置映射和SessionFactory等之外,我們需要做一些額外的工作:

    1- 在BO/Entity類上追加注解
    @org.hibernate.annotations.Entity(dynamicInsert=true,?dynamicUpdate=true)

    2- 實現org.hibernate.Interceptor接口的findDirty()方法,Hibernate提供了一個EmptyInterceptor可以作為起點,方法簽名如下:
    public?int[]?findDirty(
    ????Object?entity,?
    ????Serializable?id,?
    ????Object[]?currentState,?
    ????Object[]?previousState,?
    ????String[]?propertyNames,?
    ????Type[]?types
    );
    返回的int[]包含所有應該被認為是dirty的字段索引,返回null表示默認處理,傳入的entity是持久對象,字段列表會通過propertyNames參數傳給你。

    3- 注入到Spring的Application Context中,類似這樣:
    <bean?id="findDirtyInterceptor"?class="gao.sean.hybrid.interceptor.FindDirtyInterceptor"/>

    <bean?id="sessionFactory"
    ????class
    ="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    ????
    ????
    <property?name="entityInterceptor"><ref?bean="findDirtyInterceptor"/></property>
    ????
    </bean>

    在這樣的配置下,業務層的代碼就可以繼續"暴力"update了。

    posted @ 2009-01-29 16:54 laogao 閱讀(2840) | 評論 (4)編輯 收藏


    如果你使用早前版本的Spring,又恰好采用了Annotation注解方式(而非傳統XML方式)配置Hibernate對象關系映射,那么在通過org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean配置sessionFactory時,你一定對annotatedClasses、annotatedPackages有一種說不出的胸悶的感覺,如此以高配置性見長的Spring,怎么在這一個小小的環節上就不能做得再靈活些呢,一定要一個個手寫Class路徑么?

    估計有不少人無奈選擇了從AnnotationSessionFactoryBean繼承一個自定義的子類,自己實現掃描邏輯,找出@Entity注解過的類清單配置進去。

    Spring 2.5.6里有個不怎么起眼的改進,那就是在AnnotationSessionFactoryBean上增加了一個新的方法:
    setPackagesToScan(String[] packagesToScan)

    有了這個方法,我們不再需要自己動手去實現實體類的掃描了,直接在Spring配置文件中AnnotationSessionFactoryBean這個section上增加類似如下的一個property即可(假定你需要加載的實體類所在的包名match這個字符串"com.**.bo"):
    <property?name="packagesToScan"?value="com.**.bo"/>

    你也可以以清單的方式指定多于1條的匹配字串,如:
    <property?name="packagesToScan">
    ????
    <list>
    ????????
    <value>com.abc.core.bo</value>
    ????????
    <value>com.abc.auditing.bo</value>
    ????
    </list>
    </property>

    posted @ 2009-01-29 02:59 laogao 閱讀(22726) | 評論 (2)編輯 收藏


    Pylons是一個典型的MVC Web框架,在之前的幾篇隨筆中,我們一起了解了Pylons的安裝、默認項目結構、Routes和controller("C")以及SQLAlchemy("M"),在這個系列的最后,我們一起來看看"V"。

    在我們的controller代碼中,每個action在return的時候,可以選擇:
    1- 直接return字符串
    2- 通過render()函數將輸出交給頁面模板引擎處理
    3- redirect_to()重定向到其他URL

    直接return太簡單,redirect_to也沒有特別需要介紹的,重點看render()。如果你一直follow這個系列,那么在你的controllers/hello.py中,可以看到這樣一行import:
    from?newapp.lib.base?import?BaseController,?render

    從lib.base引入了一個render函數,跟到lib/base代碼里查看,我們會知道:
    from?pylons.templating?import?render_mako?as?render
    其實我們用到的render()函數,是pylons.templating.render_mako的別名。

    注: 這里假定你在paster create時選擇了默認的mako,其他Pylons原生支持的頁面模板引擎還有結構相對更層次化的Genshi和更接近Django實現的Jinja。

    render_mako()函數簽名如下:
    render_mako(template_name, extra_vars=None, cache_key=None, cache_type=None, cache_expire=None)

    最基本的用法是給出template文件名,然后通過extra_vars傳入參數,Pylons默認查找頁面模板文件是在項目的templates子目錄,這個路徑也可以在config/environment.py中修改。在Pylons中,被推薦的傳參做法是使用tmpl_context,在生成controller的時候,已經自動import了tmpl_context并別名為c,這樣,我們只需要在c上綁上我們需要傳遞給模板的數據,模板在解析時,也就能夠通過c得到這些數據了。像這樣:
    c.name?=?u'Pylons?n00b'
    return?render('hello.mako')

    然后,在hello.mako中:
    <h3>Hello?<b>${c.name}</b>!</h3>

    在模板代碼中,有些Pylons系統變量/函數是可以直接訪問的,包括:
    tmpl_context (c) - 用于controller和template傳遞數據
    config - 配置信息
    app_globals (g) - 應用的全局變量
    h - WebHelpers,包括h.form、h.link_to、h.url_for等等實用函數
    request - 請求
    response - 應答
    session - 會話信息
    translator、ungettext()、_()、N_() - 處理國際化

    除了基本的${}變量替代語法之外,類似JSP,Mako還支持注釋、if/else/for控制邏輯、代碼片段、return、標簽等,具體的可以掃一眼官方說明:
    http://www.makotemplates.org/docs/syntax.html
    很精簡,也非常容易理解,這里就不詳細說明了。

    至此,我們已經了解了Pylons最核心的幾個組件,足夠我們搭建常規的Web應用了。其他值得大家繼續挖掘的內容包括:國際化、表單驗證(FormEncode)、用戶驗證和權限(AuthKit、repoze.who、repoze.what)、AJAX、Python 3.0、WSGI基礎架構等。

    本文是該系列最后一篇,希望通過簡單的介紹和學習,大家能夠喜歡并順利的上手Pylons,也希望越來越多的人關注這個優秀的Python Web應用框架。

    posted @ 2009-01-27 15:50 laogao 閱讀(2225) | 評論 (0)編輯 收藏


    在前面的4篇隨筆中,我們簡要的介紹了SQLAlchemy,不過SQLAlchemy如何被集成到Pylons應用中呢?

    首先我們看一下自動生成代碼中的model子目錄,其中有兩個文件__init__.py和meta.py,其中meta.py定義了engine、Session和metadata三個公用變量,而__init__.py提供了一個核心的init_model(engine)方法,該方法分別將數據庫engine和經過sessionmaker和scoped_session包裝的Session對象植入到meta中,像這樣:
    ????sm?=?orm.sessionmaker(autoflush=True,?autocommit=True,?bind=engine)

    ????meta.engine?
    =?engine
    ????meta.Session?
    =?orm.scoped_session(sm)

    這樣一來,整個背后的"magic"就還剩下最后一塊"拼圖":誰來把engine初始化好并調用init_model方法呢?看看config/environment.py就清楚了:
    ?1?"""Pylons?environment?configuration"""
    ?2?import?os
    ?3?
    ?4?from?mako.lookup?import?TemplateLookup
    ?5?from?pylons.error?import?handle_mako_error
    ?6?from?pylons?import?config
    ?7?from?sqlalchemy?import?engine_from_config
    ?8?
    ?9?import?newapp.lib.app_globals?as?app_globals
    10?import?newapp.lib.helpers
    11?from?newapp.config.routing?import?make_map
    12?from?newapp.model?import?init_model
    13?
    14?def?load_environment(global_conf,?app_conf):
    15?????"""Configure?the?Pylons?environment?via?the?``pylons.config``
    16?????object
    17?????"""
    18?????#?Pylons?paths
    19?????root?=?os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    20?????paths?=?dict(root=root,
    21??????????????????controllers=os.path.join(root,?'controllers'),
    22??????????????????static_files=os.path.join(root,?'public'),
    23??????????????????templates=[os.path.join(root,?'templates')])
    24?
    25?????#?Initialize?config?with?the?basic?options
    26?????config.init_app(global_conf,?app_conf,?package='newapp',?paths=paths)
    27?
    28?????config['routes.map']?=?make_map()
    29?????config['pylons.app_globals']?=?app_globals.Globals()
    30?????config['pylons.h']?=?newapp.lib.helpers
    31?
    32?????#?Create?the?Mako?TemplateLookup,?with?the?default?auto-escaping
    33?????config['pylons.app_globals'].mako_lookup?=?TemplateLookup(
    34?????????directories=paths['templates'],
    35?????????error_handler=handle_mako_error,
    36?????????module_directory=os.path.join(app_conf['cache_dir'],?'templates'),
    37?????????input_encoding='utf-8',?output_encoding='utf-8',
    38?????????imports=['from?webhelpers.html?import?escape'],
    39?????????default_filters=['escape'])
    40?????
    41?????#?Setup?SQLAlchemy?database?engine
    42?????engine?=?engine_from_config(config,?'sqlalchemy.')
    43?????init_model(engine)
    44?????
    45?????#?CONFIGURATION?OPTIONS?HERE?(note:?all?config?options?will?override
    46?????#?any?Pylons?config?options)

    注意第7行的import和第42、43行代碼,是不是豁然開朗?Pylons在初始化運行環境時,從config中讀取sqlalchemy相關的配置信息,然后通過這些配置信息創建數據庫engine,并調用init_model()方法初始化SQLAlchemy功能的核心對象:metadata和Session。有了meta.Session,我們就可以方便的在代碼中執行對model層/數據庫的訪問了。

    posted @ 2009-01-27 14:00 laogao 閱讀(1188) | 評論 (0)編輯 收藏


    接著前面的例子說,我們定義了book_table和author_table,接下來:
    ?1?class?Book(object):
    ?2?????def?__init__(self,?title):
    ?3?????????self.title?=?title
    ?4?????def?__repr__(self):
    ?5?????????return?"<Book('%s')>"?%?self.title
    ?6?
    ?7?class?Author(object):
    ?8?????def?__init__(self,?name):
    ?9?????????self.name?=?name
    10?????def?__repr__(self):
    11?????????return?"<Author('%s')>"?%?self.name

    這里我們定義兩個類,繼承自object,類似JavaBeans或者POJO,這里的__init__方法和__repr__方法不是必須的,只是為了創建對象和輸出對象內容比較方便。然后就可以用SQLAlchemy的mapper和sessionmaker來建立映射關系并處理持久和查詢等操作:
    ?1?from?sqlalchemy.orm?import?mapper,sessionmaker
    ?2?
    ?3?mapper(Book,?book_table)
    ?4?mapper(Author,?author_table)
    ?5?
    ?6?Session?=?sessionmaker(bind=engine)
    ?7?session?=?Session()
    ?8?
    ?9?gia?=?Book(u'Groovy?in?Action')
    10?ag?=?Author(u'Andrew?Glover')
    11?
    12?session.add(gia)
    13?session.add(ag)
    14?session.add_all([Book('Hibernate?in?Action'),?Author('Gavin?King')])
    15?s_gia?=?session.query(Book).filter_by(title=u'Groovy?in?Action').first()
    16?s_gia.title?=u'Groovy?in?Action?Updated'
    17?
    18?print?"[DIRTY]",?session.dirty
    19
    20?session.commit() # or session.rollback()

    如果你用過Hibernate,那么這些代碼對你來說,理解起來應該沒有任何難度。

    假如我告訴你,每次都要像這樣先定義Table(schema),再定義class,然后用mapper建立對照,是不是有點那啥?SQLAlchemy的開發者們也意識到這一點,所以從0.5開始,SQLAlchemy可以通過sqlalchemy.ext.declarative支持我們實現更緊湊的model/schema定義:
    ?1?from?sqlalchemy.schema?import?Table,?Column,?ForeignKey,?Sequence
    ?2?from?sqlalchemy.types?import?*
    ?3?from?sqlalchemy.orm?import?relation
    ?4?from?sqlalchemy.ext.declarative?import?declarative_base
    ?5?
    ?6?Base?=?declarative_base()
    ?7?metadata?=?Base.metadata
    ?8?
    ?9?bookauthor_table?=?Table('bookauthor',?metadata,
    10?????Column('book_id',?Integer,?ForeignKey('book.id'),?nullable=False),
    11?????Column('author_id',?Integer,?ForeignKey('author.id'),?nullable=False),
    12?)
    13?
    14?class?Book(Base):
    15?????__tablename__?=?'book'
    16?????id?=?Column(Integer,?Sequence('seq_pk'),?primary_key=True)
    17?????title?=?Column(Unicode(255),?nullable=False)
    18?????authors?=?relation('Author',?secondary=bookauthor_table)
    19?
    20?
    21?class?Author(Base):
    22?????__tablename__?=?'author'
    23?????id?=?Column(Integer,?Sequence('seq_pk'),?primary_key=True)
    24?????name?=?Column(Unicode(255),?nullable=False)
    25?????books?=?relation('Book',?secondary=bookauthor_table)

    這里我們用到了many-to-many關系,其他的常見用法還包括many-to-one、one-to-many、JOIN、子查詢、EXISTS、Lazy/Eager Load、Cascade (all/delete/delete-orphan)等等,大家可以根據需要查閱官方文檔。

    posted @ 2009-01-27 12:52 laogao 閱讀(1690) | 評論 (0)編輯 收藏


    在介紹SQLAlchemy最核心最有價值的ORM部分之前,我們再簡單過一遍SQLAlchemy提供的SQL Expression Language用法,就從最基本的CRUD來舉例說明吧(接著上一篇的示例):

    ?1?from?sqlalchemy?import?select,update,delete
    ?2?
    ?3?conn?=?engine.connect()
    ?4?book_ins?=?book_table.insert(values=dict(title=u'Groovy?in?Action'))
    ?5?author_ins?=?author_table.insert(values=dict(name=u'Andrew?Glover'))
    ?6?conn.execute(book_ins)
    ?7?conn.execute(author_ins)
    ?8?book?=?conn.execute(select([book_table],?book_table.c.title.like(u'Groovy%'))).fetchone()
    ?9?author?=?conn.execute(select([author_table])).fetchone()
    10?bookauthor_ins?=?bookauthor_table.insert(values=dict(book_id=book[0],author_id=author[0]))
    11?conn.execute(bookauthor_ins)
    12?conn.execute(update(book_table,book_table.c.title==u'Groovy?in?Action'),?title=u'Groovy?in?Action?(中文版)')
    13?conn.execute(delete(bookauthor_table))
    14?conn.close()

    簡單說明一下代碼邏輯:
    首先從engine建立連接,然后做兩個insert動作,分別insert一條book記錄(title為'Groovy in Action')和一條author記錄(name為'Andrew Glover'),這之后分別再做兩次select,得到剛insert的這兩條記錄,其中book記錄的select用到了過濾條件,相當于"WHERE book.title like 'Groovy%'",然后構建一條新的insert語句,用于insert一條bookauthor關系記錄,接下來,做一次update,將book.title為'Groovy in Action'的更新為'Groovy in Action (中文版)',最后,在關閉連接之前,做一次delete,刪除bookauthor中的記錄。

    在指定WHERE條件時,.c是.columns的簡寫,所以book_table.c.title指代的就是book表的title列。更高級的用法是采用"&"、"|"、"!"三個符號,分別表示AND、OR和NOT,加上必要的"("和")"實現復雜的條件定義。由于傳遞給select()的第一個參數是個list,所以你應該已經猜到了,我們也可以多張表做關聯查詢。

    posted @ 2009-01-26 23:40 laogao 閱讀(1404) | 評論 (0)編輯 收藏


    在sqlalchemy.schema和sqlalchemy.types這兩個module中,包含了定義數據庫schema所需要的所有類,如Table、Column、String、Text、Date、Time、Boolean等。還是來看一個例子:

    ?1?from?sqlalchemy.engine?import?create_engine
    ?2?from?sqlalchemy.schema?import?MetaData,?Table,?Column,?ForeignKey,?Sequence
    ?3?from?sqlalchemy.types?import?*
    ?4?
    ?5?engine?=?create_engine('postgres://test:test@localhost/test',?echo=True)
    ?6?
    ?7?metadata?=?MetaData()
    ?8?metadata.bind?=?engine
    ?9?
    10?book_table?=?Table('book',?metadata,
    11?????Column('id',?Integer,?Sequence('seq_pk'),?primary_key=True),
    12?????Column('title',?Unicode(255),?nullable=False),
    13?)
    14?
    15?author_table?=?Table('author',?metadata,
    16?????Column('id',?Integer,?Sequence('seq_pk'),?primary_key=True),
    17?????Column('name',?Unicode(255),?nullable=False),
    18?)
    19?
    20?bookauthor_table?=?Table('bookauthor',?metadata,
    21??? Column('book_id',?Integer,?ForeignKey('book.id'),?nullable=False),
    22??? Column('author_id',?Integer,?ForeignKey('author.id'),?nullable=False),
    23)
    24
    25metadata.create_all(checkfirst=True)

    首先我們還是create_engine,然后新建一個MetaData對象,把engine綁上去,接下來,開始在metadata中定義表結構(metadata由Table構造函數傳入),我們這里定義了3張表,分別是book、author和bookauthor關系表(“多對多”),其中新建一個Sequence對象,專門處理主鍵生成。最后我們通過執行metadata.create_all()創建數據庫表,參數checkfirst=True表示如果數據庫相關對象已經存在,則不重復執行創建。

    對于已經存在于數據庫中的表,我們可以通過傳入autoload=True參數到Table構造函數的方式來加載現有的表結構到metadata中,而不必挨個兒再寫一遍Column清單。

    看到這兒,你也許覺得挺麻煩,不是么?Django和RoR都是可以直接定義數據model類,順帶就把schema也定義了,而不是像這樣單獨去寫表結構的schema,顯得很"底層"。確實,這樣用SQLAlchemy并不是最優化的,SQLAlchemy本身并不會自動的幫你做很多事,但它基礎打得很牢。如果你感興趣,也可以先去看一下SQLAlchemy的擴展模塊Elixir,通過Elixir,你可以像Ruby on Rails那樣定義出實體和關系("Active Record")。

    在稍后的第4部分中,我們會去了解如何以聲明方式來更緊湊的定義我們的model/schema(0.5新特性)。鑒于筆者傾向于更強的控制力,所以在這個系列中就不去介紹SQLAlchemy的其他擴展模塊了,如Elixir、SQLSoup等,大家可以根據需要去找下官方文檔。

    posted @ 2009-01-26 22:14 laogao 閱讀(2463) | 評論 (0)編輯 收藏


    ORM是個大話題,大到可能好幾本書都說不完。SQLAlchemy,別看它剛出到0.5.2,已然是Python世界ORM的事實標準,受到眾多開發者和無數框架的青睞。

    如果之前沒有或很少接觸SQLAlchemy,那么學習Pylons可能有相當一部分時間都會花在SQLAlchemy上。通常,人們選擇Pylons或者TurboGears而不是Django,SQLAlchemy在這些決定的背后有著很重的分量。作為Pylons學習筆記的一部分,接下來我將分4篇隨筆介紹SQLAlchemy,分別是Engine API、Schema Management (MetaData/Types)、SQL Expression Language和Object Relational Mapper。此文為第1篇,重點介紹Engine API。

    類似Java的JDBC,Python也有一個類似的數據庫訪問接口規范,那就是DB-API(目前是2.0),不同的常見RDBMS都有符合DB-API標準的Python庫,比如PostgreSQL有psycopg2,Oracle有cx_Oracle等。有了這個基礎,SQLAlchemy也就得以方便的通過DB-API連接不同的數據庫。

    以PostgreSQL為例,通過SQLAlchemy的Engine API訪問數據庫的代碼可以這樣來寫:
    ?1?from?sqlalchemy.engine?import?create_engine
    ?2?
    ?3?engine?=?create_engine('postgres://user:pass@localhost/testdb')
    ?4?connection?=?engine.connect()
    ?5?connection.execute(
    ?6?????"""
    ?7?????CREATE?TABLE?book
    ?8?????(
    ?9?????? id?serial?NOT?NULL,
    10??????title?character?varying(30)?NOT?NULL,
    11??????CONSTRAINT pk_book PRIMARY?KEY?(id)
    12?????);
    13??? """
    14?)
    15?connection.execute(
    16?????"""
    17?????INSERT?INTO?book?(title)?VALUES?(%s);
    18?????""",
    19?????"The?Art?of?UNIX?Programming"
    20?)
    21?rs?=?connection.execute("SELECT title FROM book")
    22?for?row?in?rs:
    23?????print?"Book?Title:?",?row['title']
    24?connection.close()

    基本步驟就是create_engine、connect、execute和close,沒有很特別的地方,不過SQLAlchemy的Engine API,并不是簡單的DBAPI調用,而是包裝了其他內容,如數據庫連接池,我們可以在create_engine的時候指定連接池參數和其他額外配置,類似這樣:
    engine?=?create_engine('postgres://user:pass@localhost/testdb',?pool_size=10, convert_unicode=True)

    類似Spring的JdbcTemplate,更多的時候,統一使用SQLAlchemy的Engine API而不是DB-API能給我們帶來更大的靈活性,通常也更方便、更安全。

    posted @ 2009-01-26 20:46 laogao 閱讀(2500) | 評論 (1)編輯 收藏


    在開始之前,說點提外話,隨著對Pylons了解的深入,你可能時不時需要看看相關組件/軟件包是否有更新出來,方法也不復雜,通過"easy_install -U [組件名]"即可,在學習或者是開發過程中,最好是保持環境相對較新,直到出現相對大的release或者即將進入產品部署階段。

    繼續介紹Pylons組件,先看個例子。首先用"paster controller hello"增加一個controller,路徑中會增加出以下兩個文件:
    controllers/hello.py
    tests/functional/test_hello.py

    分別對應新增的controller類HelloController和功能測試類TestHelloController,它們分別繼承自WSGIController->BaseController和TestCase->TestController。

    我們主要看hello.py,默認內容如下:
    ?1?import?logging
    ?2?
    ?3?from?pylons?import?request,?response,?session,?tmpl_context?as?c
    ?4?from?pylons.controllers.util?import?abort,?redirect_to
    ?5?
    ?6?from?newapp.lib.base?import?BaseController,?render
    ?7?#from?newapp?import?model
    ?8?
    ?9?log?=?logging.getLogger(__name__)
    10?
    11?class?HelloController(BaseController):
    12?
    13?????def?index(self):
    14?????????#?Return?a?rendered?template
    15?????????#???return?render('/template.mako')
    16?????????#?or,?Return?a?response
    17?????????return?'Hello?World'

    如果你的服務器沒有Ctrl-C停掉,那么這個時候你已經可以通過
    http://127.0.0.1:5000/hello/index
    看到該controller的處理結果了(Hello World)。

    簡單改造一下17行:
    ????????from?pylons?import?config
    ????????
    return?'<br/>'.join(config.keys())

    我們就可以在返回頁面上顯示出所有可以通過pylons.config訪問到的參數列表。出了返回文本,也可以通過render()方法交給頁面模板引擎生成頁面,也可以通過redirect_to()跳轉到其他URL。

    Pylons是如何找到該請求應該由HelloController的index方法來處理的呢?這背后發生了什么?答案就是Routes。

    Routes的作者是Ben Bangert,是Pylons框架三個主要作者/維護者之一,早期的版本主要是仿照Ruby on Rails的routes.rb開發的,有RoR經驗的朋友可能一眼就能發現它們之間的相似之處。目前Routes的最新版是1.10.2。

    Pylons應用中,routing的配置在config/routing.py,默認生成的內容如下:
    ?1?"""Routes?configuration
    ?2?
    ?3?The?more?specific?and?detailed?routes?should?be?defined?first?so?they
    ?4?may?take?precedent?over?the?more?generic?routes.?For?more?information
    ?5?refer?to?the?routes?manual?at?http://routes.groovie.org/docs/
    ?6?"""
    ?7?from?pylons?import?config
    ?8?from?routes?import?Mapper
    ?9?
    10?def?make_map():
    11?????"""Create,?configure?and?return?the?routes?Mapper"""
    12?????map?=?Mapper(directory=config['pylons.paths']['controllers'],
    13??????????????????always_scan=config['debug'])
    14?????map.minimization?=?False
    15?????
    16?????#?The?ErrorController?route?(handles?404/500?error?pages);?it?should
    17?????#?likely?stay?at?the?top,?ensuring?it?can?always?be?resolved
    18?????map.connect('/error/{action}',?controller='error')
    19?????map.connect('/error/{action}/{id}',?controller='error')
    20?
    21?????#?CUSTOM?ROUTES?HERE
    22?
    23?????map.connect('/{controller}/{action}')
    24?????map.connect('/{controller}/{action}/{id}')
    25?
    26?????return?map

    在這個配置中,對我們剛才的實例起到決定性作用的是第23行,我們的輸入URL為"http://127.0.0.1:5000/hello/index",其中"/hello/index"通過"/{controller}/{action}"這個表達式match出controller為hello而action為index的解析結果,從而在controllers目錄找到hello.py,和其中HelloController的index方法,進行調用。

    map.connect()在上面代碼中體現出兩種用法:
    map.connect('pattern', key=value) - 指定默認的controller、action、id等
    map.connect('pattern') - 直接指定pattern

    pattern字符串允許通配符,通常在最后一個元素上,比如'/{controller}/{action}/{*url}',將后面的整個URL片段交給前面指定的controller/action處理。除此以外,map.connect()還支持

    1- "路徑別名",如:
    map.connect('name', 'pattern', [_static=True])
    如果_static設為"True",表示為"靜態命名路徑"。
    2- 額外的匹配條件,如:
    map.connect('/{controller}/{action}/{id}', requirements={'year': '\d+',})
    map.connect('/{controller}/{action}/{id}', conditions=dict(method=['GET','POST']))

    所有的route優先級為從上到下。Routes除了提供解析進來的URL的邏輯,在我們的controller和template代碼中,我們還可以方便的通過WebHelpers的url_for()方法計算相應的URL。

    Routes 1.x中的有一些仿routes.rb功能將會在2.0中被去掉,包括Route Minimization、Route Memory、Implicit Defaults等。如果有興趣的話,可以參考一下官方文檔,這里就不一一介紹了。為什么要去掉?當然主要的動機還是減少歧義,避免一些不必要的混淆。至于深層次的原因么,可以參考Tim Peters《The Zen of Python》中的一句經典的Python哲學:Explicit is better than implicit。什么?沒有聽說過?打開python命令行,輸入"import this"后回車,慢慢體會其中的道理吧。:)


    posted @ 2009-01-26 16:21 laogao 閱讀(3993) | 評論 (0)編輯 收藏


    大家新年好!

    前一篇隨筆中,大家了解了什么是Pylons,有哪些特點,今天筆者繼續給介紹默認生成的項目結構。

    通過Paste創建新的Pylons應用很簡單,就是一句"paster create -t pylons [應用名]"命令,其中"-t pylons"或者全稱"--template=pylons",用以告訴Paste我們新建的項目,將是一個Pylons應用,或者說,從一個預定義好的Pylons默認項目模板生成。如果你愿意,你也可以自己來制作新的模板以符合需要。"paster create --list-templates"可以查看當前可用的模板。

    假定我們新建的Pylons項目名稱為NewApp,那么執行"paster create -t pylons NewApp"后,我們將得到一個新的NewApp目錄,其中包含一個docs目錄(文檔)、一個newapp目錄(代碼)、一個NewApp.egg-info目錄(元數據)和一些頂級配置文件。

    在開發過程中,我們經常會用到的是代碼目錄(這里是newapp)和位于項目根目錄下的development.ini和test.ini文件了。注意這里newapp的大小寫,Pylons在生成項目的時候,不論你指定的項目名稱大小寫是怎樣,這里會自動.lower()轉小寫,只有項目頂級路徑和egg信息會保留原始大小寫,筆者認為這更加符合Web應用的nature。

    代碼目錄下包括config、controllers、lib、model、public、templates和tests子目錄,分別用于存放配置(如環境、中間件、路徑查找邏輯)、控制器(處理請求)、全局輔助代碼(全局變量、helpers等)、模型(后臺數據訪問)、靜態頁面/媒體文件、頁面模板和測試代碼。

    最后說說ini文件:默認生成的development.ini和test.ini顧名思義分別對應開發和測試需要的基本配置,我們可以通過修改相應的參數配置來指定具體設定,如服務器IP和端口、數據庫連接、日志等。看到這里你也許會問,那么產品環境呢?答案是默認沒有生成,你可以從development.ini修改成需要的產品環境配置,也可以通過paster make-config newapp production.ini生成一個默認的產品環境典型配置。

    以development.ini為例,Pylons的ini文件主要包括4個部分的內容:

    1- 全局默認參數,如
    [DEFAULT]
    debug?
    =?true
    #?Uncomment?and?replace?with?the?address?which?should?receive?any?error?reports
    #email_to?
    =?you@yourdomain.com
    smtp_server?
    =?localhost
    error_email_from?
    =?paste@localhost



    2- 服務器配置,如
    [server:main]
    use?
    =?egg:Paste#http
    host?
    =?127.0.0.1
    port?
    =?5000



    3- 應用程序配置,如
    [app:main]
    use?
    =?egg:NewApp
    full_stack?
    =?true

    cache_dir?
    =?%(here)s/data
    beaker.session.key?
    =?newapp
    beaker.session.secret?
    =?somesecret

    #?If?you'd?like?to?fine-tune?the?individual?locations?of?the?cache?data?dirs
    #?for?the?Cache?data
    ,?or?the?Session?saves,?un-comment?the?desired?settings
    #?here:
    #beaker.cache.data_dir?
    =?%(here)s/data/cache
    #beaker.session.data_dir?
    =?%(here)s/data/sessions

    #?SQLAlchemy?database?URL
    sqlalchemy.url?
    =?sqlite:///%(here)s/development.db

    #?WARNING:?*THE?LINE?BELOW?MUST?BE?UNCOMMENTED?ON?A?PRODUCTION?ENVIRONMENT*
    #?Debug?mode?will?enable?the?interactive?debugging?tool
    ,?allowing?ANYONE?to
    #?execute?malicious?code?after?an?exception?is?raised.
    #set?debug?
    =?false
    簡單說明一下,這里的full_stack設置為true表示打開交互式調試和錯誤報告等功能,"%(here)s"會被替換成項目所在路徑,類似相對路徑url中的"."轉絕對路徑,而beaker.*為配置會話相關的設定,如緩存、cookie基本內容等。對于產品環境來說,比較重要的一個配置是"set debug=false",否則一旦出現異常,交互式調試將使得攻擊者能夠執行系統命令。

    4- 日志,如
    [loggers]
    keys?
    =?root,?routes,?newapp,?sqlalchemy

    [handlers]
    keys?
    =?console

    [formatters]
    keys?
    =?generic

    [logger_root]
    level?
    =?INFO
    handlers?
    =?console

    [logger_routes]
    level?
    =?INFO
    handlers?
    =
    qualname?
    =?routes.middleware
    #?
    "level?=?DEBUG"?logs?the?route?matched?and?routing?variables.

    [logger_newapp]
    level?
    =?DEBUG
    handlers?
    =
    qualname?
    =?newapp

    [logger_sqlalchemy]
    level?
    =?INFO
    handlers?
    =
    qualname?
    =?sqlalchemy.engine
    #?
    "level?=?INFO"?logs?SQL?queries.
    #?
    "level?=?DEBUG"?logs?SQL?queries?and?results.
    #?
    "level?=?WARN"?logs?neither.??(Recommended?for?production?systems.)

    [handler_console]
    class?
    =?StreamHandler
    args?
    =?(sys.stderr,)
    level?
    =?NOTSET
    formatter?
    =?generic

    [formatter_generic]
    format?
    =?%(asctime)s,%(msecs)03d?%(levelname)-5.5s?[%(name)s]?%(message)s
    datefmt?
    =?%H:%M:%S

    OK,這一篇就先講到這兒,下一篇將介紹Routes和controller。


    posted @ 2009-01-26 12:33 laogao 閱讀(991) | 評論 (0)編輯 收藏


    Pylons是一個Python語言的Web應用程序框架,如果你簡單了解過Ruby on Rails和Django,你大概會問,Pylons有什么不一樣呢?Pylons最大的特點是模塊化,將處理Web應用環境下不同領域、不同問題的軟件包集成在一起,形成一個整體,在提供一攬子解決方案的同時,不阻礙你選擇別的替代組件。另外,Pylons是目前對WSGI標準支持最好的框架之一,未來的TurboGears 2.0也會基于Pylons構建。

    Pylons從Ruby on Rails借鑒了不少東西,比如Routes,比如WebHelpers,從表面看更像是Python版的RoR,不過底下的架構應該說更加輕量和靈活,因為你可以靈活選擇自己熟悉或者更貼和具體應用實際的組件,從ORM到頁面模板,Pylons只是推薦一些大家普遍比較認可的選項,但并不強制你使用它們。

    說完和Ruby on Rails的異同,當然也要回過頭來說說同樣是Python編寫的Django。如果你只是想迅速的構建一個可以支撐大量訪問的Web應用,Django是個不錯的選擇,但和RoR一樣,你在很大程度上被限制在一定的pattern中:如果你按照Django的思路去實現你的應用,你會很happy;但一旦你覺得某個組件你不喜歡、不符合某個實際要求,想要來點定制,你就會覺得有些伸不開拳腳,或者工程浩大。目前感覺Django比較不爽的地方有:頁面模板較弱,表現力有些不足,也有人說夠用了;ORM目前是自己的一套,暫時沒有成熟的SQLAlchemy支持,需要第三方包或者自己做;從架構上,Django對MVC的解讀是MTV(Model-Template-View),大家都叫作controller的東東,在Django的世界里是view,以至于每次和別人解釋,都要多費一番口舌。

    Pylons目前版本是0.9.7(rc4),主要用到的第三方/獨立組件有Paste、Routes、Beaker、Mako、FormEncode、WebHelpers和SQLAlchemy。安裝方法如下:

    首先你必須有Python(2.3+),然后你可以選擇直接easy_install Pylons或者新建一個Virtual Environment,和系統中的Python環境隔離開,依賴的包可以獨立升級。這里我們按照后一種方式,如果你是第一次使用Pylons,建議你也在獨立Python virtualenv中安裝。

    1- easy_install virtualenv (這將安裝Python虛擬環境工具)
    2- python virtualenv.py ENV (創建新的虛擬環境。這里的ENV是你新建虛擬環境的路徑,如"mydevenv")
    3- source ENV/bin/activate (激活虛擬環境。如果是Windows的話,這里需要執行ENV\bin\activate.bat)
    4- easy_install Pylons (這里使用的是虛擬環境的easy_install安裝)

    如果你覺得上面的步驟麻煩,Pylons開發團隊提供了一個腳本來處理安裝過程,下載后用Python執行即可:
    http://www.pylonshq.com/download/0.9.7/go-pylons.py

    如果需要SQLAlchemy,則再執行一下
    easy_install SQLAlchemy

    安裝成功后,通過
    paster create -t pylons [應用名]
    即可新建Web應用主框架,然后cd到應用下,通過
    paster serve --reload development.ini
    啟動Web服務,默認地址在
    http://127.0.0.1:5000/

    更詳細的信息,可參考Pylons項目主頁:
    http://pylonshq.com/

    隨著使用的深入,筆者還會陸續對Pylons和其他相關組件進行進一步的介紹。祝各位農歷新年快樂!


    posted @ 2009-01-25 20:05 laogao 閱讀(2033) | 評論 (2)編輯 收藏


    在Unix環境下,命令行或者shell中sleep和kill是常見的動作,在Windows的.bat文件中處理類似的任務就不那么直接了,備忘如下:

    [sleep]
    ping 127.0.0.1 -n 需要的秒數+1 -w 1000 > nul

    [kill]
    taskkill /f /im "進程名(如notepad.exe)"
    taskkill /f /fi "WINDOWTITLE eq notepad*"

    其中/f表示強制,/im表示image鏡像名(可執行文件名),/fi表示filter,后面跟表達式,比如這里的"窗體標題等于notepad*",支持wildcast通配符。


    posted @ 2009-01-23 19:36 laogao 閱讀(2223) | 評論 (0)編輯 收藏

    僅列出標題
    共34頁: 上一頁 1 2 3 4 5 6 7 8 9 下一頁 Last 
    主站蜘蛛池模板: 亚洲男人天堂av| 亚洲精品中文字幕无乱码麻豆| 全免费a级毛片免费看不卡| 久久这里只精品99re免费| 国产免费久久精品99re丫y| 亚洲熟妇av一区二区三区漫画| 国产精品免费一区二区三区四区| 亚洲国产精品综合福利专区| 四虎影库久免费视频| 99xxoo视频在线永久免费观看| 亚洲AV永久无码精品一福利 | 九一在线完整视频免费观看| 久久被窝电影亚洲爽爽爽| 免费无码AV片在线观看软件| 中文字幕在线免费视频| 亚洲综合久久一本伊伊区| 国产AV无码专区亚洲AV手机麻豆 | 四色在线精品免费观看| 国产免费无码一区二区 | 50岁老女人的毛片免费观看| 在线亚洲v日韩v| 亚洲精品网站在线观看你懂的| 亚洲av无码天堂一区二区三区| 免费人成在线观看69式小视频| 青青青免费国产在线视频小草| 中文字幕视频在线免费观看| 免费高清A级毛片在线播放| 国内自产拍自a免费毛片| 免费在线黄色电影| 国产亚洲综合视频| 91亚洲视频在线观看| 国产亚洲AV夜间福利香蕉149| 女人与禽交视频免费看 | 在线看片人成视频免费无遮挡| 免费人成黄页在线观看日本| 美女羞羞免费视频网站| 亚洲成电影在线观看青青| 九月丁香婷婷亚洲综合色| 亚洲福利精品一区二区三区| 夜夜爽免费888视频| 999在线视频精品免费播放观看|