Мобильная версия для Django-проекта
С каждым днем пользователи смартфонов занимают все большую долю интернета. По данным LiveInternet доля российских пользователей OS Android уже превысила долю Windows7. В выходные дни пользователи мобильных платформ пользуются интернетом значительно чаще. Та же тенденция наблюдается и в мире. Все это еще раз доказывает необходимость адаптации сайта для смартфонов и планшетов.
О том, как можно адаптировать ваш Django-проект для мобильных устройств, я расскажу в этой статье. Но сначала давайте разберем, какие есть варианты создания мобильной версии сайта.
1. Варианты мобильной версии сайта
1.1 Адаптивная версткаВ этом случае мы отдаем одинаковое количество данных для большой и мобильной версии сайта. Этот подход самый простой для backend разработки, все решается версткой.
- не требует редиректов;
- не нужно отдельно сообщать поисковым роботам о наличии мобильной версии (мета теги alternate, сайтмапы и прочее).
- поскольку на мобильной версии приходится отдавать все и скрывать лишнее, создается лишний трафик и нагрузка на сервер;
- версия для мобильных устройств всегда должна создаваться с большой версией сайта;
- так как данные на обоих версиях одинаковы, может быть сложно удобно организовать обе версии, скорее всего мобильной версией придется пожертвовать; к примеру, я не представляю, как можно сделать удобный форум при таком подходе.
По сути, это два отдельных сайта. Такой подход решает проблемы лишнего трафика, дает больше гибкости и возможностей в разработке версии для мобильных устройств. Однако при этом вопрос, какую версию показывать пользователю, решается сервером, а не браузером. Также нужно дать возможность пользователю выбрать, какая версия сайта ему нужна, и «подружить» обе версии сайта редиректами и альтернейтами.
Разные URL одной страницы приводят к недостатку этого способа: относительные ссылки в материалах сайта могут вести на страницы, отсутствующие в мобильной версии. Поэтому приходится указывать абсолютные ссылки на основной домен и потом редиректить пользователя на нужную версию. Решением этого недостатка будет полностью соответствующие большая и мобильная версии, но в этом случае правильнее идти третьим способом.
1.3 Мобильная версия на том же доменеЭто доработка первого подхода и решение его минуса с трафиком и лишней нагрузкой. Реализуется он так же как с поддоменом: вы определяете, какая версия нужна клиенту, и отдаете нужное количество данных в нужный шаблон. Одинаковый URL для обоих версий сайта — безусловно плюс. Хотя проблема организации контента для обоих версий еще остается, но решать ее уже проще, так как ограничения на одинаковые данные уже нет.
1.4 Наш опытВ отделе контентных проектов Mail.Ru Group мы используем второй подход, хотя и плавно движемся в сторону третьего. Проекты Дети Mail.Ru и Здоровье Mail.Ru написаны на Django, оба имеют мобильные и/или тач версии. Несмотря на то, что проекты под капотом немного отличаются, механизм создания мобильных версий у них одинаков. Об этом я хочу с вами поделиться.
2. Исходные соглашения
2.1 Все роуты мы именуемИ обращаемся к ним всегда по этому имени.
Мы никогда не собираем URL'ы сами в контроллерах или шаблонах, они указаны только в urls.py. DRY.
2.2 Используем django-hostsОб этой библиотеке уже упоминали на Хабре. Изначально мы ее использовали на «детях» для форума на поддомене, сейчас форум переехал у нас на основной хост, и эта библиотека используется только для мобильный версий.
Вкратце, как она работает: вы подключаете middleware, которая в зависимости от Host заголовка подменяет схему URL'ов. Помимо джанговской функции reverse , вы можете использовать из этой библиотеки reverse_full , которая строит абсолютный URL. Подобный тег host_url можно использовать в шаблонах. Используемые функции reverse_host , get_host также взяты из этого приложения.
2.3 Отдельные контроллеры для большой и мобильной версииНесмотря на то, что порой контроллеры большой и мобильной версий сайта отдают в контекст одинаковые данные, мы их разделили на отдельные функции/классы. Да, между ними есть дублирование, но зато код становится понятней без лишних абстракций и проверок, когда какие функции нужны и в каком виде.
3. Разработка мобильной версии
Мобильная версия должна решать следующие задачи:
- Определение версии сайта и редирект для соответствующих устройств.
- Возможность пользователя отказаться от мобильной версии и использовать основную.
- Редиректы должны перенаправлять на соответствующую мобильную версию и наоборот. Нехорошо кидать пользователя на главную и заставлять искать с начала.
- Если пользователь попал на отсутствующую страницу в мобильной версии, для которой есть аналог в большой версии, ему нужно явно сообщать об этом. Этой задачи не было, если бы обе версии соответствовали друг другу.
- Необходимо указывать мета теги alternate и canonical, если для страницы доступен мобильный аналог.
С какого устройства пользователь зашел на наш проект мы определяем с помощью нашего модуля nginx. Выглядит это примерно вот так:
Модуль опредеяет тип версии, которую нужно показать (m или touch), но если у пользователя стоит кука mobile, мы игнорируем это. Результат передается в виде http заголовка на бэкенд.
Дальнейшая обработка запроса происходит в middleware.
- пришел GET запрос;
- запрос пришел на основную версию сайта (если пользователь явно набрал адрес мобильной версии — оставим его на ней);
- проверим, есть ли такая страница в мобильной версии (не редиректить же его на 404).
На мобильную версию мы отправили пользователя, дадим ему также возможность перейти обратно на большую версию. В подвалах наших проектов содержится ссылка вида /go-health/ , по которой и осуществляется переход.
К сожалению, иногда страницы мобильной версии отличаются от основной. Та информация, которая легко помещается на большой версии, в мобильной разделяется на 3 страницы. Поэтому отбрасывать поддомен и редиректить на тот же URL, было бы неправильно. Мы выбрали следующий алгоритм:
- Определяем имя роута той страницы, на которой мы находимся.
- Функция контроллера может содержать специальный атрибут go_view_name . В этом случае мы редиректим на страницу с этим (другим) именем роута. Это нужно как раз для того случая, когда несколько страниц одной версии соответствуют одной странице большой версии.
- В остальных случаях редиректим на роут большой версии с тем же именем.
Атрибут go_url_name назначается через декоратор
А декоратор never_use_mobile ставит куку mobile для отмены автоматического редиректа
К сожалению, тач версии развиваются после основных разделов и не всегда соответствуют страницам на большой версии, поэтому такой код приходится держать. Атрибут go_view_name просто подменяет имя роута для страницы-аналога. Это довольно ограниченное решение, но его пока хватает.
3.3 404 для мобильной версииВы можете не просто сообщать пользователю о том, что страница не найдена, но и указать, что такая страница есть в полной версии сайта. При этом проверить URL по схеме URL'ов недостаточно: запрос /news/foo/ соответствует схеме URL'ов, а новости такой нет. Поэтому надо попытаться выполнить функцию контроллера в основной схеме урлов. Есть еще одна тонкость: надо подменять текущую схему URL'ов для большой версии, так как она нужна функциям reverse и тегу url. Иначе вы будете рендерить страницу большой версии в схеме URL'ов мобильной.
3.4 Мета теги alternate и canonicalЭти URL'ы строятся с помощью функций или шаблонных тегов приложения django-host.