[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