Функция association_proxy
Функция association_proxy создает атрибут-прокси,
который позволяет обращаться к полям связанного объекта
напрямую через родительский объект, минуя промежуточную
ассоциативную таблицу. Первым параметром передается имя
существующего relationship-атрибута, вторым - имя целевого
атрибута связанного объекта. Третьим необязательным
параметром можно передать creator и getset_factory
для тонкой настройки поведения прокси.
Синтаксис
from sqlalchemy.ext.associationproxy import association_proxy
association_proxy(target_collection, attr, [creator])
Пример
Давайте создадим две модели, связанные отношением
многие-ко-многим через ассоциативную таблицу, и
применим association_proxy для удобного доступа
к тегам статьи:
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy.ext.associationproxy import association_proxy
class Base(DeclarativeBase):
pass
article_tag = Table(
'article_tag',
Base.metadata,
Column('article_id', ForeignKey('articles.id'), primary_key=True),
Column('tag_id', ForeignKey('tags.id'), primary_key=True)
)
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
tags: Mapped[list['Tag']] = relationship(secondary=article_tag)
tag_names = association_proxy('tags', 'name')
class Tag(Base):
__tablename__ = 'tags'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
from sqlalchemy.orm import Session
with Session(engine) as session:
article = Article(title='article')
article.tag_names.append('python')
article.tag_names.append('sqlalchemy')
session.add(article)
session.commit()
res = session.query(Article).first()
print(res.tag_names)
Результат выполнения кода:
['python', 'sqlalchemy']
Пример
Давайте убедимся, что запись через прокси создает новые связанные объекты автоматически, а чтение дает прямые значения атрибутов:
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session
from sqlalchemy.ext.associationproxy import association_proxy
class Base(DeclarativeBase):
pass
article_tag = Table(
'article_tag',
Base.metadata,
Column('article_id', ForeignKey('articles.id'), primary_key=True),
Column('tag_id', ForeignKey('tags.id'), primary_key=True)
)
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
tags: Mapped[list['Tag']] = relationship(secondary=article_tag)
tag_names = association_proxy('tags', 'name')
class Tag(Base):
__tablename__ = 'tags'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
article = Article(title='article')
article.tag_names.append('sqlite')
session.add(article)
session.commit()
tag = session.query(Tag).first()
print(tag.name)
print(article.tags[0].name)
Результат выполнения кода:
"sqlite"
"sqlite"
Смотрите также
-
функцию
relationship,
которая создает связь между моделями -
параметр
backref,
который создает обратную ссылку на родителя -
параметр
foreign,
который задает внешний ключ связи -
загрузку
selectinload,
которая эффективно подгружает связанные объекты