7.2 KiB
SQLAlchemy 表达式语言,更高级的用法
原文:https://www.pythoncentral.io/sqlalchemy-expression-language-advanced-usage/
概观
在上一篇文章 SQLAlchemy 表达式语言,高级用法中,我们通过包含User、ShoppingCart和Product的三表数据库了解了 SQLAlchemy 表达式语言的强大功能。在本文中,我们将回顾 SQLAlchemy 中物化路径的概念,并使用它来实现产品包含关系,其中某些产品可能包含其他产品。例如,DSLR 相机包是一种产品,其可以包含主体、三脚架、镜头和一组清洁工具,而主体、三脚架、镜头和该组清洁工具中的每一个也是一种产品。在这种情况下,DSLR 相机包产品包含其他产品。
物化路径
Materialized Path是一种在关系数据库中存储分层数据结构(通常是树)的方法。它可以用来处理数据库中任何类型的实体之间的层次关系。sqlamp是一个第三方 SQLAlchemy 库,我们将使用它来演示如何建立一个包含基于关系的分层数据结构的产品。要安装sqlamp,在您的 shell 中运行以下命令:
$ pip install sqlamp
Downloading/unpacking sqlamp
...
Successfully installed sqlamp
Cleaning up...
首先,让我们回顾一下我们在上一篇文章中所做的事情。
from sqlalchemy import Column, DateTime, String, Integer, ForeignKey, Float
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
类 User(Base):
_ _ tablename _ _ = ' User '
id = Column(Integer,primary _ key = True)
name = Column(String)
class shopping cart(Base):
_ _ tablename _ _ = ' shopping _ cart '
id = Column(Integer,primary _ key = True)
owner _ id = Column(Integer,foreign key(User . id))
owner = relationship(
User,backref = backref(' shopping _ carts ',use list = True)
)
products = relationship(
' Product ',
secondary = ' shopping _ cart _ Product _ link '
)
defr})”。格式(购物车、自助)
class Product(Base):
_ _ tablename _ _ = ' Product '
id = Column(Integer,primary _ key = True)
name = Column(String)
#使用浮点数不是对货币值建模的正确方式。
#我们将在另一篇文章中探讨这个话题。
price = Column(Float)
shopping _ carts = relationship(
' shopping cart ',
secondary = ' shopping _ cart _ product _ link '
)
def _ _ repr _ _(self):
return '({ 0 }:{ 1 . name!r}:{1.price!r})”。格式(产品、自身)
类 ShoppingCartProductLink(Base):
_ _ tablename _ _ = ' shopping _ cart _ product _ link '
shopping _ cart _ id = Column(Integer,ForeignKey('shopping_cart.id '),primary _ key = True)
product _ id = Column(Integer,ForeignKey('product.id '),primary_key=True)
我们定义了四个模型,User表示一组用户,Product表示一组产品,ShoppingCart表示一组购物车,每个购物车都由一个User拥有并包含多个Product,还有ShoppingCartProductLink,它是一个连接Product和ShoppingCart的链接表。
然后,让我们将sqlamp引入模型类,看看我们如何使用它来为Product s 创建一个物化的路径。
import sqlamp
from sqlalchemy 导入列,DateTime,String,Integer,ForeignKey,Float
from sqlalchemy.orm 导入关系,back ref
from sqlalchemy . ext . declarative import declarative _ base
Base = declarative_base(元类=sqlamp。DeclarativeMeta)
类 User(Base):
_ _ tablename _ _ = ' User '
id = Column(Integer,primary _ key = True)
name = Column(String)
class shopping cart(Base):
_ _ tablename _ _ = ' shopping _ cart '
id = Column(Integer,primary _ key = True)
owner _ id = Column(Integer,foreign key(User . id))
owner = relationship(
User,backref = backref(' shopping _ carts ',use list = True)
)
products = relationship(
' Product ',
secondary = ' shopping _ cart _ Product _ link '
)
defr})”。格式(购物车、自助)
class Product(Base):
_ _ tablename _ _ = ' Product '
# _ _ MP _ manager _ _ 指定产品的哪个字段是物化路径管理器,
#用于管理产品的子代和祖先的查询。
_ _ MP _ manager _ _ = ' MP '
id = Column(Integer,primary _ key = True)
name = Column(String)
#使用浮点数不是对货币值建模的正确方式。我们将在另一篇文章中探讨这个话题。
price = Column(Float)
shopping _ carts = relationship(
' shopping cart ',
secondary = ' shopping _ cart _ product _ link '
)
#使用自引用外键引用包含此产品的父产品
#。
parent_id = Column(Integer,foreign key(' Product . id ')
parent = relationship(' Product ',remote _ side =[id])
def _ _ repr _ _(self):
return '({ 0 }:{ 1 . name!r}:{1.price!r})”。格式(产品、自身)
类 ShoppingCartProductLink(Base):
_ _ tablename _ _ = ' shopping _ cart _ product _ link '
shopping _ cart _ id = Column(Integer,ForeignKey('shopping_cart.id '),primary _ key = True)
product _ id = Column(Integer,ForeignKey('product.id '),primary_key=True)
注意,我们在Product模型中插入了一个新的外键parent_id和一个新的关系parent,并引入了一个新的类成员字段__mp_manager__。现在我们可以使用Product.mp来查询任何product的子代和祖先。
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///')
>>>
>>>
>>> from sqlalchemy.orm import sessionmaker
>>> DBSession = sessionmaker()
>>> DBSession.configure(bind=engine)
>>> Base.metadata.create_all(engine)
>>>
>>>
>>> camera_package = Product(name='DSLR Camera Package', price=1600.00)
>>> tripod = Product(name='Camera Tripod', price=200.00, parent=camera_package)
>>> body = Product(name='Camera Body', price=400.00, parent=camera_package)
>>> lens = Product(name='Camera Lens', price=1000.00, parent=camera_package)
>>> session = DBSession()
>>> session.add_all([camera_package, tripod, body, lens])
>>> session.commit()
>>> camera_package.mp.query_children().all()
[( :u'Camera Tripod':200.0 ), ( :u'Camera Body':400.0 ), ( :u'Camera Lens':1000.0 )]
>>> tripod.mp.query_ancestors().all()
[( :u'DSLR Camera Package':1600.0 )]
>>> lens.mp.query_ancestors().all()
[( :u'DSLR Camera Package':1600.0 )]
递归处理产品树
为了递归地遍历一棵Product树,我们可以调用sqlamp.tree_recursive_iterator并使用递归函数遍历树的所有后代。
>>> def recursive_tree_processor(nodes):
... for node, children in nodes:
... print('{0}'.format(node.name))
... if children:
... recursive_tree_processor(children)
...
>>> query = camera_package.mp.query_descendants(and_self=True)
>>> recursive_tree_processor(
... sqlamp.tree_recursive_iterator(query, Product.mp)
... )
DSLR Camera Package
Camera Tripod
Camera Body
Camera Lens
摘要
在本文中,我们使用前一篇文章的Product来说明如何使用sqlamp在 SQLAlchemy 中实现物化路径。通过简单地在Product中插入一个自引用外键和一个 mp_manager 字段,我们能够为Product实现一个分层数据结构。由于sqlamp是在 SQLAlchemy 之上编写的,它应该可以与 SQLAlchemy 支持的任何数据库后端一起工作。