Archive for June, 2008

使用SQLObject

Monday, June 2nd, 2008

使用SQLObject

译者: gashero

目录

1   导入模块

from sqlobject import *

2   定义MySQL使用的URI连接

mysqluri="mysql://user:password@host:port/database"

端口号为3306,一定要指定的。否则按照旧式连接方法里面,端口port属性设为None,就会抛出TypeError异常,因为要求必须是一个整数类型的端口号。如果按照新式的URI方式连接,如果不指定端口号则port默认为0,也会出现连接失败的问题。

sqlite的连接:

sqlite:///full/path/to/database
sqlite:/C|full/path/to/database
sqlite:/:memory:

postgre数据库的连接:

postgres://user@host/database?debug=&cache=
postgres://host:5432/database

3   连接

conn=connectionForURI(mysqluri)
sqlhub.processConnection=conn

4   定义一个表格类

class Person(SQLObject):
    firstName=StringCol()
    middleInitial=StringCol(length=1,default=None)
    lastName=StringCol()

如果没有定义sqlhub,则需要使用 Person._connection=conn 来指定连接。

5   创建表格

Person.createTable()

可以指定参数ifNotExists=True,仅仅在表格不存在时才创建这个表格。

6   自动索引

自动给出一个叫做id的索引,所以不需要人为指定。

在MySQL中定义是:

INT PRIMARY KEY AUTO_INCREMENT

需要使用这个字段时,使用.id属性。

7   创建一个对象

就是创建类的实例:

Person(firstName="John",lastName="Doe")

在SQLObject中的NULL/None并不是代表缺省。NULL代表完全不同的事物、正文或者是人。有时NULL也代表缺省(default),有时代表不适用,有时代表未知。如果希望缺省一个值,可以使用NULL或其他。

SQLObject的default不同于数据库的default。SQLObject从不使用数据库的default。

注意,创建一个对象时,构造方法的参数必须要指定列名,也就是映射类的属性名,否则会出现异常。

8   空值

如果在Person的实例中省略了firstName和lastName则会产生错误,因为没有赋予缺省值。如果是赋予了缺省值的如middleInitial字段,则会被设为NULL值,等同于数据库的None。

9   查询

可以使用类的.get(n)方法得到已经存在的实例。

当创建一个对象时,SQLObject会自动将其存入数据库,而不像其他系统那样,需要手动提交修改。另外,对已有对象的属性修改也会直接存入数据库。

列可以按照属性来存取。注意,对象是独一无二的(unique),如果两次获取同一ID的记录,则获得的是同一对象。这种机制可以确保多个线程存取 同一对象时的一致性。当然,多个线程之间可以不共享对象实例。但是在使用事务(transaction)时,因为事务的隔离性而不成立。

10   开启调试显示

同步显示SQL语句和调试状态。建立连接时使用debug选项:

mysql://user:passwd@host:port/database?debug=t

或:

Person._connection.debug=True

还可以选用的其他选项包括debugOutput(缺省为False),cache(True),autoCommit(True),debugThreading(False)。

在可以看到SQL语句的情况下可以清除的理解工作过程。只需在连接URI中加入”?debug=t”即可。或者设置debug属性。这样,所有的SQL语句都会打印到控制台。这是可靠的,也推荐使用。

11   set方法

用于微弱的提高性能,一次指定多个属性,如:

>>> p.set(firstName="Robert",lastName="Hope Jr.")

12   懒惰的更新

缺省时,每修改一个属性就会导致一个UPDATE的发生,或者每次调用 .set() 时。如果想要避免(avoid)这种仿佛的更新,加入 _lazyUpdate=True 到类定义。这样只有在每次调用 inst.syncUpdate() 或者 obj.sync() 时才写入更新, .sync() 也同时会取出数据库中的最新数据,而 .syncUpdate() 并不这样做。

如果一个实例含有”.sqlmeta.dirty”属性,就是用来指示含有未提交数据的属性。则插入动作会立即提交,这时是没有办法延迟提交的。

13   一对多联系

就是需要建立外键。例如一个地址本类,需要建立外键对应到Person类。

class Address(SQLObject):
    street=StringCol()
    city=StringCol()
    state=StringCol(length=2)
    zip=StringCol(length=9)
    person=ForeignKey('Person')
