"""
IPAddressField for models with IPv4 & IPv6 support
Requires: IPy
http://software.inl.fr/trac/wiki/IPy http://c0re.23.nu/c0de/IPy/
On Debian/Ubuntu: apt-get install python-ipy
Example:
class Host(models.Model):
hostname = models.CharField()
ip = IPAddressField()
>>> my_host = Host()
>>> my_host.hostname = 'server'
>>> my_host.ip = '192.168.0.1'
>>> my_host.save()
>>> my_host.ip
IP('192.168.0.1')
>>> my_host.ip = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
>>> dir(my_host.ip)
['NoPrefixForSingleIp', 'WantPrefixLen', '__add__', '__cmp__', '__contains__',
'__doc__', '__getitem__', '__hash__', '__init__', '__len__', '__module__',
'__nonzero__', '__repr__', '__str__', '_ipversion', '_prefixlen', '_printPrefix',
'broadcast', 'int', 'ip', 'iptype', 'len', 'make_net', 'net', 'netmask', 'overlaps',
'prefixlen', 'reverseName', 'reverseNames', 'strBin', 'strCompressed', 'strDec',
'strFullsize', 'strHex', 'strNetmask', 'strNormal', 'version']
>>> my_host.ip.reverseName()
'f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.ip6.arpa.'
>>> my_host.ip.version()
6
>>> my_host.ip = '1.2.3.4.5'
ValidationError: IPv4 Address with more than 4 bytes
>>> my_host.ip = 'aa:dd:xx'
ValidationError: 'aa:dd:xx': Invalid IPv6 address: should have 8 hextets
"""
from django.forms import ValidationError as FormValidationError
from django.core.exceptions import ValidationError
from django.forms import fields, widgets
from django.db import models
from decimal import Decimal
from IPy import IP
IP.__long__ = IP.int
def clean_ip(ip):
'Method for validating IPs on forms'
try:
IP(ip)
except Exception, e:
raise FormValidationError(e)
return ip
class IPAddressWidget(widgets.TextInput):
def render(self, name, value, attrs=None):
if isinstance(value, IP):
value = unicode(value)
return super(IPAddressWidget, self).render(name, value, attrs)
class IPAddressField(models.Field):
__metaclass__ = models.SubfieldBase
def db_type(self):
return 'numeric(39, 0)'
def to_python(self, value):
if not value:
return None
elif isinstance(value, Decimal):
value = long(value)
try:
return IP(value)
except Exception, e:
raise ValidationError(e)
def get_db_prep_lookup(self, lookup_type, value):
try:
if lookup_type in ('range', 'in'):
return [self.get_db_prep_value(v) for v in value]
return [self.get_db_prep_value(value)]
except ValidationError:
return super(IPAddressField, self).get_db_prep_lookup(lookup_type, value)
def get_db_prep_value(self, value):
try:
return long(self.to_python(value))
except TypeError:
return None
def formfield(self, **kwargs):
defaults = {
'form_class': fields.CharField,
'widget': IPAddressWidget,
}
defaults.update(kwargs)
return super(IPAddressField, self).formfield(**defaults)
Comments
Great snippet!
Instead of "return None" I'd "return IP(0)" just in case you need to compare the results with another IP instance. IPy doesn't like comparing IP's with non IP objects (like None or a string).
#
You may want to consider using 45chars which is the absolute maximum it could be. http://stackoverflow.com/questions/166132/maximum-length-of-the-textual-representation-of-an-ipv6-address/
#
nm, see http://djangosnippets.org/snippets/1961/
#