在使用 Python 和 SQLAlchemy 时,结合外键映射可以让你在查询时轻松地获取其他表中的数据。SQLAlchemy 提供了丰富的 ORM(对象关系映射)功能,可以让你通过定义外键关系来查询并获取关联的数据。下面我会演示如何设置外键关系,并通过 SQLAlchemy 查询获取其他表中的数据。
1、问题背景
在使用 SQLAlchemy 进行对象关系映射时,我们可能需要获取其他表中的数据。例如,我们有一个 Customer 表和一个 Order 表,Customer 表中有 uid、name 和 email 字段,Order 表中有 item_id、item_name 和 customer 字段,customer 字段是 Customer 表的 uid 字段的外键。现在,我们希望从 Order 表中查询订单信息时,同时获取该订单所属客户的姓名和电子邮件地址。
2、解决方案
2.1 双向关系映射
为了实现上述目的,我们需要在 Customer 和 Order 类中分别定义关系属性,使用 relationship() 方法。relationship() 方法的第一个参数指定要映射的类,第二个参数指定反向引用属性。在我们的例子中,Customer 类中的 orders 属性表示该客户的所有订单,Order 类中的 customer 属性表示该订单所属的客户。
python">from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import mapper, relationship, Session
from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()class Customer(Base):__tablename__ = 'customers'uid = Column(Integer, primary_key=True)name = Column(String)email = Column(String)def __repr__(self):return str(self)def __str__(self):return "Cust: %s, Name: %s (Email: %s)" %(self.uid, self.name, self.email)class Order(Base):__tablename__ = 'orders'item_id = Column(Integer, primary_key=True)item_name = Column(String)customer_id = Column(Integer, ForeignKey('customers.uid'))def __repr__(self):return str(self)def __str__(self):return "Item ID %s: %s, has been ordered by customer no. %s" %(self.item_id, self.item_name, self.customer)# SQLAlchemy database transmutation
engine = create_engine('sqlite:///:memory:', echo=False)
metadata = MetaData()metadata.create_all(engine)
mapper(Customer, customers_table, properties={'orders': relationship(Order, backref='customer')
})
mapper(Order, orders_table)session = Session(engine)
for order in session.query(Order):print(order, order.customer)
这样,我们就可以通过 Order 对象获取该订单所属客户的信息。
2.2 单向关系映射
如果我们只需要从 Order 表中获取客户信息,而不需要从 Customer 表中获取订单信息,那么我们可以使用单向关系映射。单向关系映射只需要在 Order 类中定义关系属性,不需要在 Customer 类中定义。
python">from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import mapper, relationship, Session
from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()class Customer(Base):__tablename__ = 'customers'uid = Column(Integer, primary_key=True)name = Column(String)email = Column(String)def __repr__(self):return str(self)def __str__(self):return "Cust: %s, Name: %s (Email: %s)" %(self.uid, self.name, self.email)class Order(Base):__tablename__ = 'orders'item_id = Column(Integer, primary_key=True)item_name = Column(String)customer_id = Column(Integer, ForeignKey('customers.uid'))def __repr__(self):return str(self)def __str__(self):return "Item ID %s: %s, has been ordered by customer no. %s" %(self.item_id, self.item_name, self.customer)# SQLAlchemy database transmutation
engine = create_engine('sqlite:///:memory:', echo=False)
metadata = MetaData()metadata.create_all(engine)
mapper(Customer, customers_table)
mapper(Order, orders_table, properties={'customer': relationship(Customer)
})session = Session(engine)
for order in session.query(Order):print(order, order.customer)
这样,我们就可以通过 Order 对象获取该订单所属客户的信息,但不能通过 Customer 对象获取该客户的所有订单。
2.3 添加另一个外键
如果我们需要在 Order 表中添加另一个外键,例如 product_id 字段,并且希望获取该订单所属产品的信息,那么我们可以在 Order 类中定义一个新的关系属性,使用 relationship() 方法。relationship() 方法的第一个参数指定要映射的类,第二个参数指定反向引用属性。在我们的例子中,Order 类中的 product 属性表示该订单所属的产品。
python">from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import mapper, relationship, Session
from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()class Customer(Base):__tablename__ = 'customers'uid = Column(Integer, primary_key=True)name = Column(String)email = Column(String)def __repr__(self):return str(self)def __str__(self):return "Cust: %s, Name: %s (Email: %s)" %(self.uid, self.name, self.email)class Order(Base):__tablename__ = 'orders'item_id = Column(Integer, primary_key=True)item_name = Column(String)customer_id = Column(Integer, ForeignKey('customers.uid'))product_id = Column(Integer, ForeignKey('products.pid'))def __repr__(self):return str(self)def __str__(self):return "Item ID %s: %s, has been ordered by customer no. %s" %(self.item_id, self.item_name, self.customer)class Product(Base):__tablename__ = 'products'pid = Column(Integer, primary_key=True)product_name = Column(String)def __repr__(self):return str(self)def __str__(self):return "Product ID %s: %s" %(self.pid, self.product_name)# SQLAlchemy database transmutation
engine = create_engine('sqlite:///:memory:', echo=False)
metadata = MetaData()metadata.create_all(engine)
mapper(Customer, customers_table, properties={'orders': relationship(Order, backref='customer')
})
mapper(Order, orders_table, properties={'customer': relationship(Customer),'product': relationship(Product)
})
mapper(Product, products_table)session = Session(engine)
for order in session.query(Order):print(order, order.customer, order.product)
这样,我们就可以通过 Order 对象获取该订单所属客户和产品的信息。
总结
结合外键映射,你可以通过 SQLAlchemy 轻松地获取不同表之间关联的数据。你可以使用:
relationship
:设置表之间的关系(如外键),并通过 ORM 获取关联的数据。- 联接查询 (
joinedload
):通过联接查询加载关联数据,提高查询效率。 - 直接访问外键列:直接访问与外键相关的表格数据。
这些方法结合起来,使得 SQLAlchemy 的 ORM 功能非常强大且灵活,能够满足大部分关联查询需求。