Tuned IPAddressField with IPv4 & IPv6 support

  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
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
"""
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)

More like this

  1. Tuned IPAddressField with IPv4 & IPv6 support using Postgres Network Field type by illsci 4 years, 2 months ago
  2. IPAddressField with CIDR support by SeniorHuevo 3 years, 3 months ago
  3. MAC address field by diverman 4 years, 3 months ago
  4. Storing IP address ModelField as Integer, without nasty hacks.. by sleepycal 1 year, 11 months ago
  5. Include entire networks in INTERNAL_IPS setting by pmclanahan 4 years, 3 months ago

Comments

jono (on October 30, 2010):

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).

#

rizumu (on July 5, 2011):

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/

#

rizumu (on July 5, 2011):

nm, see http://djangosnippets.org/snippets/1961/

#

(Forgotten your password?)