Address.createTable()

这样通过ForeignKey()方法建立对Person类的外键。实际上是引用了Person对象。实际是按照类的名字(字符串)来引用一个类。数据库中对应person_id列,对应指向person列。

注意:SQLObject使用字符串来引用一个类是因为很多时候也许一个类还不存在。class关键字作为一个命令,在导入一个模块时才执行,并绑定类的名字和类。所以为了确保类之间的引用正确,在所有的类都装载完成之后才建立类之间的联系。

在需要一个人Person对应多个地址时,可用如下连接:

class Person(SQLObject):
    ...
    addresses=MultipleJoin('Address')

在我们已经拥有了Person类的情况下,可以用如下方式修改:

Person.sqlmeta.addJoin(MultipleJoin('Address',joinMethodName='addresses'))

大多数时候可以在创建SQLObject类之后再修改他们。在类定义中使用*Col对象属性等同于使用类方法addColumn()。

然后我们就可以使用自动联系aPerson.addresses,这将会返回一个列表。例如:

>>> p.addresses
[]
>>> Address(street='123 W Main St',city='Smallsville',
...         state="MN",zip='55407',person=p)
<Address 1 ...>
>>> p.addresses
[<Address 1 ...>]

多连接(Multiple Join)类似于关系连接(Related Join),返回结果的列表。你可能更喜欢得到SelectResults对象,可以使用SQLMultipleJoin和SQLRelatedJoin。

14   多对多联系

这个例子中包含用户(user)和角色(role)两个对象,其间包含多对多联系,将使用RelatedJoin来实现。

class User(SQLObject):
    class sqlmeta:
        #user是一些数据库的保留字,所以用了别名
        table="user_table"
    username=StringCol(alternateID=True,length=20)
    #暂时先定义这个,其他还可以有很多字段
    role=RelatedJoin("Role")
