迁移pylonsbook chapter 8到pylons1.0

Pylonsbook的第八章,是一个完整的CRUD程序例子。pylons升级到1.0之后,有一些代码不能正常工作了。

首先查看你使用的各种库的版本:
paster shell development.ini

import pylons
pylons.__version__
import sqlalchemy
sqlalchemy.__version__
import webhelpers
webhelpers.__version__

Pylons 1.0开始鼓励通过继承Base类的声明式ORM(pylonsbook介绍的是pylons 0.9x,sqlalchemy 0.5x,使用mapper的方式)
对pylons1.0,mode/__init__.py中声明model的代码如下:

from sqlalchemy import *
from sqlalchemy.orm import relationship

class PageTag(Base):
    __tablename__ = 'pagetag'
    id = Column('id', Integer, Sequence('pagetag_seq_id', optional=True), primary_key=True)
    pageid = Column('pageid', Integer, ForeignKey('page.id'))
    tagid = Column('tagid', Integer, ForeignKey('tag.id'))

class Page(Base):
    """docstring for Page"""
    __tablename__ = 'page'
    id = Column('id', Integer, Sequence('page_seq_id', optional=True), primary_key=True)
    content = Column('content', Text, nullable=False)
    posted = Column('posted', DateTime, default=now())
    title = Column('title', Unicode(255), default=u'Untitled Page')
    heading = Column('heading', Unicode(255))

    tags = relationship(PageTag, backref='page')
    comments = relationship("Comment", backref='page')

class Tag(Base):
    """docstring for Tag"""
    __tablename__ = 'tag'
    id = Column('id', Integer, Sequence('tag_seq_id', optional=True), primary_key=True)
    name = Column('name', Unicode(20), nullable=False, unique=True)

class Comment(Base):
    __tablename__ = 'comment'
    id = Column('id', Integer, Sequence('comment_seq_id', optional=True), primary_key=True)
    content = Column('content', Text, default=u'')
    name = Column('name', Unicode(255))
    email = Column('email', Unicode(255), nullable=False)
    created = Column('created', TIMESTAMP, default=now())
    pageid = Column(Integer, ForeignKey('page.id'))

init_model 方法的内容也不相同,但是对Session的配置都是在Session.configure里进行。 如果设置autocommit=True,即使设置了autoflush=True仍然需要Session.flush()一下才能把变更写入数据库。否则,则需要显示调用Session.commit()

在写controller的部分,pylonsbook大量用了

page_q = model.meta.Session.query(model.Page)
c.page = page_q.get(int(id))

page_q = model.meta.Session.query(model.Page)
c.page = page_q.filter_by(id=id).first()

在新版本里只需要
c.page=Session.query(model.Page).get(id)
即可。

在模板部分,pylons现在默认不依赖formbuild,所以文中提到的helper方法要额外安装formbuild库。不过这些也可以用过webhelper来实现,在lib/helpers.py里:
from webhelpers.html.tags import *

在field.html中

<p>Heading: ${h.text(
    "heading"
)}
</p>

<p>
Title: ${h.text(
    "title"
)}
</p>

<p>
Content: ${h.textarea(
    "content", cols=40, rows=10
)}
</p>

模板中一个非常常用的方法url_for在pylons1.0里被改为pylons.url,需要修改/lib/helpers.py
from pylons import url

书中所有的重定向都是直接设置response相关属性实现的,实际可以用redirect,结合url:
redirect(url(controller='page', action='view', id=page.id))

分页部分,如果你使用的是sqlalchemy 0.6的话,随pylons 1.0发布的Webhelpers 0.6还不支持 sqlalchemy 0.6的query object,需要手动升级到1.2. (easy_install -U webhelpers)

最后关于flash状态,webhelpers里有一个专门的实现,不过还是不太好用:
http://pylonshq.com/docs/en/1.0/thirdparty/webhelpers/pylonslib/

The post is brought to you by lekhonee v0.7

Pylons Tips

All scripts created by paster(paster create, paster controller and etc.) uses 4 spaces as indent while many editors(vim) uses tabs automatically. Different indent in one file will cause fatal error.

h.url_for is a popular helper function in many books and tutorials which to solve url mapping. This tool is no longer available by default since 0.97. If you got error message like “AttributeError: ‘module’ object has no attribute ‘url_for’”, just add

[codesyntax lang="python"]from routes import url_for[/codesyntax]
to yourapp/libs/helper.py .