Пример того, что может сделать SQLAlchemy, а Django ORM не может
в последнее время я много исследовал использование пирамиды с SQLAlchemy против сохранения текущего приложения в Django. Это само по себе целая дискуссия, но я не собираюсь обсуждать это.
Я хочу знать, почему SQLAlchemy повсеместно считается лучше, чем Django ORM? Почти каждое, если не каждое, сравнение, которое я нашел между двумя преимуществами SQLAlchemy. Я предполагаю, что производительность является большой, так как структура SQLAlchemy позволяет ей переводить на SQL много более гладко.
но я также слышал, что с более сложными задачами Django ORM почти невозможно использовать. Я хочу охватить, насколько огромной проблемой это может быть. Я читал одну из причин перехода на SQLAlchemy, когда Django ORM больше не соответствует вашим потребностям.
короче говоря, может ли кто-то предоставить запрос (не обязательно фактический синтаксис SQL), который может сделать SQLAlchemy, но Django ORM не может обойтись без добавления дополнительного raw SQL?
обновление:
Я заметил, что этот вопрос получает довольно много внимания с тех пор, как я впервые спросил, поэтому я хотел бы бросить свои дополнительные два цента.
в конце концов мы закончили с помощью SQLAlchemy и я должен сказать, что я доволен решением.
я пересматриваю этот вопрос, чтобы предоставить дополнительную функцию SQLAlchemy, которую до сих пор мне не удалось воспроизвести в Django ORM. Если кто-то может привести пример того, как это сделать это я с удовольствием съем мои слова.
предположим, вы хотите использовать некоторую функцию postgresql, такую как similarity (), которая обеспечивает нечеткое сравнение (см.: поиск похожих строк с помощью PostgreSQL быстро - tl;dr input two strings get back a percent similarity).
Я сделал некоторые поиски о том, как это сделать с помощью Django ORM и не нашли ничего, кроме использования raw 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 для любой определенной функции sql / postgresql / etc и не требует необработанного sql.
2 ответа:
это опасно близко к тому, чтобы быть неконструктивным, но я укушу.
Предположим, нам нужно вести запасы определенных предметов для ряда различных, скажем, счетов. 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 не может работать с составными первичными ключами. Да, вы всегда можете добавить суррогатный ключ и уникальное ограничение, но это еще один столбец и один индекс, чем вам действительно нужно. Для большой таблицы с небольшим количеством столбцов это будет добавьте заметные накладные расходы на размер и производительность. Кроме того, у ORMs обычно возникают проблемы с сопоставлением идентификаторов с использованием чего-либо, кроме первичного ключа.
теперь предположим, что мы хотим запросить каждый элемент в инвентаре данного счета, сопровождаемый его количеством, но также включить все элементы, не присутствующие там, с количеством, установленным в 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;
нет способа выразить внешнее соединение с пользовательским условием в Джанго ОРМ. Да, вы можете сделать два простых отдельных запроса и выполнить соединение вручную в цикле Python. И производительность, вероятно, не пострадает много в данном случае. Но это к делу не относится, потому что результаты запрос может быть воспроизведен на стороне приложения, используя только basic
SELECT
s.С 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.