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
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
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))
#
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.