我最近在使用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(),它提供模糊比较(请参阅: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。
答案 0 :(得分:52)
这非常接近于没有建设性,但我会咬人。
假设我们需要为许多不同的帐户维护某些商品的库存。 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循环中手动执行连接。在这种特殊情况下,性能可能不会受到太多影响。但这不是重点,因为每个查询的结果只能在应用程序端使用基本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。
答案 1 :(得分:4)
这应该适用于Django 1.11:
FEDEX