Продолжаем разрабатывать бэкенд клона твиттера для корпоративной локальной сети. Начало по ссылкам ниже:
Дисклеймер: Данный цикл статей не инструкция к тому как нужно делать. Это больше похоже на лайвкодинг, с публикацией шагов, что и как я делал. Дабы в процессе написания статьи самому более глубоко проникнуться в тему, а так же помочь другим людям, которые, возможно, тупанули на каком-то моменте и не знают как сдвинуться с мёртвой точки. Я на этом шаге тупил месяц!
Так как статья и код писалась очень долго, то случились некоторые изменения, которые могут не совпадать с предыдущими статьями.
Итак, обработку пользователей я завершил, после успешной отработки запроса на эндпойнт /api/users/me, фронтэнд запрашивает следующий эндпойнт: /api/tweets (в логах именно такой порядок):
Начну с главного файла, ибо в нем случились некоторые изменения. Появился "жизненный цикл" базы данных. Если перезапустить приложение то все данные удалятся и создадутся заново. Эндпойнты вынесены в отдельные файлы из которых они импортируются. Nginx и postgres установлены на компьютер с операционной системой Manjaro Linux дома и Arch Linux - на работе. Ну и само приложение - решил делать без докер-контейнеров. Контейнеры буду подключать только тогда когда будет всё закончено.
Как я настраивал работу фронта без контейнеров:
Итак, main.py:
Чтобы получить какие-то твиты, они у нас должны быть в базе данных, а для этого их туда надо поместить, вместе с данными каких-то пользователей. Для этого и служит "жизненный цикл приложения", пока приложение работает и не перезагружается - все изменения в базе данных сохраняются.
Модели, по которым создаются таблицы
Потому как для реализации конечной точки /api/tweets, нужны все модели (users, tweets, likes, images).
Все начинается с директории /src/core/models и файла __init__.py, где и объявляются все модели, чтобы заработало создание таблиц через Base.metadata.create_all:
Затем базовая модель, от которой будут наследоваться все остальные модели:
Модель пользователей, пожалуй, одна из самых сложных для понимания моделей, здесь очень много различных связей с другими таблицами:
Именно здесь указан атрибут backref="user" который будет использоваться для подгрузки данных пользователей:
Скажу честно, эту модель я не из головы придумал, а где-то позаимствовал и не всё в ней я до конца понял.
Далее идет модель твитов:
Модель лайков:
Модель картинок:
И вот только после того как у нас появились все нужные модели, можно создать таблицы (Base.metadata.create_all). Наполнить их данными (await insert_data(conn) в файле main.py). Это отсылка к файлу /api/crud/insert_data_in_tables.py
В котором будет создано 5 пользователей, 5 твитов, какие-то твиты будут "пролайканы", какие-то пользователи будут подписаны на других пользователей:
После того как все таблицы созданы и данные в них имеются, в базе данных получается вот такая вот прелесть:
Таблица с картинками у меня пока не используется, после того как закончу с получением твитов, буду пробовать подставить какие-нибудь картинки к этим твитам.
Ну чтож, база данных наполнена, сделаем GET-запрос на получение твитов, для пользователя с api_key = "test":
Вот такой получается хитрый запрос. Попробую объяснить некоторые моменты:
response_model=TweetListSchema - pydantic schema, которая нужна для правильной подачи данных для фронтенда.
Из схемы вывода твитов, видно что еще используются схемы вывода лайков и базовая, для пользователя.
Далее дело за аргументами асинхронной функции get_tweets_follow_user, где получается с помощью db_helper асинхронная сессия, из заголовка мы получаем текущего пользователя с помощью функции get_current_user, которая находится в файле src/api/crud/crud_users.py:
Строчка выделена серым цветом, что показывает - она как бы не используется, но это не совсем так. Если ее убрать, то исчезнет из Swagger заголовок в котором можно указать api_key:
Без этой строчки будет так:
Поэтому строчка хоть как-бы и не используется, она всё-таки используется!
Далее идет обращение к асинхронной функции get_all_tweets, в аргументах которой передаются сессия для соединения с базой данных и пользователь от имени которого посылается запрос. Название get_all_tweets пожалуй неправильное, ведь я не все твиты получаю а только те на кого подписан текущий пользователь (имя функции будет изменено).
Вот та функция, которая мне никак не хотела даваться! Я довольно таки быстро сделал фильтрацию по пользователю, подтянул имена пользователей. Картинки пока не проверено. Но мне никак не давалась сортировка по количеству лайков. Я понимал что нужно сделать счетчик этих лайков, но куда вставить - х.з. Всё давало ошибку. Спросил совета у мудрецов в интернете, ответ получил примерно как фильме "О чем говорят мужчины" - "...зеленые тапочки!"
Посоветовали использовать .group_by, а как и где и с какой "обвязкой" - хз! В итоге, как-то сам вышел на эту конструкцию, что описана выше.
Незнаю насколько это правильно - не правильно, но это работает. Теперь все твиты на которых подписан пользователь с api_key = 'test' выведены и отсортированы по убыванию популярности (то есть которые больше всех были лайкнуты).
Ну вот пожалуй на сегодня и всё. Я вчера был вне себя от счастья, что я таки смог дописать четыре строчки, чтобы всё правильно работало!