Back to Home

GeoIP and Django

django · geoip · ipgeobase · futurecolors

GeoIP and Django

    Web developers often face the classic task of locating a user by his IP address. There are many different solutions, for example, based on the world Maxmind Geolite base or the Russian IpgeoBase. All of them have fairly low-level APIs, which is understandable: at the entrance, IP, at the exit, a country or city and, if you're lucky, some other useful information.

    All the GeoIP sites that we launched have a common feature: they not only need simple geolocation, it is also necessary to display various content on the site depending on the user's location. To simplify this task for ourselves, we wrote a small django-geoip battery , inspired by the django-ipgeobase application.


    Django-geoip


    The main advantage of the application: it can automatically determine the user's geography and pass it to the request object. Now the content on the site, which has "regionality", is easy to filter in views.py by request.location value.

    Features
    • pip install django-geoip and a few simple steps to install;
    • detailed documentation on ReadTheDocs;
    • test coverage ;
    • updating the database with a management team;
    • A clear API for creating “your” geography model in addition to existing ones;
    • the user can change his region using the view ;
    • integration with django-hosts .


    Implementation Features

    The django-geoip application supports the geography hierarchy Country - Region - City, which is stored in a normalized form in the DBMS. Data on the ranges of IP addresses with links to all elements of geography is in the fourth table. The current version works only with ipgeobase.ru data, it is almost a thousand cities in Russia and Ukraine and 150 thousand IP ranges.

    One of the reasons for storing data in the database is the need to create your own geography model that meets the challenges of business logic. For example, in one of the projects we limit the definition of a user's location to regions of Russia, in the other - to the set of cities in which the company is present. This model implements the “facade” pattern for the ipgeobase geography hierarchy, allowing you to flexibly configure geolocation for yourself.

    Imagine that our site has several regional “versions”, each of which may contain its own content. In this case, the region may have an arbitrary name and, for example, contain several regions of the Russian Federation (the geo_location table in the diagram):

    Here is an example of how this is configured and works. Define your geography model 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'
    

    This is a common django model, supplemented by three class methods that implement the “interface” of the GeoIP facade. The purpose of each function is clear from the name and dockstring. It remains to fill the database with the names of the cities and bind them to djagno-geoip models.

    We write in settings.py:
    GEOIP_LOCATION_MODEL = 'geo.models.MyCustomLocation'
    

    Add middleware to automatically detect the region:
    MIDDLEWARE_CLASSES = (...
        'django_geoip.middleware.LocationMiddleware',
        ...
    )
    

    And voila: request.location now contains the value of our MyCustomLocation model for each user.
    def my_view(request):
        """ Passing location into template """
        ...
        context['location'] = request.location
        ...
    

    If the user is not in any of these cities, he will be assigned the default region (get_default_location).

    This approach greatly facilitates the task of creating “regional” sites that differ from each other in content depending on the location of the user.

    What's next


    The application, although alpha, works well in our production (version 0.2.2). Alpha status hints that the API will change in the future. We plan to maintain and develop it further, including implementing the definition of a region not only for Russia, but also for the rest of the world . The idea of optimizing the search for a suitable IP range in the database also seemed interesting .

    Source codes are available on github , waiting for your comments.

    Read Next