Basically, and IP address is just 4 bytes of data, but it's text representation can use between 7 and 15 bytes. That's not a big different when your model will have few rows, but it's a different when you'll have a huge set of IP addresses, and specially if you want to join tables by it.
The only inconvenient of storing the IPs as numbers is that are not human readable if you want to check them directly to database.
So, here you have my code that can be used as a replacement of IPAddressField:
import IPy
from django.db import models
from django import forms
from django.utils.translation import ugettext as _
def _ip_to_int(ip):
return IPy.IP(ip).ip
def _int_to_ip(numeric_ip):
return IPy.IP(numeric_ip).strNormal()
class IPFormField(forms.fields.Field):
def clean(self, value):
try:
_ip_to_int(value)
except ValueError:
raise forms.ValidationError, \
_('You must provide a valid IP address')
return super(IPFormField, self).clean(value)
class IPField(models.fields.PositiveIntegerField):
'''
IP field for django for storing IPs as integers on database
(Django's field IPAddressField stores them as text)
'''
__metaclass__ = models.SubfieldBase
def to_python(self, value):
if value:
if isinstance(value, long):
return _int_to_ip(value)
else:
return value
else:
return None
def get_db_prep_save(self, value):
try:
result = _ip_to_int(value)
except ValueError:
result = None
return result
def get_db_prep_value(self, value):
if value:
return _ip_to_int(value)
else:
return None
def get_db_prep_lookup(self, lookup_type, value):
return super(IPField, self).get_db_prep_lookup(
lookup_type,
_ip_to_int(value)
)
def formfield(self, **kwargs):
defaults = {'form_class': IPFormField}
defaults.update(kwargs)
return super(IPField, self).formfield(**defaults)
NOTE: This code requires IPy, a single file python library to work with IP addresses.
That's a neat solution, but if you are using a database that has its own storage backend for IP-addresses, I'd suggest to use that instead – like these from PostgreSQL:
ReplyDeletehttp://www.postgresql.org/docs/8.3/static/datatype-net-types.html
Do you think you could post an example that works with both ipv4 and ipv6 ip addresses?
ReplyDeleteThis is working for ipv4 and ipv6 ip's using postgres inet network fields..
ReplyDeletefrom django.db import models
from django.contrib import admin
from django.forms import ValidationError as FormValidationError
from django.core.exceptions import ValidationError
from django.forms import fields
from decimal import Decimal
from IPy import IP
class IPAddressFormField(fields.Field):
default_error_messages = {
'invalid': u'Enter a valid IPv4 or IPv6 address.',
}
def clean(self, value):
'Method for validating IPs on forms'
if value in fields.EMPTY_VALUES:
return u''
try:
IP(value)
except Exception, e:
raise FormValidationError(self.error_messages['invalid'])
return super(IPAddressFormField, self).clean(value)
class IPAddressField(models.Field):
__metaclass__ = models.SubfieldBase
def db_type(self):
return 'inet'
def to_python(self, value):
if not value or ((isinstance(value, str) or isinstance(value, unicode)) and value == ''):
return None
try:
return IP(value)
except Exception, e:
raise ValidationError(e)
def get_db_prep_value(self, value):
value = self.to_python(value)
if not value:
return None
else:
return value
def formfield(self, **kwargs):
defaults = {'form_class': IPAddressFormField }
defaults.update(kwargs)
return super(IPAddressField, self).formfield(**defaults)
Here is the example I came up with. Sorry for pasting the code in here previously.
ReplyDeletehttp://www.djangosnippets.org/snippets/1453/
It not work for django 1.2+
ReplyDelete