[Python-modules-commits] [python-django-netfields] 01/03: importing python-django-netfields_0.7.orig.tar.gz
Michael Fladischer
fladi at moszumanska.debian.org
Mon Jun 19 08:08:51 UTC 2017
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch master
in repository python-django-netfields.
commit 741ac482e0f38bb54e2ef7b4aefe8c8ea9d1d2a7
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Fri Jun 16 09:55:44 2017 +0200
importing python-django-netfields_0.7.orig.tar.gz
---
AUTHORS | 7 +
CHANGELOG | 74 ++++
LICENSE | 27 ++
MANIFEST.in | 7 +
README.rst | 132 +++++++
manage.py | 10 +
netfields/__init__.py | 5 +
netfields/apps.py | 66 ++++
netfields/fields.py | 161 +++++++++
netfields/forms.py | 95 ++++++
netfields/lookups.py | 165 +++++++++
netfields/mac.py | 7 +
netfields/managers.py | 83 +++++
netfields/models.py | 0
netfields/psycopg2_types.py | 31 ++
netfields/rest_framework.py | 35 ++
requirements.txt | 3 +
setup.py | 49 +++
test/__init__.py | 0
test/models.py | 90 +++++
test/tests/__init__.py | 0
test/tests/test_form_fields.py | 212 ++++++++++++
test/tests/test_rest_framework_fields.py | 50 +++
test/tests/test_sql_fields.py | 568 +++++++++++++++++++++++++++++++
testsettings.py | 19 ++
tox.ini | 114 +++++++
26 files changed, 2010 insertions(+)
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..c575dda
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,7 @@
+Written to provide better INET and CIDR handling for Network Administration
+Visualised (http://metanav.uninett.no).
+
+ * Thomas Adamcik
+ * Magnus Eide
+ * Ewoud Kohl van Wijngaarden
+ * James Oakley
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..534c6c3
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,74 @@
+- 0.7
+ * Added support for Django 1.10
+ * Added __overlaps lookup (Emre Hasegeli)
+ * Fixed __in lookups of empty lists (Simeon J Morgan)
+
+- 0.6
+ * Removed support for Django 1.7
+ * MACAddressField fixes and cleanups (Andreas Rammhold)
+ * Support for ArrayField from django.contrib.postgres. Note that the types
+ returned in the array will not be the proper types returned by the
+ non-array fields in Django < 1.9.6 due to
+ https://code.djangoproject.com/ticket/25143
+
+- 0.5.3
+ * Add max_prefixlen and min_prefixlen lookups (Hugo Castilho)
+ * Changed field subclassing to Django 1.8+ format (Antwan86)
+ * Use the ipaddress module instead of py2-ipaddress for Python 2.x
+ * Standard lookups now inherit from base lookups
+
+- 0.5.2
+ * Support for Django 1.9
+ * Invalid lookups on Django < 1.9 will now raise FieldError or
+ NotImplementedError to match Django 1.9
+
+- 0.5.1
+ * Fixed form validation error messages to avoid confusion
+ * Updated README to reflect the change to the ipaddress module
+
+- 0.5
+ * Switched InetAddressField and CidrAddressField from netaddr to the
+ ipaddress module found in core Python >= 3.3. For Python < 3 the backported
+ py2-ipaddress module will be used. The API of these objects are mostly
+ compatible with the netaddr API and do not suffer from some subtle issues
+ that netaddr objects have when used in Django forms. Most applications
+ should not require any adjustments as a result of this change
+
+- 0.4.1
+ * Added serializer fields for Django REST Framework. Thanks to Brandon
+ Cazander
+ * Added a store_prefix_length argument to InetAddressField. If set to False
+ an IPAddress will be returned instead of an IPNetwork. If there is a prefix
+ length before conversion, it will be truncated
+
+- 0.4
+ * Return IPNetwork object from InetAddressField instead of IPAddress. This
+ better matches the capabilities of the underlying inet type since it can
+ contain an embedded prefix length/netmask. This change may affect some
+ users
+ * Added __family lookups for filtering by address family, eg:
+ 'filter(ip__family=4)' to select only IPv4 addresses. Thanks to leifurhauks
+ * Removed support for Django < 1.7
+
+- 0.3.1
+ * Fix CidrAddressField in lookups with single-item lists #39
+ * Fix validation of CIDR with bits to right of mask #19
+
+- 0.3
+ * Added support for Django 1.7/1.8 (Antwan86, smclenithan)
+ * Added support for Python 3.x
+ * Removed support for Django 1.4
+
+- 0.2.2
+ * Support for Django 1.6 (Jay McEntire)
+
+- 0.2.1
+ * Fix net_contained lookups in InetAddressField
+
+- 0.2
+ * Fix IP type casting in form fields
+ * Add South introspection rules
+ * Expand tests for form fields and MAC addresses
+ * Use netaddr instead of IPy for address representation. New requirement of
+ netaddr module. This change affects backwards compatibility
+ * New maintainer James Oakley <jfunk at funktronics.ca>
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9afb566
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) Thomas Adamcik and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ 3. Neither the name of Django nor the names of its contributors may be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..dc1948a
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,7 @@
+include AUTHORS
+include CHANGELOG
+include LICENSE
+include README.rst
+include requirements.txt
+include manage.py
+include testsettings.py
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..e34e0e8
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,132 @@
+Django PostgreSQL Netfields
+===========================
+
+.. image:: https://secure.travis-ci.org/jimfunk/django-postgresql-netfields.png
+
+This project is an attempt at making proper PostgreSQL net related fields for
+Django. In Django pre 1.4 the built in ``IPAddressField`` does not support IPv6
+and uses an inefficient ``HOST()`` cast in all lookups. As of 1.4 you can use
+``GenericIPAddressField`` for IPv6, but the casting problem remains.
+
+In addition to the basic ``IPAddressField`` replacement a ``CIDR`` and
+a ``MACADDR`` field have been added. This library also provides a manager that
+allows for advanced IP based lookup directly in the ORM.
+
+In Python, the values of the IP address fields are represented as types from
+the ipaddress_ module. In Python 2.x, a backport_ is used. The MAC address
+field is represented as an EUI type from the netaddr_ module.
+
+.. _ipaddress: https://docs.python.org/3/library/ipaddress.html
+.. _backport: https://pypi.python.org/pypi/ipaddress/
+.. _netaddr: http://pythonhosted.org/netaddr/
+
+Dependencies
+------------
+
+Current version of code is targeting Django >= 1.8 support, as this relies
+heavily on ORM internals and supporting multiple versions is especially tricky.
+
+Getting started
+---------------
+
+Make sure ``netfields`` is in your ``PYTHONPATH`` and in ``INSTALLED_APPS``.
+
+``InetAddressField`` will store values in PostgreSQL as type ``INET``. In
+Python, the value will be represented as an ``ipaddress.ip_interface`` object
+representing an IP address and netmask/prefix length pair unless the
+``store_prefix_length`` argument is set to `False``, in which case the value
+will be represented as an ``ipaddress.ip_address`` object.
+
+ from netfields import InetAddressField, NetManager
+
+ class Example(models.Model):
+ inet = InetAddressField()
+ # ...
+
+ objects = NetManager()
+
+``CidrAddressField`` will store values in PostgreSQL as type ``CIDR``. In
+Python, the value will be represented as an ``ipaddress.ip_network`` object.
+
+ from netfields import CidrAddressField, NetManager
+
+ class Example(models.Model):
+ inet = CidrAddressField()
+ # ...
+
+ objects = NetManager()
+
+``MACAddressField`` will store values in PostgreSQL as type ``MACADDR``. In
+Python, the value will be represented as a ``netaddr.EUI`` object. Note that
+the default text representation of EUI objects is not the same as that of the
+``netaddr`` module. It is represented in a format that is more commonly used
+in network utilities and by network administrators (``00:11:22:aa:bb:cc``).
+
+ from netfields import MACAddressField, NetManager
+
+ class Example(models.Model):
+ inet = MACAddressField()
+ # ...
+
+For ``InetAddressField`` and ``CidrAddressField``, ``NetManager`` is required
+for the extra lookups to be available. Lookups for ``INET`` and ``CIDR``
+database types will be handled differently than when running vanilla Django.
+All lookups are case-insensitive and text based lookups are avoided whenever
+possible. In addition to Django's default lookup types the following have been
+added:
+
+``__net_contained``
+ is contained within the given network
+
+``__net_contained_or_equal``
+ is contained within or equal to the given network
+
+``__net_contains``
+ contains the given address
+
+``__net_contains_or_equals``
+ contains or is equal to the given address/network
+
+``__net_overlaps``
+ contains or contained by the given address
+
+``__family``
+ matches the given address family
+
+These correspond with the operators and functions from
+http://www.postgresql.org/docs/9.4/interactive/functions-net.html
+
+``CidrAddressField`` includes two extra lookups:
+
+``__max_prefixlen``
+ Maximum value (inclusive) for ``CIDR`` prefix, does not distinguish between IPv4 and IPv6
+
+``__min_prefixlen``
+ Minimum value (inclusive) for ``CIDR`` prefix, does not distinguish between IPv4 and IPv6
+
+Related Django bugs
+-------------------
+
+* 11442_ - Postgresql backend casts inet types to text, breaks IP operations and IPv6 lookups.
+* 811_ - IPv6 address field support.
+
+https://docs.djangoproject.com/en/dev/releases/1.4/#extended-ipv6-support is also relevant
+
+.. _11442: http://code.djangoproject.com/ticket/11442
+.. _811: http://code.djangoproject.com/ticket/811
+
+
+Similar projects
+----------------
+
+https://bitbucket.org/onelson/django-ipyfield tries to solve some of the same
+issues as this library. However, instead of supporting just postgres via the proper
+fields types the ipyfield currently uses a ``VARCHAR(39)`` as a fake unsigned 64 bit
+number in its implementation.
+
+History
+-------
+
+Main repo was originaly kept https://github.com/adamcik/django-postgresql-netfields
+Late April 2013 the project was moved to https://github.com/jimfunk/django-postgresql-netfields
+to pass the torch on to someone who actually uses this code actively :-)
diff --git a/manage.py b/manage.py
new file mode 100755
index 0000000..819d703
--- /dev/null
+++ b/manage.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+import os
+import sys
+
+if __name__ == "__main__":
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testsettings")
+
+ from django.core.management import execute_from_command_line
+
+ execute_from_command_line(sys.argv)
diff --git a/netfields/__init__.py b/netfields/__init__.py
new file mode 100644
index 0000000..bb53988
--- /dev/null
+++ b/netfields/__init__.py
@@ -0,0 +1,5 @@
+from netfields.managers import NetManager
+from netfields.fields import (InetAddressField, CidrAddressField,
+ MACAddressField)
+
+default_app_config = 'netfields.apps.NetfieldsConfig'
diff --git a/netfields/apps.py b/netfields/apps.py
new file mode 100644
index 0000000..286abe5
--- /dev/null
+++ b/netfields/apps.py
@@ -0,0 +1,66 @@
+import django
+from django.apps import AppConfig
+from django.db.models import Field
+
+from netfields.fields import CidrAddressField, InetAddressField
+from netfields.lookups import (
+ EndsWith,
+ Family,
+ IEndsWith,
+ IRegex,
+ IStartsWith,
+ InvalidLookup,
+ InvalidSearchLookup,
+ MaxPrefixlen,
+ MinPrefixlen,
+ NetContained,
+ NetContainedOrEqual,
+ NetContains,
+ NetContainsOrEquals,
+ NetOverlaps,
+ Regex,
+ StartsWith,
+)
+
+
+class NetfieldsConfig(AppConfig):
+ name = 'netfields'
+
+ if django.VERSION < (1, 9):
+ for lookup in Field.class_lookups.keys():
+ if lookup not in ['contains', 'startswith', 'endswith', 'icontains', 'istartswith', 'iendswith', 'isnull', 'in',
+ 'exact', 'iexact', 'regex', 'iregex', 'lt', 'lte', 'gt', 'gte', 'equals', 'iequals', 'range', 'search']:
+ invalid_lookup = InvalidLookup
+ invalid_lookup.lookup_name = lookup
+ CidrAddressField.register_lookup(invalid_lookup)
+ InetAddressField.register_lookup(invalid_lookup)
+ CidrAddressField.register_lookup(InvalidSearchLookup)
+ InetAddressField.register_lookup(InvalidSearchLookup)
+
+ CidrAddressField.register_lookup(EndsWith)
+ CidrAddressField.register_lookup(IEndsWith)
+ CidrAddressField.register_lookup(StartsWith)
+ CidrAddressField.register_lookup(IStartsWith)
+ CidrAddressField.register_lookup(Regex)
+ CidrAddressField.register_lookup(IRegex)
+ CidrAddressField.register_lookup(NetContained)
+ CidrAddressField.register_lookup(NetContains)
+ CidrAddressField.register_lookup(NetContainedOrEqual)
+ CidrAddressField.register_lookup(NetContainsOrEquals)
+ CidrAddressField.register_lookup(NetOverlaps)
+ CidrAddressField.register_lookup(Family)
+ CidrAddressField.register_lookup(MaxPrefixlen)
+ CidrAddressField.register_lookup(MinPrefixlen)
+
+ InetAddressField.register_lookup(EndsWith)
+ InetAddressField.register_lookup(IEndsWith)
+ InetAddressField.register_lookup(StartsWith)
+ InetAddressField.register_lookup(IStartsWith)
+ InetAddressField.register_lookup(Regex)
+ InetAddressField.register_lookup(IRegex)
+ InetAddressField.register_lookup(NetContained)
+ InetAddressField.register_lookup(NetContains)
+ InetAddressField.register_lookup(NetContainedOrEqual)
+ InetAddressField.register_lookup(NetContainsOrEquals)
+ InetAddressField.register_lookup(NetOverlaps)
+ InetAddressField.register_lookup(Family)
diff --git a/netfields/fields.py b/netfields/fields.py
new file mode 100644
index 0000000..4312503
--- /dev/null
+++ b/netfields/fields.py
@@ -0,0 +1,161 @@
+from django.core.exceptions import ValidationError
+from django.db import models
+from django.utils.six import with_metaclass, text_type
+from ipaddress import ip_interface, ip_network
+from netaddr import EUI
+from netaddr.core import AddrFormatError
+
+from netfields.forms import InetAddressFormField, CidrAddressFormField, MACAddressFormField
+from netfields.mac import mac_unix_common
+from netfields.managers import NET_OPERATORS, NET_TEXT_OPERATORS
+from netfields.psycopg2_types import Inet, Macaddr
+
+
+class _NetAddressField(models.Field):
+ empty_strings_allowed = False
+
+ def __init__(self, *args, **kwargs):
+ kwargs['max_length'] = self.max_length
+ super(_NetAddressField, self).__init__(*args, **kwargs)
+
+ def from_db_value(self, value, expression, connection, context):
+ return self.to_python(value)
+
+ def to_python(self, value):
+ if not value:
+ return value
+
+ if isinstance(value, bytes):
+ value = value.decode('ascii')
+
+ try:
+ return self.python_type()(value)
+ except ValueError as e:
+ raise ValidationError(e)
+
+ def get_prep_lookup(self, lookup_type, value):
+
+ if (lookup_type in NET_OPERATORS and
+ NET_OPERATORS[lookup_type] not in NET_TEXT_OPERATORS):
+ if (lookup_type.startswith('net_contained') or
+ lookup_type.endswith('prefixlen')) and value is not None:
+ # Argument will be CIDR
+ return str(value)
+ return self.get_prep_value(value)
+
+ return super(_NetAddressField, self).get_prep_lookup(
+ lookup_type, value)
+
+ def get_prep_value(self, value):
+ if not value:
+ return None
+
+ return str(self.to_python(value))
+
+ def get_db_prep_value(self, value, connection, prepared=False):
+ if not value:
+ return None
+
+ return Inet(self.get_prep_value(value))
+
+ def get_db_prep_lookup(self, lookup_type, value, connection,
+ prepared=False):
+ if not value:
+ return []
+
+ if (lookup_type in NET_OPERATORS and
+ NET_OPERATORS[lookup_type] not in NET_TEXT_OPERATORS):
+ return [value] if prepared else [self.get_prep_value(value)]
+
+ return super(_NetAddressField, self).get_db_prep_lookup(
+ lookup_type, value, connection=connection, prepared=prepared)
+
+ def formfield(self, **kwargs):
+ defaults = {'form_class': self.form_class()}
+ defaults.update(kwargs)
+ return super(_NetAddressField, self).formfield(**defaults)
+
+ def deconstruct(self):
+ name, path, args, kwargs = super(_NetAddressField, self).deconstruct()
+ if self.max_length is not None:
+ kwargs['max_length'] = self.max_length
+ return name, path, args, kwargs
+
+
+class InetAddressField(_NetAddressField):
+ description = "PostgreSQL INET field"
+ max_length = 39
+
+ def __init__(self, *args, **kwargs):
+ self.store_prefix_length = kwargs.pop('store_prefix_length', True)
+ super(InetAddressField, self).__init__(*args, **kwargs)
+
+ def db_type(self, connection):
+ return 'inet'
+
+ def python_type(self):
+ return ip_interface
+
+ def to_python(self, value):
+ value = super(InetAddressField, self).to_python(value)
+ if value:
+ if self.store_prefix_length:
+ return value
+ else:
+ return value.ip
+ return value
+
+ def form_class(self):
+ return InetAddressFormField
+
+
+class CidrAddressField(_NetAddressField):
+ description = "PostgreSQL CIDR field"
+ max_length = 43
+ python_type = ip_network
+
+ def db_type(self, connection):
+ return 'cidr'
+
+ def python_type(self):
+ return ip_network
+
+ def form_class(self):
+ return CidrAddressFormField
+
+
+class MACAddressField(models.Field):
+ description = "PostgreSQL MACADDR field"
+ max_length = 17
+
+ def db_type(self, connection):
+ return 'macaddr'
+
+ def from_db_value(self, value, expression, connection, context):
+ return self.to_python(value)
+
+ def to_python(self, value):
+ if not value:
+ return value
+
+ try:
+ return EUI(value, dialect=mac_unix_common)
+ except AddrFormatError as e:
+ raise ValidationError(e)
+
+ def get_prep_value(self, value):
+ if not value:
+ return None
+
+ return text_type(self.to_python(value))
+
+ def get_db_prep_value(self, value, connection, prepared=False):
+ if not value:
+ return None
+
+ return Macaddr(self.get_prep_value(value))
+
+ def formfield(self, **kwargs):
+ defaults = {'form_class': MACAddressFormField}
+ defaults.update(kwargs)
+ return super(MACAddressField, self).formfield(**defaults)
diff --git a/netfields/forms.py b/netfields/forms.py
new file mode 100644
index 0000000..d19eeb9
--- /dev/null
+++ b/netfields/forms.py
@@ -0,0 +1,95 @@
+from ipaddress import ip_interface, ip_network, _IPAddressBase, _BaseNetwork
+from netaddr import EUI, AddrFormatError
+
+from django import forms
+import django
+from django.forms.utils import flatatt
+from django.utils.safestring import mark_safe
+from django.core.exceptions import ValidationError
+
+from netfields.mac import mac_unix_common
+
+
+class NetInput(forms.Widget):
+ input_type = 'text'
+
+ def render(self, name, value, attrs=None):
+ # Default forms.Widget compares value != '' which breaks IP...
+ if value is None:
+ value = ''
+ final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
+ if value:
+ final_attrs['value'] = value
+ return mark_safe(u'<input%s />' % flatatt(final_attrs))
+
+
+class InetAddressFormField(forms.Field):
+ widget = NetInput
+ default_error_messages = {
+ 'invalid': u'Enter a valid IP address.',
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(InetAddressFormField, self).__init__(*args, **kwargs)
+
+ def to_python(self, value):
+ if not value:
+ return None
+
+ if isinstance(value, _IPAddressBase):
+ return value
+
+ try:
+ return ip_interface(value)
+ except ValueError as e:
+ raise ValidationError(self.default_error_messages['invalid'])
+
+
+class CidrAddressFormField(forms.Field):
+ widget = NetInput
+ default_error_messages = {
+ 'invalid': u'Enter a valid CIDR address.',
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(CidrAddressFormField, self).__init__(*args, **kwargs)
+
+ def to_python(self, value):
+ if not value:
+ return None
+
+ if isinstance(value, _BaseNetwork):
+ network = value
+
+ try:
+ network = ip_network(value)
+ except ValueError as e:
+ raise ValidationError(self.default_error_messages['invalid'])
+
+ return network
+
+
+class MACAddressFormField(forms.Field):
+ default_error_messages = {
+ 'invalid': u'Enter a valid MAC address.',
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(MACAddressFormField, self).__init__(*args, **kwargs)
+
+ def to_python(self, value):
+ if not value:
+ return None
+
+ if isinstance(value, EUI):
+ return value
+
+ try:
+ return EUI(value, dialect=mac_unix_common)
+ except (AddrFormatError, TypeError):
+ raise ValidationError(self.error_messages['invalid'])
+
+ def widget_attrs(self, widget):
+ attrs = super(MACAddressFormField, self).widget_attrs(widget)
+ attrs.update({'maxlength': '17'})
+ return attrs
diff --git a/netfields/lookups.py b/netfields/lookups.py
new file mode 100644
index 0000000..ea33afd
--- /dev/null
+++ b/netfields/lookups.py
@@ -0,0 +1,165 @@
+from django.core.exceptions import FieldError
+from django.db.models import Lookup, Transform, IntegerField
+from django.db.models.lookups import EndsWith, IEndsWith, StartsWith, IStartsWith, Regex, IRegex
+import ipaddress
+from netfields.fields import InetAddressField, CidrAddressField
+
+
+class InvalidLookup(Lookup):
+ """
+ Emulate Django 1.9 error for unsupported lookups
+ """
+ def as_sql(self, qn, connection):
+ raise FieldError("Unsupported lookup '%s'" % self.lookup_name)
+
+
+class InvalidSearchLookup(Lookup):
+ """
+ Emulate Django 1.9 error for unsupported search lookup
+ """
+ lookup_name = 'search'
+
+ def as_sql(self, qn, connection):
+ raise NotImplementedError("Full-text search is not implemented for this database backend")
+
+
+class NetFieldDecoratorMixin(object):
+ def process_lhs(self, qn, connection, lhs=None):
+ lhs = lhs or self.lhs
+ lhs_string, lhs_params = qn.compile(lhs)
+ if isinstance(lhs.source if hasattr(lhs, 'source') else lhs.output_field, InetAddressField):
+ lhs_string = 'HOST(%s)' % lhs_string
+ elif isinstance(lhs.source if hasattr(lhs, 'source') else lhs.output_field, CidrAddressField):
+ lhs_string = 'TEXT(%s)' % lhs_string
+ return lhs_string, lhs_params
+
+
+class EndsWith(NetFieldDecoratorMixin, EndsWith):
+ pass
+
+
+class IEndsWith(NetFieldDecoratorMixin, IEndsWith):
+ pass
+
+
+class StartsWith(NetFieldDecoratorMixin, StartsWith):
+ pass
+
+
+class IStartsWith(NetFieldDecoratorMixin, IStartsWith):
+ pass
+
+
+class Regex(NetFieldDecoratorMixin, Regex):
+ pass
+
+
+class IRegex(NetFieldDecoratorMixin, IRegex):
+ pass
+
+
+class NetworkLookup(object):
+ def get_prep_lookup(self):
+ if isinstance(self.rhs, ipaddress._BaseNetwork):
+ return str(self.rhs)
+ return str(ipaddress.ip_network(self.rhs))
+
+
+class AddressLookup(object):
+ def get_prep_lookup(self):
+ if isinstance(self.rhs, ipaddress._BaseAddress):
+ return str(self.rhs)
+ return str(ipaddress.ip_interface(self.rhs))
+
+
+class NetContains(AddressLookup, Lookup):
+ lookup_name = 'net_contains'
+
+ def as_sql(self, qn, connection):
+ lhs, lhs_params = self.process_lhs(qn, connection)
+ rhs, rhs_params = self.process_rhs(qn, connection)
+ params = lhs_params + rhs_params
+ return '%s >> %s' % (lhs, rhs), params
+
+
+class NetContained(NetworkLookup, Lookup):
+ lookup_name = 'net_contained'
+
+ def as_sql(self, qn, connection):
+ lhs, lhs_params = self.process_lhs(qn, connection)
+ rhs, rhs_params = self.process_rhs(qn, connection)
+ params = lhs_params + rhs_params
+ return '%s << %s' % (lhs, rhs), params
+
+
+class NetContainsOrEquals(AddressLookup, Lookup):
+ lookup_name = 'net_contains_or_equals'
+
+ def as_sql(self, qn, connection):
+ lhs, lhs_params = self.process_lhs(qn, connection)
+ rhs, rhs_params = self.process_rhs(qn, connection)
+ params = lhs_params + rhs_params
+ return '%s >>= %s' % (lhs, rhs), params
+
+
+class NetContainedOrEqual(NetworkLookup, Lookup):
+ lookup_name = 'net_contained_or_equal'
+
+ def as_sql(self, qn, connection):
+ lhs, lhs_params = self.process_lhs(qn, connection)
+ rhs, rhs_params = self.process_rhs(qn, connection)
+ params = lhs_params + rhs_params
+ return '%s <<= %s' % (lhs, rhs), params
+
+
+class NetOverlaps(NetworkLookup, Lookup):
+ lookup_name = 'net_overlaps'
+
+ def as_sql(self, qn, connection):
+ lhs, lhs_params = self.process_lhs(qn, connection)
+ rhs, rhs_params = self.process_rhs(qn, connection)
+ params = lhs_params + rhs_params
+ return '%s && %s' % (lhs, rhs), params
+
+
+class Family(Transform):
+ lookup_name = 'family'
+
+ def as_sql(self, compiler, connection):
+ lhs, params = compiler.compile(self.lhs)
+ return "family(%s)" % lhs, params
+
+ @property
+ def output_field(self):
+ return IntegerField()
+
+
+class _PrefixlenMixin(object):
+ def process_lhs(self, qn, connection, lhs=None):
+ lhs = lhs or self.lhs
+ lhs_string, lhs_params = qn.compile(lhs)
+ lhs_string = 'MASKLEN(%s)' % lhs_string
+ return lhs_string, lhs_params
+
+ def get_prep_lookup(self):
+ return str(int(self.rhs))
+
+
+class MaxPrefixlen(_PrefixlenMixin, Lookup):
+ lookup_name = 'max_prefixlen'
+
+ def as_sql(self, qn, connection):
+ lhs, lhs_params = self.process_lhs(qn, connection)
+ rhs, rhs_params = self.process_rhs(qn, connection)
+ params = lhs_params + rhs_params
+ return '%s <= %s' % (lhs, rhs), params
+
+
+class MinPrefixlen(_PrefixlenMixin, Lookup):
+ lookup_name = 'min_prefixlen'
+
+ def as_sql(self, qn, connection):
+ lhs, lhs_params = self.process_lhs(qn, connection)
+ rhs, rhs_params = self.process_rhs(qn, connection)
+ params = lhs_params + rhs_params
+ return '%s >= %s' % (lhs, rhs), params
diff --git a/netfields/mac.py b/netfields/mac.py
new file mode 100644
index 0000000..0eb0dee
--- /dev/null
+++ b/netfields/mac.py
@@ -0,0 +1,7 @@
+import netaddr
+
+
+class mac_unix_common(netaddr.mac_eui48):
+ """Common form of UNIX MAC address dialect class"""
+ word_sep = ':'
+ word_fmt = '%.2x'
diff --git a/netfields/managers.py b/netfields/managers.py
new file mode 100644
index 0000000..6be708d
--- /dev/null
+++ b/netfields/managers.py
@@ -0,0 +1,83 @@
+import datetime
+from ipaddress import _BaseNetwork
+
+from django import VERSION
+from django.db import models
+from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper
+from django.db.models import sql, query
+from django.db.models.fields import DateTimeField
+
+NET_OPERATORS = DatabaseWrapper.operators.copy()
+
+for operator in ['contains', 'startswith', 'endswith']:
+ NET_OPERATORS[operator] = 'ILIKE %s'
+ NET_OPERATORS['i%s' % operator] = 'ILIKE %s'
+
+NET_OPERATORS['iexact'] = NET_OPERATORS['exact']
+NET_OPERATORS['regex'] = NET_OPERATORS['iregex']
+NET_OPERATORS['net_contained'] = '<< %s'
+NET_OPERATORS['net_contained_or_equal'] = '<<= %s'
+NET_OPERATORS['net_contains'] = '>> %s'
+NET_OPERATORS['net_contains_or_equals'] = '>>= %s'
+NET_OPERATORS['net_overlaps'] = '&& %s'
+NET_OPERATORS['max_prefixlen'] = '%s'
+NET_OPERATORS['min_prefixlen'] = '%s'
+
+NET_TEXT_OPERATORS = ['ILIKE %s', '~* %s']
+
+
+class NetQuery(sql.Query):
+ query_terms = sql.Query.query_terms.copy()
+ query_terms.update(NET_OPERATORS)
+
+
+if VERSION < (1, 9):
+ # _prepare_data has been removed / deprecated in
+ # https://github.com/django/django/commit/5008a4db440c8f7d108a6979b959025ffb5789ba
+ class NetWhere(sql.where.WhereNode):
+ def _prepare_data(self, data):
+ """
+ Special form of WhereNode._prepare_data() that does not automatically consume the
+ __iter__ method of _BaseNetwork objects. This is used in Django >= 1.6
+ """
+ if not isinstance(data, (list, tuple)):
+ return data
+ obj, lookup_type, value = data
+ if not isinstance(value, _BaseNetwork) and hasattr(value, '__iter__') and hasattr(value, 'next'):
+ # Consume any generators immediately, so that we can determine
+ # emptiness and transform any non-empty values correctly.
+ value = list(value)
+
+ # The "value_annotation" parameter is used to pass auxilliary information
+ # about the value(s) to the query construction. Specifically, datetime
+ # and empty values need special handling. Other types could be used
+ # here in the future (using Python types is suggested for consistency).
+ if (isinstance(value, datetime.datetime)
+ or (isinstance(obj.field, DateTimeField) and lookup_type != 'isnull')):
+ value_annotation = datetime.datetime
+ elif hasattr(value, 'value_annotation'):
+ value_annotation = value.value_annotation
+ else:
+ value_annotation = bool(value)
+
+ if hasattr(obj, "prepare"):
+ value = obj.prepare(lookup_type, value)
+ return (obj, lookup_type, value_annotation, value)
+else:
+ NetWhere = sql.where.WhereNode
+
+
+class NetManager(models.Manager):
+ use_for_related_fields = True
+
+ def get_queryset(self):
+ q = NetQuery(self.model, NetWhere)
+ return query.QuerySet(self.model, q)
+
+ def filter(self, *args, **kwargs):
+ for key, val in kwargs.items():
+ if isinstance(val, _BaseNetwork):
+ # Django will attempt to consume the _BaseNetwork iterator, which
+ # will convert it to a list of every address in the network
+ kwargs[key] = str(val)
+ return super(NetManager, self).filter(*args, **kwargs)
diff --git a/netfields/models.py b/netfields/models.py
new file mode 100644
index 0000000..e69de29
diff --git a/netfields/psycopg2_types.py b/netfields/psycopg2_types.py
new file mode 100644
index 0000000..e5bdf90
--- /dev/null
+++ b/netfields/psycopg2_types.py
@@ -0,0 +1,31 @@
+import psycopg2.extensions
+from psycopg2.extras import Inet
+
+
+class Macaddr(Inet):
+ """
+ Wrap a string for the MACADDR type, like Inet
+ """
+ def getquoted(self):
+ obj = psycopg2.extensions.adapt(self.addr)
+ if hasattr(obj, 'prepare'):
+ obj.prepare(self._conn)
+ return obj.getquoted() + psycopg2.extensions.b("::macaddr")
+
+
+# Register array types for CIDR and MACADDR (Django already registers INET)
+CIDRARRAY_OID = 651
+CIDRARRAY = psycopg2.extensions.new_array_type(
+ (CIDRARRAY_OID,),
+ 'CIDRARRAY',
+ psycopg2.extensions.UNICODE,
+)
+psycopg2.extensions.register_type(CIDRARRAY)
+
+MACADDRARRAY_OID = 1040
+MACADDRARRAY = psycopg2.extensions.new_array_type(
+ (MACADDRARRAY_OID,),
+ 'MACADDRARRAY',
+ psycopg2.extensions.UNICODE,
+)
+psycopg2.extensions.register_type(MACADDRARRAY)
diff --git a/netfields/rest_framework.py b/netfields/rest_framework.py
new file mode 100644
index 0000000..6223c6b
--- /dev/null
+++ b/netfields/rest_framework.py
@@ -0,0 +1,35 @@
+from __future__ import absolute_import
+
+from django.core.exceptions import ValidationError as DjangoValidationError
... 1191 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-netfields.git
More information about the Python-modules-commits
mailing list