class Role(SQLObject):
    name=StringCol(alternateID=True,length=20)
    users=RelatedJoin('User")
User.createTable()
Role.createTable()

注意:使用sqlmeta类。这个类用于存储多种元信息(metadata)。这是SQLObject 0.7中新引入的特性。查看Class sqlmeta节了解详细。

使用:

bob=User(username="bob")
tim=User(username="tim")
admin=Role(name='admin')
editor=Role(name='editor')
bob.addRole(admin)
bob.addRole(editor)
tim.addRole(editor)
>>> bob.roles
[<Role 1 name="admin">, <Role 2 name='editor'>]
>>> tim.roles
[<Role 2 name='editor'>]
>>> admin.users
[<User 1 username='bob'>]
>>> editor.users
[<User 1 username='bob'>, <User 2 username='tim'>]

这会自动生成一个中间表role_user。用来同时引用其他类。这个表不会成为一个暴露(expose)的类,这个表的行也不会等同于Python对象。这种多对多的关系完全被隐藏了。

如果想要自己创建中间表,用来增加一些字段,可以查看标准的SQLObject的add/remove方法来工作。假设你可以提供连接列和其他类的 正确连接,也无法通过这些方法插入扩展信息,而且也无法设置默认值。(Assuming that you are providing the join with the correct joinColumn and otherColumn arguments, be aware it’s not possible to insert extra data via such methods, nor will they set any default value.)。

如前述的User/Role系统,如果创建了UserRole中间表,通过创建两个外键来建立MTM(多对多Many-to-Many)联系,并且 附加了DateTimeCol类型的字段(缺省为当前时间)。那么这个列在使用addRole()方法加入role之前会保持为空。

你可能会注意到列加入了扩展保留字alternateID。可以使用alternateID=True来确保该字段的值唯一(uniquely)。 有如确保用户名必须唯一一样。这个标识符会成为主键,且可以访问的。对于这种字段,如果添加了相同值,则会抛出异常 pysqlite2.dbapi2.IntegrityError: column [列名] is not unique。

注意:SQLObject必须确保主键是唯一且不可改变的。可以通过SQLObject来改变主键,但是你要自己提供确保数据一致性的机制。正是因为这个原因,才推荐使用无意义的整数ID,这样可以确保在未来改变时比较安全。

一个alternateID类创建一个类方法,形如byUsername来对应一个叫做username的列(也可以使用alternateMethodName关键字参数来重载)。如下使用:

>>> User.byUsername('bob')
<User 1 username='bob'>
>>> Role.byName('admin')
<Role 1 name='admin'>

15   选择多个对象(查询)

查询才是真正有用的东西,比显示联系重要的多。select是一个类方法,可以按照如下方式使用:

>>> Person._connection.debug=True
>>> peeps=Person.select(Person.q.firstName=="John")
>>> list(peeps)
 1/Select : 使用的SQL语句
 1/COMMIT : auto
[<Person 1 firstName='John' ...>]

这个例子放回使用John作为firstName的所有人。一个使用表达式的复杂例子:

>>> peeps=Person.select(
...     AND(Address.q.personID==Person.q.id,
...         Address.q.zip.startswith('504')))
>>> list(peeps)
..............
[]

属性q用于给出存取特定对象的结构化查询子句。所有被q所引用的列名将会转换成SQL语句。当然也可以手工生成SQL语句:

>>> peeps=Person.select("""address.id=person.id AND
...                        address.zip LIKE '504%'""",
                        clauseTable=['address'])

注意必须使用clauseTable(子表)来指定子表。如果使用q属性,SQLObject会自动计算出(figure out)所需要使用的扩展信息类。

你也可以使用MyClass.sqlrepr来手工指定任何SQL语句,而在使用q属性时是自动指定的。

还可以使用orderBy关键字创建select语句中的”ORDER BY”。orderBy获取一个字符串,表示数据库的列名,例如Person.q.firstName。也可以使用”-colname”来反向排序。或者 调用MyClass.select().reversed()。

也可以使用类实例的_defaultOrder属性指定缺省的排序列。如果在这时需要获得未排序的结果,使用orderBy=None。

select的结果是一个生成器(generator),可以用于后续调用。所以SQL仅在列出选择结果时才执行,或者使用list()返回所有结 果时。当列举查询结果时,每次取回一个行。这种方法可以在返回结果很大时避免将所有结果放入内存。也可以使用.reversed()而不必获得所有结果实 体,取而代之的是自动修改了SQL语句来获得需要的结果。

还可以对查询结果分片。这将会修改SQL语句,所以peeps[:10]将会把”LIMIT 10″加入SQL语句中。如果切片无法反映到SQL(如peeps[:-10]),则执行查询之后,对查询结果列表进行操作。当然,这只是会出现在使用负索引时。

大多数情况会得到多个查询结果对象。如果不希望这样,可以加入关键字MyClass.select(…,distinct=True),对应SQL中的SELECT DISTINCT。

你也可以通过count得到查询结果的个数,比如MyClass.select().count()。这将会导致一个COUNT(*)查询。这时并不会从数据库中取得对象,而仅仅是获得结果数量。

在少数特别注重效率的时候,效率实际上是依赖于批处理的使用方法。提高排序和查找效率的好办法是使用索引。且缓存比切片更好。

在这种情况下缓存意味着响应所有的结果。可以使用list(MyClass.select(…))来实现。可以在规定的时间内保存查询结果,来让用户分页查看结果。这样,第一次查询会看上去花费更多的时间,但是后面的页面显示却非常快速。

更多关于查询子表的问题参见”SQLBuilder documentation”。

16   selectBy方法

除了.select之外的另一个选择是.selectBy。按照如下工作:

>>> peeps=Person.selectBy(firstName="John",lastName="Doe")

每个关键字对应一个列,且所有的键值对都是以AND进行逻辑连接。返回结果是SelectResult。所以可以切片,计数,排序等等。

17   sqlmeta类

这是在SQLObject 0.7中引入的,允许使用一种清晰的方式指定数据库的元数据,而不需要使用类的命名空间中的属性来指定。

有一些特别的属性可以用在这个类当中,可以用于改变类的行为。他们包括:

  1. table 数据库的表名,是从style派生而来的,仅用于没有指定类名时。如果没有指定名字,且没有定义可选的style,则标准方式是指定MixedCase为mixed_case。
  2. idName 指定数据库表的主键名,如未指定时继承自style。缺省为id。
  3. idType 设置ID时的强制函数。缺省为int。
  4. style 一个样式(style)对象,这个对象允许使用其他算法翻译Python属性和类名称与数据库列名和表名之间的对应关系。参考”Changing the Naming Style”了解更多。它是一个IStyle实例的接口。
  5. lazyUpdate 布尔值,缺省为False。如果为True,那么改变属性时不会自动更新查询。而等待调用inst.syncUpdates()或inst.sync()时才执行更新。
  6. defaultOrder 查询数据时的缺省排序方式。
  7. cacheValues 布尔值,缺省为True。如果为True,保存在行中的值一直缓存到inst.expire()被调用。如果设为False,属性值不会被缓存,所以每次 存取一个属性的值都会使用查询来返回结果。如果需要处理多进程并发处理时,也许需要这样。当然也可以使用事务(transactions),这不是默认 的。
  8. registry SQLObject使用字符串来连接一个类,且这些类必须避开模块名,而又是又会出现在不同的系统中的命名冲突。这个属性提供了类的命名空间。
  9. fromDatabase 布尔值,缺省为False。如果为True,创建一个类时会自动查询数据库的各个列,如果有缺失的列则自动加上。
  10. columns 形如{columnName:anSOColInstance}形式的字典。可以通过只读属性获取列的信息。
  11. columnList columns的列表,用于需要一个有序而牢固的列信息列表时使用。
  12. columnDefinitions 类似columns的字典,但是包含列的原始定义信息。并非是特定类的,且没有逻辑。
  13. joins 这个类的所有联系对象。
  14. indexes 这个类的所有索引。
  15. createSQL 创建表之后的SQL查询。createSQL可以是单行的SQL命令或者是一些SQL命令组成的列表,或者是按照数据库名(dbNames)和值组成的字典,值可以是单行SQL或者是SQL列表。这经常用于ALTER TABLE命令来修改表定义。
  16. expired 布尔值。如果为True,那么下次存取对象列属性时,将会执行查询。

在上一版本的SQLObject中这些属性是作为类的属性而直接存在的,属性名前加上一个下划线。现在推荐将代码改成新的样式(style)。这些旧的方法在SQLObject 0.8释出时将不再支持。

注意:当继承SQLObject时,sqlmeta属性不会继承。也无法通过sqlmeta.columns词典访问父类列对象。

18   使用sqlmeta

按照如下代码:

class MyClass(SQLObject):
    class sqlmeta:
        lazyUpdate=True
        cacheValues=False
    columnA=StringCol()
    columnB=IntCol()
    def _set_attr1(self,value):
        #设置值时需要做的事情
    def _get_attr1(self):
        #取得值时需要作的事情

如上定义将会创建表my_class(表名在更改style属性时会有所不同),包含两个列columnA和columnB。还有第三个可以被存取 的属性MyClass.attr1。sqlmeta改变了MyClass的行为,可以延迟更新,并告知不要使用缓存,所以每次请求信息时都会查询数据库。

19   SQLObject

除了sqlmeta和列规范之外,其他的特定属性可以设置在类中。这些属性可以用除了_connection属性之外都可以在sqlmeta中定义。如果在SQLObject 0.7中使用,可以得到不赞成使用的警告。最好及时修改代码来保持对未来的兼容。

  1. _connection 使用的连接对象,从DBConnection类实现。也可以在包定义中使用__connection__供使用,但是必须确保在类定义之前定义 __connection__。也可以在实例创建时传递connection对象,有如transaction中描述的那样。如果已经定义了 sqlhub.processConnection,则会忽略这个属性而使用sqlhub。如果只有少数几个类使用相同的连接是有好处的,除了 (besides)使用了多种类型。
  2. _table 这是旧样式(style)的属性,等同于sqlmeta类中的table属性。
  3. _joins 同上,对应于sqlmeta的joins属性。
  4. _cacheValues 同上,对应于sqlmeta的cacheValues属性。
  5. _idName 同上,对应于sqlmeta的idName属性。
  6. _style 同上,对应于sqlmeta的style属性。

20   自定义对象

自定义类往往需要一些自定义方法,但是需要注意一些细节。

初始化对象:

有两种方式实例化SQLObject对象,从数据库引出和插入数据库。相同的是都要创建Python对象。这导致了__init__的微笑差异。

一般来说,不需要更改__init__。作为替换的_init方法,可以在引出或插入之后执行。方法定义如同_init(self,id, connection=None,selectResults=None),也许你喜欢使用_init(self,*args,**kw)的形式。注意, 如果重载方法,一定要调用SQLObject._init(self,*args,**kw)。

添加魔术属性:

你可以使用任何已有的技术来定义这种新样式中的方法,包括classmethod(类方法),static(静态方法),和property(属 性),而且还可以使用捷径。比如你有一个方法名以_set_、_get_、_del_、_doc_开始,它将会被用于创建属性。所以,如果对应 Person的ID下包含图像在/var/people/images目录下,可以使用:

class Person(SQLObject):
    # ...
    def imageFilename(self):
        return 'images/person-%s.jpg'%self.id
    def _get_image(self):
        if not os.path.exists(self.imageFilename()):
            return None
        f=open(self.imageFilename())
        v=f.read()
        f.close()
        return v
    def _set_image(self,value):
        f=open(self.imageFilename(),'w')
        f.write(value)
        f.close()
    def _del_image(self,value):
        os.unlink(self.imageFilename())

然后,可以像使用普通属性(attribute)一样使用.image属性(property)。对它的修改也会直接反映到文件系统当中。这是保存无意义数据的好方法。

同样也可以传递一个image关键字参数到构造方法或者set方法,形如Person(…,image=imageText)。所有的方法 (_get_、_set_等)都是可选的,你可以使用其中的任何一个而省略其他的。这样如果只定义了_get_attr方法,那么attr属性就是只读 的。

重载列属性:

重载数据库列属性时有些复杂。例如(for instance),想要在改变一个人名字的时候执行特定代码。在大多数系统中你需要自己实现这个功能,然后再调用父类的代码。但是父类(SQLObject)并不知道子类的列。

SQLObject创建了形如_set_lastName的方法定义你的列,当时当你再次想要使用时却发现父类没有相关引用(即不可以写 SQLObject._set_lastName(…),因为SQLObject类并不知道你的类的列)。你需要自己重载_set_lastName 方法。

为了处理这种问题,SQLObjec类创建了两个方法作为getter和setter,例如:_set_lastName和_SO_set_lastName。所以可以截获所有对lastName的更改:

class Person(SQLObject):
    lastName=StringCol()
    firstName=StringCol()
    def _set_lastName(self,value):
        self.notifyLastNameChange(value)
        self._SO_set_lastName(value)

或者你可能想要包含电话号码的数字,需要限制长度,可以按照如下格式:

import re
class PhoneNumber(SQLObject):
    phoneNumber=StringCol(length=30)
    _garbageCharactersRE=re.compile(r'[\-\.\(\) ]')
    _phoneNumberRE=re.compile(r'^[0-9]+$')
    def _set_phoneNumber(self,value):
        value=self._garbageCharactersRE.sub('',value)
        if not len(value)>=10:
            raise ValueError(
                'Phone numbers must be at least 10 digits long')
        if not self._phoneNumberRE.match(value):
            raise ValueError,'Phone numbers can contain only digits'
        self._SO_set_phoneNumber(value)
    def _get_phoneNumber(self):
        value=self._SO_get_phoneNumber()
        number='(%s) %s-%s'%(value[0:3],value[3:6],value[6:10])
        if len(value) > 10:
            number+=' ext.%s'%value[10:]
        return number

在修改从属性中获得的数据时必须小心。有些时候,人们希望设置与返回的值相同。这个例子中我们在存入数据库之前去除了一些字符,并在取出的时候重新格式化了。这个方法(反对存取属性)的优点之一是程序员往往希望将这些分开。

当然,还应该注意,这些转换在存入和取出时都会发生,但是在查询的时候却不会发生。所以如果你将值从Pythonic形式转换为SQLish形式 时,你的查询(当使用.select()或者.selectBy()方法)需要使用SQL/Database形式(因为这些命令是按照SQL来在数据库上 运行的)。

取消属性定义(Undefined attributes)

还有一个有用的特性,因为你有时需要返回奇怪的结果。SQLObject在你设置一个未定义的属性时不会跑出异常;这很好解释,并且不会改变数据库。他的工作方式有如其他Python类一样,但是在SQLObject类中却没有。

这在有些时候可能会出问题,如果你已经有了一个’name’属性,而你却写了’a.namme=”Victor”‘,这时不会跑出异常,但是却是错误的。

21   参考手册(Reference)

上面的信息可以让你快速进入工作,下面的信息让你定义更加完整。

22   Col类,定义列

列的列表是Col对象的列表。这些对象本身并没有功能,用于定义列。

  1. dbName 数据库的列名,如果不指定你指定的Python名称将会从大小写混用的形式转换到小写加下划线的形式。
  2. default 列的缺省值,在创建一个新行时使用。如果指定了一个可调用对象或函数,将会调用这个函数,并且使用其返回值。所以你可以使用 DateTimeCol.now作为当前时间的缺省值。或者你可以使用sqlbuilder.func.NOW()来设置数据库使用NOW()内部函数。 如果你不指定缺省值,则在调用这个记录的new时会抛出异常。
  3. alternateID 这是一个布尔型变量,缺省为False。指定列是否作为ID属性,例如用户名,尽管并不一定是主键。如果是这样,会添加一个类方法,例如 byUsername将会返回这个对象,使用laternateMethodName,当你希望使用by*类似的名称时,例如 alternateMethodName=”username”。这个列将会被声明为UNIQUE。
  4. unique 如果为True,当SQLObject创建一个表格时,将会指定这个列为UNIQUE。
  5. notNone 如果为True,则这个列不允许使用空值,用于创建表格。
  6. sqlType 这个列的SQL类型,例如INT、BOOLEAN等。你可以使用下面的类来定义,但是有时候使用sqlType更容易一些。仅在SQLObject创建表格时有效。

23   列类型

ForeignKey类可以替换Col来使用,当列是其他表的外键时。一般使用方法如ForeignKey(‘Role’),在这个列子中创建了一 个到表Role的引用。这基本等同于Col(foreignKey=’Role’,ssqlType=’INT’)。这会创建两个属性,role,会返回 Role的实例;roleID会返回与之关联的role的整数ID。

Col还有一些其他子类,用于SQLObject创建表格时指示列的类型。

  1. BLOBCol 二进制数据列,目前只能在MySQL、PostgreSQL、SQLite中使用。

  2. BoolCol 创建一个BOOLEAN列在Postgre,或者INT在其他数据库中。而且会将”t”/”f”或者0/1转换到数据库后端。

  3. CurrencyCol 等同于DecimalCol(size=10,precision=2)。注意DecimalCol可能不会返回正确的值,这个列可能共享一些行为。注意阅读DecimalCol的注意事项。

  4. DateTimeCol 日期时间,一般返回datetime或mxDateTime对象。

  5. DateCol 一个日期对象,一般返回datetime或mxDateTime对象。

  6. TimeCol 一个日期对象,一般返回datetime或mxDateTime对象。

  7. DecimalCol 以10为基础的,正确数据,使用关键字参数size指定存储的数字位数,precision指定小数点位数。警告:偶尔会发生在DecimalCol值, 尽管正确的存入数据库,但是可能返回浮点数而不是decimals。你可以自己测试一下,也可以试着导入Decimal类型,在你的数据库适配器导入 SQLObject之前。

  8. EnumCol 枚举类型,包含有限个数的字符串值。给出列表中可能的字符串,依靠enumValues关键字参数。MySQL有内置的本地ENUM类型,但是在其他数据库中也可以工作,只不过效率上并不占优势。

  9. FloatCol 浮点数

  10. ForeignKey 其他表/类的外键,例如user=ForeignKey(‘User’)

  11. IntCol 整数

  12. PickleCol 一种扩展的BLOBCol,这个列可以存储/取出任何Python对象;实际上是使用了Python的pickle来对对象串行化的压缩和解压缩的,而最终存取的是字符串。

  13. StringCol 一个字符串列。String(character)列。扩展关键字如下:

    length:如果给定了则字段类似于VARCHAR(length)。如果未指定则使用TEXT字段类型。

    varchar:如果包含了length,则用于区别CHAR和VARCHAR,缺省为True,使用VARCHAR。

  14. UnicodeCol StringCol的子类,接受dbEncoding关键字参数,缺省为”UTF-8″。其值在存取数据库的过程中被编码和解码。在使用UnicodeCol进行查询时有些限制:

    只有简单的q-magic字段支持,不允许表达式;只支持”==”和”<>”操作符。

    如下为示例代码:

    MyTable.select(u'value'==MyTable.q.name)
    MyTable.select(MyTable.q.name<>u'value')
    MyTable.select(OR(MyTable.q.col1==u'value1',MyTable.q.col2<>u'value2'))
    MyTable.selectBy(name=u'value')
    MyTable.selectBy(col1=u'value1',col2=u'value2')
    MyTable.byCol1(u'value1') #假设col1是一个alternetID字段

    如下为错误代码:

    MyTable.select((MyTable.q.name+MyTable.q.surname)==u'value')

    如下情况,必须先转换编码:

    MyTable.select((MyTable.q.name+MyTable.q.surname)==u'value'.encode(dbEncoding))

24   两个类/表之间的关系

必须使用ForeignKey来处理表的外键,后台实际使用连接(join)。

25   多连接和SQL多连接:一对多

查看”One-to-Many关系”查看一对多连接的例子。

多连接(MultipleJoin)返回结果类表,而SQLMultipleJoin返回SelectResults对象。

少数关键字参数允许MultipleJoin构造器:

joinColumn:

列名

subprocess模块指南

Monday, June 2nd, 2008

subprocess模块指南

翻译: gashero

目录

从Python2.4开始引入。

subprocess模块用于产生一个新的进程,并且连接到其input/output/error管道,并获取返回码。这个模块准备用于替换很多其他旧模块中的函数,例如 os.system 、 os.spawn* 、 os.popen* 、 popen2.* 、 commands.* 。

详细细节见下面。

1   使用subprocess模块

模块中只是定义了一个类叫做 Popen

class Popen(args,bufsize=0,executable=None,stdin=None,stdout=None,stderr=None,preexec_fn=None,close_fds=False,shell=False,cwd=None,env=None,universal_newlines=False,startupinfo=None,creationflags=0)

参数描述如下:

@waiting…

1.1   便利函数

该模块定义了两个快捷函数:

call(*popenargs,**kwargs)

使用指定的参数运行命令,并等待命令结束,然后返回返回码。参数与Popen结构相同,例如:

retcode=call(["ls","-l"])

check_call(*popenargs,**kwargs)

使用指定参数运行命令,等待结束。如果退出码是0则返回,否则抛出 CalledProcessError 异常。而该异常对象包含了返回码,称为 returncode 属性。

参数与Popen结构相同,例如:

check_call(["ls","-l"])

从Python2.5开始引入。

1.2   异常

子进程中抛出的异常可以传递到父进程。另外,异常对象包含一个附加属性叫做 child_traceback ,是一个字符串,包含了子进程那里可以看到的错误回调信息。

最常见的异常是 OSError 。例如尝试执行一个不存在的文件。应用应该防备OSError异常。

在调用Popen而使用了无效的参数时会抛出ValueError异常。

check_call() 将会抛出 CalledProcessError ,在进程返回码不是0时。

1.3   安全

不像其他popen函数,这个实现不会隐含调用 /bin/sh 。这意味着所有的字符,甚至shell的元字符,都可以安全的传递到子进程。

2   popen对象

Popen的实例拥有如下方法:

poll()

检查子进程是否结束,并返回returncode属性。

wait()

等待子进程结束,返回returncode属性。

communicate(input=None)

与进程交互:发送数据岛stdin。从stdout和stderr读取数据,直到接收到end-of-file。等待进程结束。可选的input参数是要发送到子进程的字符串,或者为None。

communicate() 返回元组 (stdout,stderr) 。

注意:读取的数据缓存在内存里,所以在数据很大或者不受限制时不要使用这个方法。

如下属性也可用:

stdin

如果stdin参数为PIPE,这个属性提供了子进程的标准输入,否则为None。

stdout

如果stdout参数为PIPE,……

stderr

如果stderr参数为PIPE,……

pid

子进程的进程ID。

returncode

子进程的返回码,在还没有返回时则为None。一个否定值-N表示子进程是被信号N所终止的(仅Unix)。

3   使用subprocess替换旧模块

TCPDUMP-3.9.8安装过程的一点问题记录

Monday, June 2nd, 2008

TCPDUMP-3.9.8安装过程的一点问题记录

安装时没有默认加上动态连接库的辅助库的静态连接参数,导致连接时找不到一些符号(假设地球人可以听懂)。所以需要加上这个静态连接库,实际的修改方法是打开 “Makefile” 文件,在55行处的最后加上 “-ldl” 。然后编译即可通过。

运行时需要有root权限,而且相关命令是在 “/usr/local/sbin” 下面的。所以先切换到root再说。

购买数码相机过程中所遇到的经济学行为

Sunday, June 1st, 2008

购买数码相机过程中所遇到的经济学行为

捏哈哈哈,估计订阅我博客的人都快要气疯了。都是为了研究erlang/python的朋友们看我在这里忽悠经济学。

下周末要去看一个大学同学,路程较远,另外以前的相机不好用了也该另外买个了。研究了很久以后备选方案有canon A720 IS和olympus u840。再次研究过后决定买u840。(愤青勿扰,我上个相机是清华紫光的,给了我严重的教训,另外我直接避开了尼康)。

从概率上讲,一个淘宝店铺卖家的信用越高越值得信赖,不过价格也会略高。一般来说我会在信用与价格之间找一个平衡点。另外,为了便于保修、扯皮和验货,我一般只选在上海的货源。所以在淘宝搜索并加限制条件:“u840 上海 价格排序”。

这时得到的结果除去相机包等不靠谱的东西以后,价格最低的3家店铺就浮出水面了。后来证实其中的一家联系不到,另外其信用也不是很高。剩余的两家店铺一个是四钻,一个是五钻,信用方面基本不是问题了。只是其价格实在有些夸张,一个是1150,另一个是1225。仔细研读其商品介绍发现如下行为:

1、标价根本就不是拿来卖的,是用来忽悠人的。那个价格不包含任何配件,而且是日版水货。就算你有胆量真的拍下来了,对方也有勇气跟你赖账。那个价格卖家是不会卖的,仅仅用来吸引消费者进来看看。
2、实际出售的往往是套餐,一般有2-3个套餐,包含全套的其他配件,而且较为实用。另外,套餐可定制。
3、套餐中山寨货成堆。存储卡、电池、相机包等等这些东西往往全都是山寨货。而且也就是这些山寨货套餐占了400元左右的价格。让套餐的总售价重新回到卖家心理的预期售价。
4、定制套餐中的各类配件价格也要比其他单独卖的店铺价格高,往往比单独出售店铺中该配件加上邮费的价格都更高。

关于店铺的行为:

1、大的淘宝卖家往往有不只一个网店,甚至不只一个实体店。拥有实体店的卖家生意相对好做一些。
2、上海的相当一部分卖家没有实体店,甚至没有库存,在网店中拍卖成功的商品由深圳发货。此类店的规模很小,甚至有些还是业余的。但是信用未必很低,甚至会出现五钻以上的卖家是这种皮包公司。
3、大的卖家会在几个大城市开设分店,这些店提供当地的发货、保修服务等。
4、同行业的几个大的卖家一般关系未必是竞争关系,甚至会形成类似卡特尔的组织。比如研究发现上面的最后剩余两家网店总部都在深圳,却在上海只有一个实体店,代理这两家网店的维修服务。
5、深圳、广州等地的商品价格很低,上海最高,北京比上海略低,不过也比深圳高很多。所以以上海实体店为幌子要高价,却从深圳发货,就可以获得较高的利润。像一些大的店铺提供几个大城市的实体店保修则获得更高的信用能力。
6、把该店铺历史上曾经有过的所有商品都挂到网上去,并不管仓储的问题。要求卖家在购买之前确认是否有货,减轻了店主的管理成本。

水货与保修:

1、利润来自于非正规渠道。比如水货、港行等等。网店的价格比实体店要低很大一截,跟大商场的价格更远远不是同一个起跑线上,这就是关税、店铺成本等等造成的。
2、水货没有保修,为了便于销售,提供店铺保修。但是实际上比较大的网店一般号称的店铺保修是不包括更换元件费用的。其实免费保修的范围小到不能再小了。
3、为了提高用户信心,部分店铺提供了反向销售方式提供保修的保障。保修时由客户在网上拍卖物品,商家付款买回,但是不确认收货;拿去修理后寄回用户,用户关闭交易。在这个过程中,确保物品的安全性。