【问题标题】:Example of what SQLAlchemy can do, and Django ORM cannotSQLAlchemy 可以做什么,而 Django ORM 不能做的例子
【发布时间】:2013-08-14 11:26:18
【问题描述】:

我最近一直在做大量研究,研究将 Pyramid 与 SQLAlchemy 结合使用,而不是在 Django 中保留当前应用程序。这本身就是一场完整的辩论,但我不是来讨论这个的。

我想知道的是,为什么普遍认为 SQLAlchemy 比 Django ORM 更好?我发现的几乎每一个(如果不是每一个)比较都支持 SQLAlchemy。我认为性能是一个重要因素,因为 SQLAlchemy 的结构让它更顺利地转换为 SQL。

但是,我也听说对于更难的任务,Django ORM 几乎无法使用。我想弄清楚这可能是一个多么巨大的问题。我一直在阅读切换到 SQLAlchemy 的原因之一是 Django ORM 不再适合您的需求。

因此,简而言之,是否有人可以提供 SQLAlchemy 可以执行的查询(不一定是实际的 SQL 语法),但如果不添加额外的原始 SQL,Django ORM 就不可能完成?

更新

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

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

我正在重新审视这个问题,以提供 SQLAlchemy 的附加功能,到目前为止,我还无法在 Django ORM 中进行复制。如果有人可以提供如何做到这一点的例子,我很乐意接受我的话。

假设您想使用一些 postgresql 函数,例如similarity(),它提供了一个模糊比较(参见:Finding similar strings with PostgreSQL quickly - 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。

【问题讨论】:

标签: python sqlalchemy django-orm


【解决方案1】:

这很危险,几乎是非建设性的,但我会咬人的。

假设我们需要为多个不同的帐户(例如帐户)维护某些物品的库存。 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 循环中手动执行连接。在这种特殊情况下,性能可能不会受到太大影响。但这无关紧要,因为每个查询的结果都可以在应用程序端仅使用基本的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。

【讨论】:

  • 谢谢!这正是我一直在寻找的。我也担心它是非建设性的,但我的目标是获得一个可靠的示例,其中大多数在线资源都说“SQLAlchemy 更好”并且没有提供更多细节。
  • COALESCE - "返回列表中的第一个非空值"
  • 另一个答案声称它已解决。这个答案不应该也被编辑吗?
【解决方案2】:

这应该在 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)))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-17
    • 2020-07-09
    • 2011-08-23
    • 2018-05-30
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    • 2013-03-11
    相关资源
    最近更新 更多