Найти в Дзене

Как я сервис сокращения ссылок писал

Оглавление

Привет! Начать свой блог я решил с написания небольшого рассказа о том, как я писал пет-проект по сокращению ссылок, с какими сложностями столкнулся и к чему это привело.

Почему именно сокращение ссылок?

Написать именно этот сервис я решил довольно просто, загуглив "идеи для проектов на python", а пишу я в последнее время именно на нём, я наткнулся на статью со списком, из которого и выбрал этот проект. Остальные мне показались или не перспективными или слишком долгими для реализации в одиночку, либо просто скучными.

Архитектура проекта

В качестве основного фреймворка я не задумываясь выбрал Джанго, потому что он позволил мне набросать бэкенд без лишних заморочек. Кто-то может подумать, что это too much для такой разработки, но в этой ситуации скорость написания проекта была одним из ключевых аргументов при выборе сервиса для реализации.

На фронте все просто - немного jQuery для отправки ajax запросов и джанговские templates для вывода данных.

Фичи

Чтобы сервис отличался от конкурентов и чтобы хоть как-то усложнить проект по сравнению с однострочным вызовом метода redirect(), я принял решение написать еще и телеграм-бота к этому проекту, а так же показывать ранее сгенерированные ссылки исходя из сессии браузера.

Код

Как вы знаете, джанго исповедует концепцию MVC (model view controller), а если точнее, то MVT (model view template), и первое что я решил сделать, это описать архитектуру моделей БД.

Модель ссылки

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

View для обработки перехода по ссылке

Следующее, что мне предстояло сделать, это описать view для обработки перехода по сгенерированной ссылке. Для описания класса RedirectView я решил наследовать его от Generic класса View, потому что его достаточно для этой задачи и в дальнейшем класс будет удобно масштабировать при необходимости. Алгоритм следующий:
при помощи метода get_object_or_404() ищем в БД ссылку, значение которой равно текущему пути запроса. Тут важное уточнение: можно было доставать ссылку из request.path, но я решил использовать более правильный путь - получать значение из именованных аргументов, которые передаются при запросе, сейчас urlpatterns выглядит так:

Класс RedirectView:

Собственно, это вся логика сервиса. Этого уже достаточно для элементарной работы - убедиться в этом можно создав ссылочную пару через django-админку. Дальше осталось лишь описать форму для генерации ссылки, передать её в template и настроить отправку ajax запроса, чтобы не происходило обновления страницы при нажатии на кнопку "Сократить". Давайте к ним и перейдем.

Index View

Класс IndexView представляет собой обработчик рендера главной страницы, на ней отображаются сверстанные на коленке инпуты с минимальным набором стилей, созданные ранее ссылки и ссылка на телеграм-бота в шапке сайта. По большому счету в IndexView достаточно вызвать метод render из django.shortcuts, передав в него request и какой-то контекст - в контекст я решил передавать ранее сгенерированные ссылки, если они есть, ну и собственно всё.

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

Тут нужно отступить, рассказав о том, как происходит генерация ссылок:
кто-то предпочитает использовать хэш функции, я же решил сделать так:
написал небольшую функцию
generate_random_string(length, char_list)
которая возвращает строку длиной по умолчанию 7 символов, эта функция в свою очередь вызывается из create_short_link(source_link). Функция
create_short_link(source_link)
опрашивает базу до тех пор, пока в она не вернет False в exists(), как правило достаточно одной итерации, по крайней мере пока набор сгенеренных ссылок невелик. Код функции:

Тут же и производится запись созданной ссылочной пары в базу.

Фронтенд

В построении фронтенда самым сложным для меня оказалось написать мобильную верстку, никогда раньше особо этим не занимался, а использовать бутстрап для такого маленького проекта казалось излишним, поэтому пришлось открывать w3school и читать про медиа теги.

Что касается отправки формы, то тут все куда нельзя проще:
навешиваем event listener на кнопку "отправить" и ждем ответа от сервера. Так как нам возвращается json, то достаем из него сгенеренную ссылку и выводим на экран. Код main.js выглядит так:

Итоги

Всего на разработку сервиса было потрачено четыре вечера после работы, и один выходной на развертывание проекта: купить домен, настроить конфиги nginx и gunicorn, написать конфигурацию systemd и всякие мелочи вроде валидации урл на бекенде (ее я написал уже после того, как залил проект на продакшен), уведомления об успешно_созданной/невалидной ссылке на фронте. И еще один вечер на написание адаптивной верстки да и стилей в целом, хотелось чтобы проект выглядел симпатично, как ready to use продукт. Осталось дождаться индексации сайта, после чего будет понятно что делать с проектом дальше