SQLAlchemy可以做什么,Django ORM不能做什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (24)

我将重新讨论这个问题,以提供SQLAlchemy的另一个特性,到目前为止,我还无法在Django ORM中进行复制。如果有人能给我举个例子,说明如何做到这一点,我会欣然接受我的话。

假设您想要使用一些PostgreSQL函数,例如相似性(),

我已经对如何使用Django ORM进行了一些搜索,除了使用原始SQL之外,没有发现其他任何东西,

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

然而,SQLalchemy有func(),如下所述:...

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

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

提问于
用户回答回答于

假设我们需要为一些不同的,比如说,帐户,保持某些物品的库存。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。

用户回答回答于

在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)))

扫码关注云+社区