Может быть я неверным путём пошел, но мне показалось что SQLAlchemy нужно познать более детально чем это даётся на лекциях python_advanced от SkillBox. Я нашел неплохой курс по этой теме на ютубе и решил его проработать для себя, а заодно может и кому-то ещё пригодится. Не знаю как другие люди, а я не воспринимаю подобные видео, если их просто посмотреть, мне нужно все повторить, иначе просмотр в памяти не отложится! А еще лучше - написать об этом...
Начало публикаций здесь:
Начнем...
У нас есть две таблицы (workers и resumes) и когда мы их объединяем с помощью JOIN у нас в итоговой таблице появляется много лишнего:
Соединившись вместе они выдают результат:
Как можно заметить то в итоговой таблице один и тот же пользователь повторяется в разных "резюме", и получается много лишних строк. Удобней видеть только шесть пользователей (столько у меня в таблице workers) и открывая пользователя можно увидеть его резюме (их может быть несколько).
Итак, у нас на данный момент есть две модели:
Теперь напишем базовый "ленивый" запрос в orm.py
И добавим его вызов в main.py:
Как можно видеть, теперь при вызове select(WorkersORM) подгружаются данные и из второго класса, в котором хранятся resumes:
Ленивыми запросы называются так потому что первым запросом получается "работник", его резюме не запрашивается до тех пор пока не поступит запрос типа:
Пока программа не захочет прочитать что-то из конкретного резюме.
Такой сценарий годится тогда когда при загрузке всех работников нам не всегда нужны данные по их резюме. Может быть они будут использованы в дальнейшем, а может быть и нет. Такой подход не очень хорош, так как может сильно подгружать базу данных.
Если мы хотим сразу же получить не только всех работников, но и их резюме, то можно указать опцию подгрузки и резюме тоже. Для этого импортируем joinedload (selectinload будет использоваться далее, заодно импортировал)
Добавляем новый метод в orm.py:
Так же добавляем вызов этого метода в main.py:
При таком запросе будут сразу же подгружаться все резюме, всех работников. А не как в ленивом запросе - когда это требуется.
joinedload подходит для загрузки данных по типу many-to-one, one-to-one. Для таких типов связей в БД как many-to-many и one-to-many лучше всего подходит опция "selectedinload", которую я импортировал вместе с joindeload. Напишем ещё один метод, который продемонстрирует работу selectinload.
В данном запросе сначала получаются все работники (workers), затем все резюме тех работников которых мы загрузили первым запросом.
joinedload используется для m2o и o2o (многие к одному и один к одному)
selectinload используется для m2m и o2m (многие ко многим и один ко многим)
Когда мы выводим список резюме, относящихся к тому или иному пользователю то получаем скучный перечень объектов, который нам ни о чем не говорит:
Чтобы более детально увидеть что там в этих объектах, нужно проходиться циклом по объектам или еще какой нибудь код написать, а можно объявить в каждом классе, который мы будем создавать метод __repr_(self): в котором задавать то, какие данные мы хотим видеть в консоли вместо скучного отображения списка объектов.
Но иногда этих самых классов будет такое количество что запаришься в каждом указывать метод __repr__(self): и тогда тоже самое (тут у меня уже все параметры класса будут выведены) можно прописать в базовом классе, который является "родительским" для всех наших классов:
Но опять и этот вывод не очень красив, так как нам столько много информации для вывода не надо, можно указать конкретней - сколько выводить колонок для __repr__
Причем переменные repr_cols_num и repr_cols можно переопределять в дочерних классах, указывая сколько по умолчанию вывести колонок и возможно, еще какую нибудь колонку, например - "created_at" вывести дополнительно:
Ну и в довершение - при асинхронном коде нельзя использовать ленивую подгрузку, это приведет к тому что приложение зависнет, рухнет и т.д. Там нужно заранее все relationship подгружать. Нужно прописывать .options() и в ней joinedload или selectedinload.