[SQLObject官方文档] 联系与查询

·一对多联系
就是需要建立外键。例如一个地址本类,需要建立外键对应到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对象属性等同于使用(by gashero)类方法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。

·多对多联系
这个例子中包含用户(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中新引入的特性(by gashero)。查看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'>

·选择多个对象(查询)
查询才是真正有用的东西,比显示联系重要的多。select是一个类方法,可以按照如下方式使用:
>>> Person._connection.debug=True
>>> peeps=Person.select(Person.q.firstName=="John")
>>> list(peeps) #by gashero
 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"。

·selectBy方法
除了.select之外的另一个选择是.selectBy。按照如下工作:
>>> peeps=Person.selectBy(firstName="John",lastName="Doe")
每个关键字对应一个列,且所有的键值对都是以AND进行逻辑连接。返回结果是SelectResult。所以可以切片,计数,排序等等。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值