Django GeoIP — определение местоположения посетителя средствами Django Framework

На днях в одном из преоектов пришлось прикручивать функционал определения страны нахождения пользователя по IP адресу. По сути задача не сложная, если знать как это делается. Но основная проблема состояла в том, чтобы названия государств выводились на русском и/или английском языках. Постораюсь коротко, без лишней воды и болтовни, пошагово описать все действия по установке библиотек и настройке проекта.

Чтобы добавить в проект приложение Django GIS достаточно в INSTALLED_APPS вписать ‘django.contrib.gis’. Так пишут здесь. Но они не предупреждают, что все весь проект после этого разваливается, если не установлены необходимые библиотеки и базы. В статье использованы материалы официальной документации и собственные разработки.

Начнем с установки всех необходимых библиотек и баз данных. Что нам нужно?

  • Python 2.4+
  • Django
  • PostgreSQL (с PostGIS), MySQL или Oracle (я использую PostgreSQL)
  • GEOS
  • PROJ.4

Единственная нерешенная проблема — это ошибка приподключенном ‘haystack’. Поэтому пришлось временно удалить его из проекта.

Все устанавливаем с исходников

Установка GEOS

$ wget http://download.osgeo.org/geos/geos-3.2.2.tar.bz2
$ tar xjf geos-3.2.2.tar.bz2
$ cd geos-3.2.2
$ ./configure
$ make
$ sudo make install
$ cd ..

Установка PROJ.4

Это библиотека, конвертирующая геопространственные данные в различных системах координат.

$ wget http://download.osgeo.org/proj/proj-4.7.0.tar.gz
$ wget http://download.osgeo.org/proj/proj-datumgrid-1.5.zip
$ tar xzf proj-4.7.0.tar.gz
$ cd proj-4.7.0/nad
$ unzip ../../proj-datumgrid-1.5.zip
$ cd ..
$ ./configure
$ make
$ sudo make install
$ cd ..

Установка PostGIS

PostGIS добавляет поддержку географического объекта PostgreSQL, превратив его в базу картографических данных. GEOS и PROJ.4 должны быть установлены до начала сборки PostGIS.

$ wget http://postgis.refractions.net/download/postgis-1.5.2.tar.gz
$ tar xzf postgis-1.5.2.tar.gz
$ cd postgis-1.5.2
$ ./configure
$ make
$ sudo make install
$ cd ..

Создание пространственного шаблон базы данных для PostGIS

$ sudo su - postgres

Дальше на странице документации качаем подходящий скрипт и запускаем. После этого вы можете создать пространственную базу данных, просто указав template_postgis как шаблон для использования (с помощью опции-T):

$ createdb -T template_postgis <db name>

Библиотеки установлены. Теперь качаем базы стран и городов. Распаковываем. Я для удобства файлы размещаю в директории MEDIA_ROOT/geoip

Вот теперь пора работать с settings.py проекта:

INSTALLED_APPS = (
    #....................
    'django.contrib.gis',
    #....................
)
#..............................................
GEOIP_PATH = os.path.join(MEDIA_ROOT, 'geoip')
GEOIP_COUNTRY = 'GeoIP.dat'
GEOIP_CITY = 'GeoLiteCity.dat'

Теперь можете тестировать. Код для тестирования я не буду копипастить. Смотрите здесь docs.djangoproject.com/en/1.3/ref/contrib/gis/geoip/#example. Да и документацию, думаю, не стоит переводить. Следующий вопрос, которому стоит уделить внимание, это русификация. Как получить название страны на русском языке?

Для этого я создал три модели:

class WorldPart(models.Model):
    name_ru = models.CharField(max_length=255, verbose_name=_('Name RU'), null=True, blank=True)
    name_en = models.CharField(max_length=255, verbose_name=_('Name EN'), null=True, blank=True)

    def __unicode__(self):
        return self.name_ru

    class Meta:
        ordering = ['name_ru', 'name_en',]
        verbose_name = _('Part of the World')
        verbose_name_plural = _('Parts of the World')

class Country(models.Model):
    name_ru = models.CharField(max_length=255, verbose_name=_('Name RU'), null=True, blank=True)
    name_en = models.CharField(max_length=255, verbose_name=_('Name EN'), null=True, blank=True)
    code = models.CharField(max_length=2, verbose_name=_('Code'), null=True, blank=True)
    worldpart = models.ForeignKey(WorldPart, verbose_name=_('Part of the World'), null=True, blank=True)
    flag = models.ImageField(upload_to=os.path.join(settings.STATIC_ROOT, 'img', 'flags'), verbose_name=_('Flag'), null=True, blank=True)
    is_active = models.BooleanField(verbose_name=_('Is active'))

    def __unicode__(self):
        return self.name_ru

    class Meta:
        ordering = ['name_ru', 'name_en',]
        verbose_name = _('Country')
        verbose_name_plural = _('Countries')

class City(models.Model):
    country = models.ForeignKey(Country, verbose_name=_('Country'))
    name_ru = models.CharField(max_length=255, verbose_name=_('Name RU'), null=True, blank=True)
    name_en = models.CharField(max_length=255, verbose_name=_('Name EN'), null=True, blank=True)
    region = models.CharField(max_length=100, verbose_name=_('Region'), null=True, blank=True)
    postal_code = models.CharField(max_length=10, verbose_name=_('Postal Code'), null=True, blank=True)
    latitude = models.CharField(max_length=255, verbose_name=_('Lattitude'), null=True, blank=True)
    longitude = models.CharField(max_length=255, verbose_name=_('Longitude'), null=True, blank=True)
    is_active = models.BooleanField(verbose_name=_('Is active'))

    def __unicode__(self):
        return self.name_ru

    class Meta:
        ordering = ['name_ru', 'name_en',]
        verbose_name = _('City')
        verbose_name_plural = _('Cities')

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


А работает весь этот механизм следующим образом.

# Подключаем модуль
>>> from django.contrib.gis.utils import GeoIP
>>> from myapp.models import *
>>> g = GeoIP()
Получаем название государства
>>> cn = g.country('google.com')
>>> cn
{'country_code': 'US', 'country_name': 'United States'}
>>> try:
>>>     cn_db = Country.objects.get(code=cn['country_code'])
>>> except Country.DoesNotExist:
>>>     pass

На этом всё. Возможно что то лишнее, но я постарался максимально сжато указать на основные моменты. Спасибо всем за внимание.