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章节文档的更多相关文章

  1. 《Introduction to Tornado》中文翻译计划——第五章:异步Web服务

    http://www.pythoner.com/294.html 本文为<Introduction to Tornado>中文翻译,将在https://github.com/alioth3 ...

  2. 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  3. 翻译连载 | 第 10 章:异步的函数式(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  4. 翻译连载 | 第 10 章:异步的函数式(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  5. 翻译连载 | 第 11 章:融会贯通 -《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  6. Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  7. Gradle 1.12 翻译——第十六章. 使用文件

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  8. Gradle 1.12用户指南翻译——第五十三章. 签名插件

    其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上的地址:https://gith ...

  9. Gradle 1.12用户指南翻译——第四十七章. Build Init 插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

随机推荐

  1. 避免Block的循环引用

    避免Block的循环引用 什么是循环引用,什么时候发生循环引用 1 循环引用就是当self 拥有一个block的时候,在block 又调用self的方法.形成你中有我,我中有你,谁都无法将谁释放的困局 ...

  2. Yii数据库

    http://www.yiiframework.com/doc/guide/1.1/zh_cn/database.ar http://zhengdl126.iteye.com/blog/1460779 ...

  3. ZOJ 2760 How Many Shortest Path

    题目大意:给定一个带权有向图G=(V, E)和源点s.汇点t,问s-t边不相交最短路最多有几条.(1 <= N <= 100) 题解:从源点汇点各跑一次Dij,然后对于每一条边(u,v)如 ...

  4. 我学hash_map(2)

    啊,转眼之间就来到了我学hash_map(2)了.我们也从hash_map转移到了unordered_map上来了,今天这个文章的目的就是要来分享一下使用这个hash_map,哦不,unordered ...

  5. jsp中pageEncoding、charset=UTF -8

    jsp中pageEncoding.charset=UTF -8" 在JSP/Servlet  中主要有以下几个地方可以设置编码,pageEncoding="UTF-8". ...

  6. hdu-3790最短路径问题

    Problem Description 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的.   Inp ...

  7. velocity自定义动画

         话说好久没有写博客了,零星的整理了一些东西,没有形成系统,所以也没有在这里记录.        废话不多说了,进入今天的正题.不知道大家是否记得之前写过的一篇文章<制作炫酷的专题页面& ...

  8. JS-Array数组内置对象

    直接上代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <ti ...

  9. MYSQL常用操作函数的封装

    1.mysql常用函数封装文件:mysql.func.php <?php /** * 连接MYSQL函数 * @param string $host * @param string $usern ...

  10. Django 内置分页--Paginator类

    官方文档 http://python.usyiyi.cn/django/topics/pagination.html 前端方法 http://www.tuicool.com/articles/RniU ...