development

SQLAlchemy가 할 수있는 것과 Django ORM이 할 수없는 것의 예

big-blog 2020. 11. 25. 08:12
반응형

SQLAlchemy가 할 수있는 것과 Django ORM이 할 수없는 것의 예


저는 최근에 SQLAlchemy와 함께 Pyramid를 사용하는 것과 Django에서 현재 응용 프로그램을 유지하는 것에 대해 많은 연구를 해왔습니다. 그 자체로는 전체적인 논쟁이지만 저는 여기에서 논의 할 필요가 없습니다.

내가 알고 싶은 것은 왜 SQLAlchemy가 일반적으로 Django ORM보다 더 나은 것으로 간주됩니까? 내가 찾은 거의 모든 비교는 SQLAlchemy를 선호합니다. SQLAlchemy의 구조를 통해 훨씬 더 원활하게 SQL로 변환 할 수 있으므로 성능이 큰 것이라고 가정합니다.

그러나 더 어려운 작업에서는 Django ORM을 사용하는 것이 거의 불가능하다고 들었습니다. 나는 이것이 얼마나 큰 문제가 될 수 있는지 범위를 지정하고 싶습니다. SQLAlchemy로 전환하는 이유 중 하나는 Django ORM이 더 이상 귀하의 요구에 적합하지 않을 때입니다.

간단히 말해서 누군가 SQLAlchemy가 할 수있는 쿼리 (실제 SQL 구문 일 필요는 없음)를 제공 할 수 있지만 Django ORM은 원시 SQL을 추가하지 않고는 할 수 없습니까?

업데이트 :

처음 질문 한 이후로이 질문이 꽤 많은 관심을 받고 있다는 것을 알아 차렸 기 때문에 2 센트를 추가로 투자하고 싶습니다.

결국 우리는 SQLAlchemy를 사용하게되었고 결정에 만족한다고 말해야합니다.

지금까지 Django ORM에서 복제 할 수 없었던 SQLAlchemy의 추가 기능을 제공하기 위해이 질문을 다시 검토하고 있습니다. 누군가가 이것을하는 방법의 예를 제공 할 수 있다면 나는 기꺼이 내 말을 먹을 것입니다.

모호한 비교를 제공하는 similarity ()와 같은 postgresql 함수를 사용하고 싶다고 가정 해 보겠습니다 (참조 : 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])

그러나 SQLalchemy에는 http://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.func에 설명 된대로 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은 일반적으로 기본 키 이외의 것을 사용하는 ID 매핑에 문제가 있습니다.

이제 주어진 계정의 인벤토리에서 수량과 함께 각 항목을 쿼리하고 수량이 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테이블에 항목을 투명하게 삽입하거나 업데이트합니다 .

Complex joins, subqueries, window aggregates — Django ORM fails to deal with anything of that without falling back to raw SQL.


This should work in 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)))

참고URL : https://stackoverflow.com/questions/18199053/example-of-what-sqlalchemy-can-do-and-django-orm-cannot

반응형