(翻译玩)SQLALchemy backref章节文档
Linking Relationships with Backref
自从在Object Relational Tutorial中第一次提到backref参数后,许多案例中也用到了backref,那么他到底做了什么?让我们从典型的"用户-地址"模型来探究吧.
from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship("Address", backref="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
email = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
上面的代码在User建立addresses属性,可以通过User.addresses查询对应的Address对象集合的.同样也在Address中建立了用于查询User对象的user属性.
实际上,backref关键字只是一个建立关系的快捷方式,他在Address中建立了关系映射,including the establishment of an event listener on both sides which will mirror attribute operations in both directions.上面的代码等价于:
from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship("Address", back_populates="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
email = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship("User", back_populates="addresses")
我们明确的在Address表中添加了.user关系,在两个关系中,back_populates明确的向另一边表明另一边需要在两表之间建立一个双向的联系.
这些代码主要做的是当append事件或者set事件发生时,把自己设置到对应对象的某个属性中.下面将用User-Address来简单阐明这一行为,address属性和user属性现在为空
>>> u1 = User()
>>> a1 = Address()
>>> u1.addresses
[]
>>> print a1.user
None
然而,当Address插入到u1.addresses中时,u1.addresses属性和a1.user属性同时被添加了内容
>>> u1.addresses.append(a1)
>>> u1.addresses
[<__main__.Address object at 0x12a6ed0>]
>>> a1.user
<__main__.User object at 0x12a6590>
反过来,设置a1.user时也一样,对两边的属性设置效果是等价.例如,当设置a1.user为None时,Address对象也同样从User的addresses中移除了.
>>> a1.user = None
>>> u1.addresses
[]
对.addresses属性和.user属性的操作完全是通过python完成的,而没有通过SQL交互环境操作,不然的话the proper state would be apparent on both sides once the data has been flushed to the database, and later reloaded after a commit or expiration operation occurs. backref/back_populates的优点是操作后不需要往返数据库就可以正确的反应出正确的状态
记住,backref关键字单独的用在一边和在关系两边使用back_populates关键字,效果是完全等价的
Backref Arguments
我们已经确定了backref关键字仅仅只是建立两个单独relationship()的快捷方式,设置在一个relationship()中的barkref配置将会自动设置在另一边.也就是说,两边的关系设置不太可能有差异. 一般情况,有secondary属性的多对多关系,或者是有primaryjoin属性的一对多,多对一关系(primaryjoin属性在Specifying Alternate Join Conditions中讨论过).Such as if we limited the list of Address objects to those which start with “tony”:
from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship("Address",
primaryjoin="and_(User.id==Address.user_id, "
"Address.email.startswith('tony'))",
backref="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
email = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
We can observe, by inspecting the resulting property, that both sides of the relationship have this join condition applied:
>>> print User.addresses.property.primaryjoin
"user".id = address.user_id AND address.email LIKE :email_1 || '%%'
>>>
>>> print Address.user.property.primaryjoin
"user".id = address.user_id AND address.email LIKE :email_1 || '%%'
>>>
This reuse of arguments should pretty much do the “right thing” - it uses only arguments that are applicable, and in the case of a many-to- many relationship, will reverse the usage of primaryjoin and secondaryjoin to correspond to the other direction (see the example in Self-Referential Many-to-Many Relationship for this).
It’s very often the case however that we’d like to specify arguments that are specific to just the side where we happened to place the “backref”. This includes relationship() arguments like lazy, remote_side, cascade and cascade_backrefs. For this case we use the backref() function in place of a string:
from sqlalchemy.orm import backref
class User(Base):
tablename = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship("Address",
backref=backref("user", lazy="joined"))
Where above, we placed a lazy="joined" directive only on the Address.user side, indicating that when a query against Address is made, a join to the User entity should be made automatically which will populate the .user attribute of each returned Address. The backref() function formatted the arguments we gave it into a form that is interpreted by the receiving relationship() as additional arguments to be applied to the new relationship it creates.
One Way Backrefs
An unusual case is that of the “one way backref”. This is where the “back-populating” behavior of the backref is only desirable in one direction. An example of this is a collection which contains a filtering primaryjoin condition. We’d like to append items to this collection as needed, and have them populate the “parent” object on the incoming object. However, we’d also like to have items that are not part of the collection, but still have the same “parent” association - these items should never be in the collection.
Taking our previous example, where we established a primaryjoin that limited the collection only to Address objects whose email address started with the word tony, the usual backref behavior is that all items populate in both directions. We wouldn’t want this behavior for a case like the following:
>>> u1 = User()
>>> a1 = Address(email='mary')
>>> a1.user = u1
>>> u1.addresses
[<__main__.Address object at 0x1411910>]
Above, the Address object that doesn’t match the criterion of “starts with ‘tony’” is present in the addresses collection of u1. After these objects are flushed, the transaction committed and their attributes expired for a re-load, the addresses collection will hit the database on next access and no longer have this Address object present, due to the filtering condition. But we can do away with this unwanted side of the “backref” behavior on the Python side by using two separate relationship() constructs, placing back_populates only on one side:
from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship("Address",
primaryjoin="and_(User.id==Address.user_id, "
"Address.email.startswith('tony'))",
back_populates="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
email = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship("User")
With the above scenario, appending an Address object to
the .addresses collection of a User will always establish the .user attribute on that Address:
>>> u1 = User()
>>> a1 = Address(email='tony')
>>> u1.addresses.append(a1)
>>> a1.user
<__main__.User object at 0x1411850>
However, applying a User to the .user attribute of an Address, will not append the Address object to the collection:
>>> a2 = Address(email='mary')
>>> a2.user = u1
>>> a2 in u1.addresses
False
Of course, we’ve disabled some of the usefulness of backref here, in that when we do append an Address that corresponds to the criteria of email.startswith('tony'), it won’t show up in the User.addresses collection until the session is flushed, and the attributes reloaded after a commit or expire operation. While we could consider an attribute event that checks this criterion in Python, this starts to cross the line of duplicating too much SQL behavior in Python. The backref behavior itself is only a slight transgression of this philosophy - SQLAlchemy tries to keep these to a minimum overall.
(翻译玩)SQLALchemy backref章节文档的更多相关文章
- 《Introduction to Tornado》中文翻译计划——第五章:异步Web服务
http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth3 ...
- 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- 翻译连载 | 第 10 章:异步的函数式(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- 翻译连载 | 第 10 章:异步的函数式(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- 翻译连载 | 第 11 章:融会贯通 -《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
- Gradle 1.12 翻译——第十六章. 使用文件
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
- Gradle 1.12用户指南翻译——第五十三章. 签名插件
其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上的地址:https://gith ...
- Gradle 1.12用户指南翻译——第四十七章. Build Init 插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
随机推荐
- P1337 fibonacci数列(tyvj)
http://www.tyvj.cn/p/1337 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 著名的斐波那契数列f[n]=1 ...
- ios app 开发中ipa重新签名步骤介绍-备
作为一个app应用程序开发者,在app应用程序在苹果商店上架前总需要将安装包安装到ios机器上进行测试,这个时候我们就需要打包in house版本的ipa了,打包in house实际上是一个将ipa应 ...
- [POJ] 3020 Antenna Placement(二分图最大匹配)
题目地址:http://poj.org/problem?id=3020 输入一个字符矩阵,'*'可行,'o'不可行.因为一个点可以和上下左右四个方向的一个可行点组成一个集合,所以对图进行黑白染色(每个 ...
- 关于Bayes网络新解
经典贝叶斯网络 贝叶斯分类器的分类原理是通过某对象的先验概率,利用贝叶斯公式计算出其后验概率,即该对象属于某一类的概率,选择具有最大后验概率的类作为该对象所属的类.目前研究较多的贝叶斯分类器主要有四种 ...
- 转:ASCII码表_全_完整版
ASCII码表 ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符 0 NUL 32 (space) 64 @ 96 . 1 SOH 33 ! 65 A 9 ...
- PHP常用字符串函数
1:访问子字符串:strpos()2:提取子字符串:substr()3:逐字节处理字符串:strstr()4:计算字符串长度:strlen()5:替换子字符串:substr_replace()6:按字 ...
- oschina BI商业智能开源软件
54款 BI商业智能开源软件 MySQL数据仓库解决方案 Infobright OLAP 分析引擎 Apache Kylin 数据处理和分发系统 Apache NiFi OLAP 数据查询引擎 Dru ...
- 【转】VS2013中如何解决error C4996: 'fopen'问题
原文网址:http://jingyan.baidu.com/article/ce436649fd61543773afd32e.html 今天编写控制台应用程序时出现如下错误 error C4996: ...
- lesson5:利用jmeter来压测消息队列(activemq)
本文讲述了利用jmeter来压测消息队列,其中消息队列采用apache的activemq,jmeter本身是支持符合jms标准消息队列的压测,由于jmeter的官方sampler配置比较复杂,本文直接 ...
- POJ 2697 A Board Game (bfs模拟)
比较水的一道题,在4*4的棋盘上有黑白子,现在有某种移动方式,问能否通过它将棋盘从某个状态移动到另一种状态 只要想好怎么保存hash表来去重,其他就差不多了... #include <iostr ...