Вебразработчики частенько сталкиваются с классической задачей определения местоположения пользователя по его IP-адресу. Существует множество различных решений, например на основе мировой базы Maxmind Geolite или российской IpgeoBase. Все они обладают достаточно низкуровневыми API, ну оно и понятно: на входе айпишник, на выходе страна, либо город и, если повезёт, ещё какая-нибудь полезная информация.
У всех сайтов с GeoIP, которые мы запускали, есть общая черта: они не только нуждаются в простой геолокации, необходимо также выводить различный контент на сайте в зависимости от месторасположения пользователя. Чтобы упростить для себя эту задачу мы написали небольшую батарейку django-geoip, вдохновившись приложением django-ipgeobase.
Основное достоинство приложения: оно умеет автоматически определять географию пользователя и передавать её в объект request. Теперь контент на сайте, обладающий «региональностью», легко отфильтровать во views.py по значению request.location.
Одной из причин хранения данных в БД является потребность в создании своей модели географии, отвечающей задачам бизнес-логики. Например, в одном из проектов мы ограничиваем определение локации пользователя областями России, в другом — набором городов, в которых присутствует компания. Эта модель реализует паттерн «фасада» к иерархии географии ipgeobase, позволяя гибко настроить геолокацию под себя.
Представим, что наш сайт имеет несколько региональных «версий», каждая из которых может содержать свой контент. При этом регион может иметь произвольное имя и, к примеру, содержать несколько областей РФ (таблица geo_location на схеме):
Вот пример того, как это настраивается и работает. Определим свою модель географии MyCustomLocation:
Это обычная джанго-модель, дополненная тремя классметодами, реализующие “интерфейс” фасада GeoIP. Назначение каждой функции понятно из названия и докстринга. Осталось заполнить базу названиями городов и привязать их к моделям djagno-geoip.
Прописываем в settings.py:
Добавляем middleware для автоматического определения региона:
И вуаля: request.location теперь содержит значение нашей модели MyCustomLocation для каждого пользователя.
Если пользователь не оказался ни в одном из этих городов, ему будет присвоен регион по-умолчанию (get_default_location).
Данный подход сильно облегчает задачу создания «региональных» сайтов, отличающихся друг от друга содержимым в зависимости от месторасположения пользователя.
Приложение хотя и альфа, неплохо работает у нас в продакшене (версия 0.2.2). Статус альфа намекает, что в будущем API будет меняться. Мы планируем поддерживать и развивать его дальше, в том числе реализовать определение региона не только для России, но и для остального мира. Также показалось интересной идея оптимизации поиска подходящего IP-диапазона по базе.
Исходные коды доступны на гитхабе, жду ваших комментариев.
У всех сайтов с GeoIP, которые мы запускали, есть общая черта: они не только нуждаются в простой геолокации, необходимо также выводить различный контент на сайте в зависимости от месторасположения пользователя. Чтобы упростить для себя эту задачу мы написали небольшую батарейку django-geoip, вдохновившись приложением django-ipgeobase.
Django-geoip
Основное достоинство приложения: оно умеет автоматически определять географию пользователя и передавать её в объект request. Теперь контент на сайте, обладающий «региональностью», легко отфильтровать во views.py по значению request.location.
Фичи
- pip install django-geoip и ещё несколько простых шагов для установки;
- подробная документация на ReadTheDocs;
- покрытие тестами;
- обновление базы management-командой;
- понятный API для создания «своей» модели географии в дополнение к существующим;
- пользователь может поменять свой регион с помощью вьюхи;
- интеграция с django-hosts.
Особенности реализации
Приложение django-geoip поддерживает иерархию географии Страна — Область — Город, которая хранится в нормализованном виде в СУБД. Данные по диапазонам IP-адресов со связями ко всем элементам географии — в четвёртой таблице. Текущая версия работает только с данными ipgeobase.ru, это почти тысяча городов России и Украины и 150 тысяч IP-диапазонов.Одной из причин хранения данных в БД является потребность в создании своей модели географии, отвечающей задачам бизнес-логики. Например, в одном из проектов мы ограничиваем определение локации пользователя областями России, в другом — набором городов, в которых присутствует компания. Эта модель реализует паттерн «фасада» к иерархии географии ipgeobase, позволяя гибко настроить геолокацию под себя.
Представим, что наш сайт имеет несколько региональных «версий», каждая из которых может содержать свой контент. При этом регион может иметь произвольное имя и, к примеру, содержать несколько областей РФ (таблица geo_location на схеме):
Вот пример того, как это настраивается и работает. Определим свою модель географии MyCustomLocation:
# geo/models.py
class MyCustomLocation(GeoLocationFacade):
name = models.CharField(max_length=100)
region = models.OneToOneField(Region, related_name='my_custom_location')
is_default = models.BooleanField(default=False)
@classmethod
def get_by_ip_range(cls, ip_range):
""" Получаем модель географии по IP-дапазону.
В данном примере диапазон связан с регионом, тот, в свою очередь,
связан с нашей кастомной моделью географии
"""
return ip_range.region.geo_location
@classmethod
def get_default_location(cls):
""" Локация по-умолчанию, на случай, если не смогли определить местоположение по IP"""
return cls.objects.get(is_default=True)
@classmethod
def get_available_locations(cls):
return cls.objects.all()
class Meta:
db_table = 'geo_location'
Это обычная джанго-модель, дополненная тремя классметодами, реализующие “интерфейс” фасада GeoIP. Назначение каждой функции понятно из названия и докстринга. Осталось заполнить базу названиями городов и привязать их к моделям djagno-geoip.
Прописываем в settings.py:
GEOIP_LOCATION_MODEL = 'geo.models.MyCustomLocation'
Добавляем middleware для автоматического определения региона:
MIDDLEWARE_CLASSES = (...
'django_geoip.middleware.LocationMiddleware',
...
)
И вуаля: request.location теперь содержит значение нашей модели MyCustomLocation для каждого пользователя.
def my_view(request):
""" Passing location into template """
...
context['location'] = request.location
...
Если пользователь не оказался ни в одном из этих городов, ему будет присвоен регион по-умолчанию (get_default_location).
Данный подход сильно облегчает задачу создания «региональных» сайтов, отличающихся друг от друга содержимым в зависимости от месторасположения пользователя.
Что дальше
Приложение хотя и альфа, неплохо работает у нас в продакшене (версия 0.2.2). Статус альфа намекает, что в будущем API будет меняться. Мы планируем поддерживать и развивать его дальше, в том числе реализовать определение региона не только для России, но и для остального мира. Также показалось интересной идея оптимизации поиска подходящего IP-диапазона по базе.
Исходные коды доступны на гитхабе, жду ваших комментариев.