Найти тему
Computer Pro

SQLAlchemy: Введение в RELATIONSHIP - познай всю мощь ORM! #7

Может быть я неверным путём пошел, но мне показалось что SQLAlchemy нужно познать более детально чем это даётся на лекциях python_advanced от SkillBox. Я нашел неплохой курс по этой теме на ютубе и решил его проработать для себя, а заодно может и кому-то ещё пригодится. Не знаю как другие люди, а я не воспринимаю подобные видео, если их просто посмотреть, мне нужно все повторить, иначе просмотр в памяти не отложится! А еще лучше - написать об этом...

Начало публикаций здесь:

Начнем...

У нас есть две таблицы (workers и resumes) и когда мы их объединяем с помощью JOIN у нас в итоговой таблице появляется много лишнего:

-2
-3

Соединившись вместе они выдают результат:

-4

Как можно заметить то в итоговой таблице один и тот же пользователь повторяется в разных "резюме", и получается много лишних строк. Удобней видеть только шесть пользователей (столько у меня в таблице workers) и открывая пользователя можно увидеть его резюме (их может быть несколько).

Итак, у нас на данный момент есть две модели:

создадим для начала два связующих эти два класса поля resumes и worker, а relationship() оставим пока пустым
создадим для начала два связующих эти два класса поля resumes и worker, а relationship() оставим пока пустым

Теперь напишем базовый "ленивый" запрос в orm.py

-6

И добавим его вызов в main.py:

-7

Как можно видеть, теперь при вызове select(WorkersORM) подгружаются данные и из второго класса, в котором хранятся resumes:

-8

Ленивыми запросы называются так потому что первым запросом получается "работник", его резюме не запрашивается до тех пор пока не поступит запрос типа:

-9

Пока программа не захочет прочитать что-то из конкретного резюме.

Такой сценарий годится тогда когда при загрузке всех работников нам не всегда нужны данные по их резюме. Может быть они будут использованы в дальнейшем, а может быть и нет. Такой подход не очень хорош, так как может сильно подгружать базу данных.

Если мы хотим сразу же получить не только всех работников, но и их резюме, то можно указать опцию подгрузки и резюме тоже. Для этого импортируем joinedload (selectinload будет использоваться далее, заодно импортировал)

-10

Добавляем новый метод в orm.py:

помимо options нужно еще указать unique() иначе будет ошибка уникальности username's. unique не отправляется к БД, он выполняется на уровне sqlalchemy
помимо options нужно еще указать unique() иначе будет ошибка уникальности username's. unique не отправляется к БД, он выполняется на уровне sqlalchemy

Так же добавляем вызов этого метода в main.py:

-12

При таком запросе будут сразу же подгружаться все резюме, всех работников. А не как в ленивом запросе - когда это требуется.

-13

joinedload подходит для загрузки данных по типу many-to-one, one-to-one. Для таких типов связей в БД как many-to-many и one-to-many лучше всего подходит опция "selectedinload", которую я импортировал вместе с joindeload. Напишем ещё один метод, который продемонстрирует работу selectinload.

-14
-15

В данном запросе сначала получаются все работники (workers), затем все резюме тех работников которых мы загрузили первым запросом.

-16

joinedload используется для m2o и o2o (многие к одному и один к одному)

selectinload используется для m2m и o2m (многие ко многим и один ко многим)

Когда мы выводим список резюме, относящихся к тому или иному пользователю то получаем скучный перечень объектов, который нам ни о чем не говорит:

-17

Чтобы более детально увидеть что там в этих объектах, нужно проходиться циклом по объектам или еще какой нибудь код написать, а можно объявить в каждом классе, который мы будем создавать метод __repr_(self): в котором задавать то, какие данные мы хотим видеть в консоли вместо скучного отображения списка объектов.

-18

Но иногда этих самых классов будет такое количество что запаришься в каждом указывать метод __repr__(self): и тогда тоже самое (тут у меня уже все параметры класса будут выведены) можно прописать в базовом классе, который является "родительским" для всех наших классов:

-19

Но опять и этот вывод не очень красив, так как нам столько много информации для вывода не надо, можно указать конкретней - сколько выводить колонок для __repr__

-20

Причем переменные repr_cols_num и repr_cols можно переопределять в дочерних классах, указывая сколько по умолчанию вывести колонок и возможно, еще какую нибудь колонку, например - "created_at" вывести дополнительно:

-21

Ну и в довершение - при асинхронном коде нельзя использовать ленивую подгрузку, это приведет к тому что приложение зависнет, рухнет и т.д. Там нужно заранее все relationship подгружать. Нужно прописывать .options() и в ней joinedload или selectedinload.

Конец первой части про relationship... Продолжение следует...