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

lim*_*to0 65 python 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类似的字符串 - 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])
Run Code Online (Sandbox Code Playgroud)

但是,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))
Run Code Online (Sandbox Code Playgroud)

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

rkh*_*rov 54

这非常危险地接近于非建设性,但我会咬人.

假设我们需要为许多不同的账户维护某些项目的库存.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)
);
Run Code Online (Sandbox Code Playgroud)

首先,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;
Run Code Online (Sandbox Code Playgroud)

在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())
Run Code Online (Sandbox Code Playgroud)

作为旁注,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))
Run Code Online (Sandbox Code Playgroud)

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

account.inventory[item_id] += added_value
Run Code Online (Sandbox Code Playgroud)

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

复杂连接,子查询,窗口聚合 - Django ORM无法处理任何内容而不会回退到原始SQL.

  • 另一个答案声称它已解决。这个答案不应该也被编辑吗? (3认同)
  • 谢谢!这正是我想要的。我担心它也是非建设性的,但是我的目标是得到一个可靠的例子,其中大多数在线资源都说“ SQLAlchemy更好”,并且没有提供更多详细信息。 (2认同)

spi*_*ama 5

这应该在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)))
Run Code Online (Sandbox Code Playgroud)