SQLAlchemy可以执行的示例,而Django ORM不能执行的示例


73

我最近在将Pyramid与SQLAlchemy结合使用以及在Django中保留当前应用程序方面进行了大量研究。那本身就是一个完整的辩论,但是我不是在这里讨论。

我想知道的是,为什么普遍认为SQLAlchemy比Django ORM好?我发现两者之间的几乎每一个比较(如果不是每个比较)都有利于SQLAlchemy。我认为性能是很大的,因为SQLAlchemy的结构使它可以更平滑地转换为SQL。

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

简而言之,有人可以提供SQLAlchemy可以执行的查询(不一定是实际的SQL语法),但是Django ORM如果不添加其他原始SQL便无法做到?

更新

自从我第一次问起这个问题以来,我就一直注意到这个问题,所以我想多花2美分。

最后,我们最终使用了SQLAlchemy,我必须对这个决定感到满意。

我正在重新讨论这个问题,以提供SQLAlchemy的其他功能,到目前为止,我还无法在Django ORM中进行复制。如果有人可以提供有关此操作的示例,那么我很乐意吃我的话。

假设您要使用一些postgresql函数,例如sameity(),该函数提供了模糊的比较(请参阅:使用PostgreSQL快速查找相似的字符串-tl; dr输入两个字符串会返回相似性百分比)。

我已经在使用Django ORM的方法上进行了一些搜索,发现除了使用原始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。



sqlalchemy不仅仅是ORM。ORM不能很好地发挥作用,asyncio但是sqlalchemy表达式语言可以与异步代码一起很好地工作。
Meitham '19

Answers:


57

这很危险地接近于非建设性的,但我会咬一口。

假设我们需要为多个不同的帐户维护某些项目的库存。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无法使用复合主键。是的,您始终可以添加代理键和唯一约束,但这比实际需要多一列和多一个索引。对于具有少量列的大表,这将增加明显的大小和性能开销。而且,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循环中手动执行连接。在这种特殊情况下,性能可能不会受太大影响。但这不重要,因为每个查询的结果可以仅使用basic SELECTs在应用程序端进行复制。

使用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。


2
谢谢!这正是我想要的。我担心它也是非建设性的,但是我的目标是得到一个可靠的例子,其中大多数在线资源都说“ SQLAlchemy更好”,并且不提供更多详细信息。
limasxgoesto0

1
COALESCE-“返回列表中的第一个非空值”
cardamom

另一个答案声称已解决。此答案是否也应该编辑?
MJafari

9

这应该在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)))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.