Login

MaxMind(R) GeoIP Lite geolocation models

Author:
jbronn
Posted:
July 18, 2007
Language:
Python
Version:
.96
Tags:
log gis ip geolocation maxmind geodjango
Score:
2 (after 2 ratings)

This provides GeoDjango models for the maxmind GeoIP Lite data products. Use the corresponding CSV import script for data import. Requires: GeoDjango and the BigIntegerField patch by Peter Nixon.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# Copyright (c) 2007, Justin Bronn
# All rights reserved.
#
# Released under New BSD License
#
"""
  This module contains example models for the MaxMind(R) free
  geolocation datasets, specifically, GeoLite Country and City:
   http://www.maxmind.com/app/geolitecity
   http://www.maxmind.com/app/geoip_country

  To search for IP addresses requires long integers, however, Django
  does not yet support these fields.  These models require the BigIntegerField
  patch by Peter Nixon:
   http://code.djangoproject.com/attachment/ticket/399/django-bigint-20070712.patch
"""

from django.contrib.gis.db import models
import re

ipregex = re.compile(r'^(?P<w>\d\d?\d?)\.(?P<x>\d\d?\d?)\.(?P<y>\d\d?\d?)\.(?P<z>\d\d?\d?)$')
def ip2long(ip):
    "Converts an IP address into a long suitable for querying."
    m = ipregex.match(ip)
    if m:
        w, x, y, z = map(int, [m.group(g) for g in 'wxyz'])
        ipnum = 16777216*w + 65536*x + 256*y + z
        return ipnum
    else:
        raise TypeError, 'Invalid IP Address: %s' % ip

class IPManager(models.GeoManager):
    def ipquery(self, ip):
        "Returns any LocationBlocks matching the IP query."
        num = ip2long(ip)
        # Using select_related slows down this query a lot
        qs = self.get_query_set().filter(ipfrom__lte=num).filter(ipto__gte=num)
        if len(qs) == 1: return qs[0]
        else: return qs

class Country(models.Model, models.GeoMixin):
    name = models.CharField(maxlength=50)
    code = models.CharField(maxlength=2)
    point = models.PointField(null=True)
    objects = models.GeoManager()
    def __unicode__(self): return self.name

class CountryBlock(models.Model):
    ipfrom = models.BigIntegerField(db_index=True)
    ipto = models.BigIntegerField(db_index=True)
    startip = models.IPAddressField()
    endip = models.IPAddressField()
    country = models.ForeignKey(Country)
    objects = IPManager()
    def __unicode__(self): return unicode('%s: %s to %s' % (self.country.name, self.startip, self.endip))

class Location(models.Model, models.GeoMixin):
    locid = models.IntegerField(db_index=True)
    country = models.ForeignKey(Country)
    region = models.CharField(maxlength=7, blank=True)
    city = models.CharField(maxlength=41, blank=True)
    postalcode = models.CharField(maxlength=7, blank=True)
    dmacode = models.CharField(maxlength=3, blank=True)
    areacode = models.CharField(maxlength=3, blank=True)
    point = models.PointField()
    objects = models.GeoManager()
    def __unicode__(self):
        return unicode('Location %d: %s - %s, %s %s' % (self.locid, self.country.name, self.region, self.city, self.postalcode))

class LocationBlock(models.Model):
    location = models.ForeignKey(Location)
    ipfrom = models.BigIntegerField(db_index=True)
    ipto = models.BigIntegerField(db_index=True)
    objects = IPManager()

More like this

  1. MaxMind(R) GeoIP Lite CSV Import by jbronn 7 years, 8 months ago
  2. WorldIP - access to IP database over API by Alrond 6 years, 6 months ago
  3. Google v3 geocoding for Geodjango admin site by samhag 2 years, 4 months ago
  4. Save Geolocation for an Address based model on save() by publicFunction 1 year, 8 months ago
  5. PatchModelForm - A ModelForm subclass with the semantics of the PATCH HTTP method by gnrfan 2 years, 10 months ago

Comments

adroffner (on February 6, 2008):

The ip2long(ip) can be done more effeciently via the Python struct & socket modules. They use the base C nhtoh() family. I also provided the inverse function.

I tested these on i686 hardware to be sure the unsigned integers matched the MaxMind CSV data sets. Other hardware may need the endian flag '>' switched around (See struct module docs).

import socket, struct

def ip4_to_int(ip): "Converts an IPv4 address, e.g. '208.0.1.4', into an unsigned long integer." return struct.unpack('>L',socket.inet_aton(ip))[0]

def int_to_ip4(num): "Converts an unsigned long integer into an IPv4 address." return socket.inet_ntoa(struct.pack('>L',num))

#

adroffner (on February 7, 2008):

MySQL Users Limitations

GeoDjango's MySQL support has odd limitations. Here are some that I've found: 1) Only MyISAM Tables support a SPATIAL INDEX 2) GIS Fields, e.g. PointField(), must be non-NULL. Use: my_point = PointField(null=False) 3) Every GIS field gets a SPATIAL INDEX by default!

#

Please login first before commenting.