什么SQLAlchemy可以做的例子,Django的ORM不能

最近我在使用Pyramid和SQLAlchemy进行了大量的研究,而在Django中保留了当前的应用程序。 这本身就是一场辩论,但我不是在这里讨论的。

我想知道的是,为什么SQLAlchemy普遍认为比Django ORM更好? 几乎每一个,如果不是每一个,我都发现了两种偏好SQLAlchemy之间的比较。 我认为性能是一个很大的一个,因为SQLAlchemy的结构可以让它更顺利地转换成SQL。

但是,我也听说,更艰巨的任务,Django ORM几乎不可能使用。 我想确定这个问题有多大。 我一直在阅读的一个原因,切换到SQLAlchemy是当Django ORM不再适合您的需求。

所以,简而言之,有人可以提供SQLAlchemy可以执行的查询(不一定是实际的SQL语法),但是Django ORM不可能不添加额外的原始SQL呢?

更新

自从我第一次问起,我就注意到这个问题引起了相当多的关注,所以我想多加两分钱。

最后,我们结束了使用SQLAlchemy,我必须说我对这个决定感到高兴。

我正在重温这个问题,提供一个额外的SQLAlchemy特性,到目前为止,我还没有能够在Django ORM中进行复制。 如果有人能提供一个如何做到这一点的例子,我会很乐意吃我的话。

假设你想使用一些postgresql函数,例如similarity(),它提供了一个模糊的比较(参见: 用PostgreSQL快速查找类似的string -tl; drinput两个string得到相似的百分比)。

我已经做了一些关于如何使用Django ORM进行search的方法,并且除了使用原始的sql之外,没有发现任何东西,这似乎是从他们的文档中显而易见的: https : //docs.djangoproject.com/en/dev/topics/db / sql / 。

Model.objects.raw('SELECT * FROM app_model ORDER BY \ similarity(name, %s) DESC;', [input_name]) 

然而,SQLalchemy具有func(),如下所述: http ://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.func

 from sqlalchemy import desc, func session.query(Model).order_by(func.similarity(Model.name, input_name)) 

这允许你为任何定义的sql / postgresql / etc函数生成sql,而不需要原始的sql。

这是危险的接近非build设性,但我会咬。

假设我们需要维护一些不同的库存,比如账户。 DDL如下:

 CREATE TABLE account ( id serial PRIMARY KEY, ... ); CREATE TABLE item ( id serial PRIMARY KEY, name text NOT NULL, ... ); CREATE TABLE inventory ( account_id integer NOT NULL REFERENCES account(id), item_id integer NOT NULL REFERENCES item(id), amount integer NOT NULL DEFAULT 0 CHECK (amount >= 0), PRIMARY KEY (account_id, item_id) ); 

首先,Django的ORM不能使用复合主键。 是的,你总是可以添加一个代理键和唯一的约束,但是这是多一个列和一个索引比你实际需要。 对于列数较less的大表,这会增加明显的大小和性能开销。 而且,ORM通常使用除主键之外的任何其他标识映射都是有问题的。

现在,我们假设我们要查询给定账户的库存中的每个项目以及其数量,还包括所有不存在的项目,数量设置为0.然后按数量降序排列。 对应的SQL:

 SELECT item.id, item.name, ..., coalesce(inventory.amount, 0) AS amount FROM item LEFT OUTER JOIN inventory ON item.id = inventory.item_id AND inventory.team_id = ? ORDER BY amount DESC; 

在Django ORM中,没有办法用自定义条件来表示外部连接。 是的,你可以做两个简单的单独查询,并在Python循环中手动执行连接。 在这种情况下性能可能不会受到太大影响。 但是这并不重要,因为每个查询的结果可以在应用程序端使用基本的SELECT来重现。

使用SQLAlchemy:

 class Account(Base): __tablename__ = 'account' id = Column(Integer, primary_key=True) ... class Item(Base): __tablename__ = 'item' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) ... class Inventory(Base): __tablename__ = 'inventory' account_id = Column(Integer, ForeignKey('account.id'), primary_key=True, nullable=False) account = relationship(Account) item_id = Column(Integer, ForeignKey('item.id'), primary_key=True, nullable=False) item = relationship(Item) amount = Column(Integer, CheckConstraint('amount >= 0'), nullable=False, default=0) account = session.query(Account).get(some_id) result = (session .query(Item, func.coalesce(Inventory.amount, 0).label('amount')) .outerjoin(Inventory, and_(Item.id==Inventory.item_id, Inventory.account==account)) .order_by(desc('amount')) .all()) 

作为一个侧面说明,SQLAlchemy使基于字典的集合非常容易。 通过在Account模型中添加以下代码,您可以使与Inventory关系显示为:从项目到其数量的映射。

 items = relationship('Inventory', collection_class=attribute_mapped_collection('item_id')) inventory = association_proxy('items', 'amount', creator=lambda k, v: Inventory(item_id=k, amount=v)) 

这使您可以编写代码,如:

 account.inventory[item_id] += added_value 

透明地插入或更新inventory表中的条目。

复杂的连接,子查询,窗口聚合 – Django ORM无法处理任何事情,而不会退回到原始SQL。

这应该在Django 1.11中工作:

 inventory_amount = Subquery(account.inventory_set.filter(item=OuterRef('pk')).values('amount')) Item.objects.annotate(inventory_amount=Coalesce(inventory_amount, Value(0)))