Метод comparator класса hybrid_property
Метод comparator класса hybrid_property позволяет
задать пользовательский класс для сравнения гибридного
свойства на уровне SQL. По умолчанию SQLAlchemy использует
стандартный класс сравнения, который преобразует операции
(например, ==, >, like) в SQL-выражения.
Метод comparator принимает класс-наследник
Comparator и позволяет переопределить поведение
любого оператора. Это полезно, когда нужно реализовать
нестандартную логику сравнения, которая не сводится
к простому отображению на колонку.
Синтаксис
from sqlalchemy.ext.hybrid import hybrid_property
@hybrid_property
def property_name(self):
return ...
@property_name.comparator
def property_name(cls):
return CustomComparator
Пример
Давайте создадим модель Article с гибридным
свойством is_published, которое возвращает
True, если статус статьи равен 'published'.
Определим собственный компаратор, который преобразует
сравнение в SQL-условие:
from sqlalchemy import create_engine, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
from sqlalchemy.ext.hybrid import hybrid_property
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(100))
status: Mapped[str] = mapped_column(String(50))
@hybrid_property
def is_published(self):
return self.status == 'published'
@is_published.comparator
def is_published(cls):
return ArticleComparator(cls)
class ArticleComparator:
def __init__(self, cls):
self.cls = cls
def __eq__(self, other):
if other:
return self.cls.status == 'published'
return self.cls.status != 'published'
def __ne__(self, other):
if other:
return self.cls.status != 'published'
return self.cls.status == 'published'
Теперь создадим таблицы, добавим статьи и выполним запрос с использованием нашего компаратора:
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from models import Base, Article
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add_all([
Article(title='article 1', status='published'),
Article(title='article 2', status='draft'),
Article(title='article 3', status='published')
])
session.commit()
res = session.query(Article).filter(Article.is_published == True).all()
print([article.title for article in res])
Результат выполнения кода:
['article 1', 'article 3']
Итоговый SQL запрос:
SELECT articles.id AS articles_id, articles.title AS articles_title, articles.status AS articles_status
FROM articles
WHERE articles.status = 'published'
Пример
Давайте усложним пример: добавим гибридное свойство
has_text, которое проверяет, что поле text
не пустое. Компаратор будет поддерживать сравнение
с True и False:
from sqlalchemy import create_engine, String, Text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
from sqlalchemy.ext.hybrid import hybrid_property
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(100))
text: Mapped[str] = mapped_column(Text, nullable=True)
@hybrid_property
def has_text(self):
return self.text is not None and len(self.text) > 0
@has_text.comparator
def has_text(cls):
return HasTextComparator(cls)
class HasTextComparator:
def __init__(self, cls):
self.cls = cls
def __eq__(self, other):
if other:
return self.cls.text.isnot(None) & (self.cls.text != '')
return self.cls.text.is_(None) | (self.cls.text == '')
Выполним запрос и выведем статьи, у которых есть текст:
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from models import Base, Article
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add_all([
Article(title='article 1', text='some text'),
Article(title='article 2', text=None),
Article(title='article 3', text='')
])
session.commit()
res = session.query(Article).filter(Article.has_text == True).all()
print([article.title for article in res])
Результат выполнения кода:
['article 1']
Итоговый SQL запрос:
SELECT articles.id AS articles_id, articles.title AS articles_title, articles.text AS articles_text
FROM articles
WHERE articles.text IS NOT NULL AND articles.text != ''
Смотрите также
-
класс
hybrid_property,
который создает гибридное свойство -
метод
expression,
который задает SQL-выражение для гибридного свойства -
метод
update_expression,
который определяет SQL-выражение для обновления -
метод
getter,
который задает геттер для гибридного свойства