Python SQLAlchemy ORM教程(4)

由于SQLAlchemy 中文资料比较少,所以根据官网给的tutorial外加其他大佬写的中文资料整合以后准备写一个SQLAlchemy 系列的基础入门教程。本系列可能会夹杂一些个人对于python 、SQLAlchemy 以及ORM的理解,如有出错部分,请指正我。

版本信息:

  • SQLAlchemy 1.2.15
  • Python 3.6+
  • Mac OS 10.14
  • DB基于SQLite

前序文章Python SQLAlchemy ORM教程(3)已经讲完了,关于使用SQLAlchemy接口进行查询的的操作,这章将着重讲数据库的关系怎么用Python代码表达出来。

Building a Relationship

我们考虑一下,为用户添加额外的表,用来储存用户的邮箱地址。一个用户可能有多个邮箱地址。这里面有一个对应的一对多的数据关系,我们将给users表创建一个与之关联的新表叫addresses用来储存与用户相关的邮箱。

>>> from sqlalchemy import ForeignKey
>>> from sqlalchemy.orm import relationship

>>> class Address(Base):
...     __tablename__ = 'addresses'
...     id = Column(Integer, primary_key=True)
...     email_address = Column(String, nullable=False)
...     user_id = Column(Integer, ForeignKey('users.id'))
...
...     user = relationship("User", back_populates="addresses")
...
...     def __repr__(self):
...         return "" % self.email_address
>>> User.addresses = relationship(
...     "Address", order_by=Address.id, back_populates="user")

我们需要在Address表中设立外键ForeignKey,传入格式为表名字.表属性Address映射类应该链接到User映射类,所以我们需要使用relationship()这个函数来建立关联。所以我们决定让Address.user来表达AddressUser的关系。relationship()Foreignkey 的使用决定了Adress.user 将会是多对一的关系。

值得注意的是我们也对User类使用了relationship()来定义UserAddress 的关系。在两个类中的relationship()函数内我们给back_populates这个参数分配了值,这个参数的值是想要与之发生关连的表的表名。当你设置了back_populates这个参数后,SQLAlchemy会智能的决定需要连接到哪个表。在这种情况下Address.user 会链接到User实例,User.address也会链接到Address实例。

因为翻译水平的原因可能上段不太能看懂。

我用大白话加上自己的理解说一下。在数据库中,当我们定义一对多的关系的时候,外键是必须的,这个不再赘述。但是由于我们在用Python 代码表达数据库的东西并且在此基础上加入了更多ORM的设计理念,让你的表类以后更好修改,关系更加明晰。也为了让你数据的查询更加方便快捷。所以SQLAlchemy引入了relationship()函数来表述类之间的数据关系。使用两个relationshup()函数并且定义back_populates也是为了反向引用。其实不使用两个relationship也没关系,也同样的可以定义一对多的关系,具体也不再赘述,这个下一个章节会讲留个链接。根据SQLAlchemy的官方解释说这是一种"bidirectional behavior" 双向行为。主要是的影响就是relationship()会返回一个event handler· 给这个属性,当你向这个event handler插入数据的时候,其他与相关联的set也会被插入数据

我们看一个例子

>>> u1 = User()
>>> a1 = Address()
>>> u1.addresses
[]
>>> print(a1.user)
None

当我们将Address插入到u1.addresses中的时候,与之先关的集合也会被插入数据

>>> u1.addresses.append(a1)
>>> u1.addresses
[<__main__.Address object at 0x12a6ed0>]
>>> a1.user
<__main__.User object at 0x12a6590>

当我们把a1.user设置为None的时候u1.addresses内的内容也会被清空

>>> a1.user = None
>>> u1.addresses
[]

英语好的同学可以自己看下官方的解释Linking Relationships with Backref

Working with Related Objects

现在我们可以创建一个User实例,然后一个空的address数组将会被创建。你可以自己定义你想穿件的数据类型,字典,集合,数组都可以,具体参看这个Customizing Collection Access,但是默认的是数组list

>>> jack = User(name='jack', fullname='Jack Bean', password='gjffdd')
>>> jack.addresses
[]

我们可以往Address对象内加入User对象。我们只需要往.addresses内加入内容就行

>>> jack.addresses = [
...                 Address(email_address='[email protected]'),
...                 Address(email_address='[email protected]')]

当我们使用了双向行为,你将元素加在任何一边,他都会自动的帮你在另外一个生成对应的数据。

>>> jack.addresses[1]


>>> jack.addresses[1].user

最后我们将数据加入到数据库内

>>> session.add(jack)
>>> session.commit()

Querying with Joins

因为我自己不是太懂join的操作,所以仅仅做一些翻译

我们有了两张互相关联的数据表了,我们可以使用一些更加复杂的查询技术了。

我们可以使用filter来实现我们想要的查询:

>>> for u, a in session.query(User, Address).\
...                     filter(User.id==Address.user_id).\
...                     filter(Address.email_address=='[email protected]').\
...                     all():
...     print(u)
...     print(a)


以上查询本质上就是一次join的查询,所以我们可以直接使用Query.join()方法来查询我们想要的结果

>>> session.query(User).join(Address).\
...         filter(Address.email_address=='[email protected]').\
...         all()
[]

在上面的例子中由于只存在一个ForeignKey,Query.join知道如何选取合适的列进行JOIN。如果没有定义ForeignKey,或者存在多个,此时你需要手动指明你参与JOIN的列。Query.join()以如下方式进行:

query.join(Address, User.id==Address.user_id)    # explicit condition
query.join(User.addresses)                       # specify relationship from left to right
query.join(Address, User.addresses)              # same, with explicit target
query.join('addresses')

以下还有非常多的内容,包括Using Aliases,Using Subqueries等等,具体可以看(Object Relational Tutorial)[https://docs.sqlalchemy.org/en/latest/orm/tutorial.html#building-a-relationship].

因为本身是面对新手,所以不太想往下写很深的内容,基本的查询已经可以满足,如果希望我把内容翻译完的,评论告诉我。

你可能感兴趣的:(Python SQLAlchemy ORM教程(4))