Что дает использование relationship в SQLAlchemy
Что проверяют
Отличаете ли вы уровень ORM от уровня базы и понимаете ли, в какой момент обращение к атрибуту превращается в запрос. Вопрос звучит безобидно, но следом почти всегда спрашивают про число запросов на страницу.
Как отвечать
relationship описывает связь на уровне маппинга и даёт навигацию между объектами. В базе связь задаёт ForeignKey на колонке, а relationship объясняет, как по этому ключу ходить в обе стороны.
Что это даёт:
- атрибут вместо ручного соединения таблиц:
user.postsиpost.author; back_populatesдержит обе стороны согласованными в сессии ещё до фиксации транзакции;- выбор стратегии загрузки: по умолчанию ленивая, отдельный запрос при первом обращении, а
selectinloadиjoinedloadподгружают заранее,lazy="raise"запрещает неявные запросы; - каскады:
cascade="all, delete-orphan"убирает потерявших родителя детей,secondaryописывает связь многие ко многим через промежуточную таблицу.
Сильный ответ заканчивается тем, ради чего вопрос и задан: связь удобна, но прячет запросы, поэтому стратегия загрузки выбирается осознанно под сценарий, а не оставляется по умолчанию.
Где спотыкаются
Думают, что relationship создаёт связь в самой базе. Без ForeignKey в схеме не будет ограничения целостности, а маппинг не поймёт, по какому условию соединять таблицы.
Ловят запрос на каждой итерации. Цикл по списку пользователей с обращением к коллекции постов даёт один запрос на список и по запросу на каждого, и это самая частая причина жалоб на медленную ORM. Лечится предварительной загрузкой, причём для коллекций обычно берут вариант с отдельным запросом: соединение таблиц размножает строки родителя.
Обращаются к ленивой связи, когда сессия уже закрыта, и получают ошибку об отсоединённом объекте. В асинхронном коде это вылезает ещё заметнее: неявная подгрузка внутри корутины падает, и связанные данные приходится загружать заранее.