[Python-modules-commits] [python-django-netfields] 02/06: Import python-django-netfields_0.7.2.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Mon Jun 19 13:29:59 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 6071f678c0745c7c14818eef8d7a43ee13949d8e
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Mon Jun 19 15:04:20 2017 +0200

    Import python-django-netfields_0.7.2.orig.tar.gz
---
 CHANGELOG                                      |  14 +++
 PKG-INFO                                       | 150 +++++++++++++++++++++++++
 django_netfields.egg-info/PKG-INFO             | 150 +++++++++++++++++++++++++
 django_netfields.egg-info/SOURCES.txt          |  31 +++++
 django_netfields.egg-info/dependency_links.txt |   1 +
 django_netfields.egg-info/not-zip-safe         |   1 +
 django_netfields.egg-info/requires.txt         |   3 +
 django_netfields.egg-info/top_level.txt        |   2 +
 netfields/apps.py                              |   2 +
 netfields/fields.py                            |  25 ++++-
 netfields/forms.py                             |  19 +++-
 netfields/lookups.py                           |  26 +++--
 netfields/managers.py                          |   8 +-
 netfields/psycopg2_types.py                    |   2 +-
 netfields/rest_framework.py                    |   4 +-
 setup.cfg                                      |   5 +
 setup.py                                       |   2 +-
 test/models.py                                 |  12 +-
 test/tests/test_form_fields.py                 |  43 ++++++-
 test/tests/test_rest_framework_fields.py       |  27 +++--
 test/tests/test_sql_fields.py                  |  42 ++++++-
 tox.ini                                        | 114 -------------------
 22 files changed, 534 insertions(+), 149 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 534c6c3..ae4d6a4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,17 @@
+- 0.7.2
+  * Fix issue where prefetch_related returns empty querysets (Jay McEntire)
+  * Improve validation error messages to include the underlying error (Diego
+    Gaustein)
+  * Strip extraneous whitespace around form inputs
+
+- 0.7.1
+  * Fix issue with recent psycopg2 releases (Jonas Genannt)
+  * Add a lookup for exact CIDR prefix length (Joey Wilhelm)
+  * Use proper unicode type when casting ipaddress/ipnetwork objects for
+    queries
+  * Support array aggregation with netfields (orf)
+  * compatibility with template based widgets (Leo Honkanen)
+
 - 0.7
   * Added support for Django 1.10
   * Added __overlaps lookup (Emre Hasegeli)
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..7c2d33c
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,150 @@
+Metadata-Version: 1.1
+Name: django-netfields
+Version: 0.7.2
+Summary: Django PostgreSQL netfields implementation
+Home-page: https://github.com/jimfunk/django-postgresql-netfields
+Author: James Oakley
+Author-email: jfunk at funktronics.ca
+License: BSD
+Description: 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 :-)
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: Framework :: Django
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Utilities
diff --git a/django_netfields.egg-info/PKG-INFO b/django_netfields.egg-info/PKG-INFO
new file mode 100644
index 0000000..7c2d33c
--- /dev/null
+++ b/django_netfields.egg-info/PKG-INFO
@@ -0,0 +1,150 @@
+Metadata-Version: 1.1
+Name: django-netfields
+Version: 0.7.2
+Summary: Django PostgreSQL netfields implementation
+Home-page: https://github.com/jimfunk/django-postgresql-netfields
+Author: James Oakley
+Author-email: jfunk at funktronics.ca
+License: BSD
+Description: 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 :-)
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: Framework :: Django
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Utilities
diff --git a/django_netfields.egg-info/SOURCES.txt b/django_netfields.egg-info/SOURCES.txt
new file mode 100644
index 0000000..10338b8
--- /dev/null
+++ b/django_netfields.egg-info/SOURCES.txt
@@ -0,0 +1,31 @@
+AUTHORS
+CHANGELOG
+LICENSE
+MANIFEST.in
+README.rst
+manage.py
+requirements.txt
+setup.py
+testsettings.py
+django_netfields.egg-info/PKG-INFO
+django_netfields.egg-info/SOURCES.txt
+django_netfields.egg-info/dependency_links.txt
+django_netfields.egg-info/not-zip-safe
+django_netfields.egg-info/requires.txt
+django_netfields.egg-info/top_level.txt
+netfields/__init__.py
+netfields/apps.py
+netfields/fields.py
+netfields/forms.py
+netfields/lookups.py
+netfields/mac.py
+netfields/managers.py
+netfields/models.py
+netfields/psycopg2_types.py
+netfields/rest_framework.py
+test/__init__.py
+test/models.py
+test/tests/__init__.py
+test/tests/test_form_fields.py
+test/tests/test_rest_framework_fields.py
+test/tests/test_sql_fields.py
\ No newline at end of file
diff --git a/django_netfields.egg-info/dependency_links.txt b/django_netfields.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/django_netfields.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/django_netfields.egg-info/not-zip-safe b/django_netfields.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/django_netfields.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/django_netfields.egg-info/requires.txt b/django_netfields.egg-info/requires.txt
new file mode 100644
index 0000000..0585d81
--- /dev/null
+++ b/django_netfields.egg-info/requires.txt
@@ -0,0 +1,3 @@
+netaddr
+django>=1.8
+ipaddress
diff --git a/django_netfields.egg-info/top_level.txt b/django_netfields.egg-info/top_level.txt
new file mode 100644
index 0000000..1a72369
--- /dev/null
+++ b/django_netfields.egg-info/top_level.txt
@@ -0,0 +1,2 @@
+netfields
+test
diff --git a/netfields/apps.py b/netfields/apps.py
index 286abe5..ab2729c 100644
--- a/netfields/apps.py
+++ b/netfields/apps.py
@@ -18,6 +18,7 @@ from netfields.lookups import (
     NetContains,
     NetContainsOrEquals,
     NetOverlaps,
+    Prefixlen,
     Regex,
     StartsWith,
 )
@@ -51,6 +52,7 @@ class NetfieldsConfig(AppConfig):
     CidrAddressField.register_lookup(Family)
     CidrAddressField.register_lookup(MaxPrefixlen)
     CidrAddressField.register_lookup(MinPrefixlen)
+    CidrAddressField.register_lookup(Prefixlen)
 
     InetAddressField.register_lookup(EndsWith)
     InetAddressField.register_lookup(IEndsWith)
diff --git a/netfields/fields.py b/netfields/fields.py
index 4312503..2f908c9 100644
--- a/netfields/fields.py
+++ b/netfields/fields.py
@@ -19,6 +19,9 @@ class _NetAddressField(models.Field):
         super(_NetAddressField, self).__init__(*args, **kwargs)
 
     def from_db_value(self, value, expression, connection, context):
+        if isinstance(value, list):
+            # Aggregation detected, return a list of values
+            return [self.to_python(v) for v in value if v is not None]
         return self.to_python(value)
 
     def to_python(self, value):
@@ -53,8 +56,15 @@ class _NetAddressField(models.Field):
         return str(self.to_python(value))
 
     def get_db_prep_value(self, value, connection, prepared=False):
-        if not value:
-            return None
+        # Django <= 1.8, ArrayField does not pass model to the base_field so we have to check for existance
+        model = getattr(self, 'model', None)
+        if model is None or model._meta.get_field(self.name).get_internal_type() == 'ArrayField':
+            is_array_field = True
+        else:
+            is_array_field = False
+
+        if prepared is False and is_array_field is False:
+            return self.get_prep_value(value)
 
         return Inet(self.get_prep_value(value))
 
@@ -150,8 +160,15 @@ class MACAddressField(models.Field):
         return text_type(self.to_python(value))
 
     def get_db_prep_value(self, value, connection, prepared=False):
-        if not value:
-            return None
+        # Django <= 1.8, ArrayField does not pass model to the base_field so we have to check for existance
+        model = getattr(self, 'model', None)
+        if model is None or model._meta.get_field(self.name).get_internal_type() == 'ArrayField':
+            is_array_field = True
+        else:
+            is_array_field = False
+
+        if prepared is False and is_array_field is False:
+            return self.get_prep_value(value)
 
         return Macaddr(self.get_prep_value(value))
 
diff --git a/netfields/forms.py b/netfields/forms.py
index d19eeb9..b2510c3 100644
--- a/netfields/forms.py
+++ b/netfields/forms.py
@@ -5,6 +5,7 @@ from django import forms
 import django
 from django.forms.utils import flatatt
 from django.utils.safestring import mark_safe
+from django.utils.six import text_type
 from django.core.exceptions import ValidationError
 
 from netfields.mac import mac_unix_common
@@ -17,7 +18,14 @@ class NetInput(forms.Widget):
         # 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 attrs is None:
+            attrs = {}
+        base_attrs = {
+            "type": self.input_type,
+            "name": name
+        }
+        base_attrs.update(attrs)
+        final_attrs = self.build_attrs(base_attrs)
         if value:
             final_attrs['value'] = value
         return mark_safe(u'<input%s />' % flatatt(final_attrs))
@@ -39,6 +47,9 @@ class InetAddressFormField(forms.Field):
         if isinstance(value, _IPAddressBase):
             return value
 
+        if isinstance(value, text_type):
+            value = value.strip()
+
         try:
             return ip_interface(value)
         except ValueError as e:
@@ -61,6 +72,9 @@ class CidrAddressFormField(forms.Field):
         if isinstance(value, _BaseNetwork):
             network = value
 
+        if isinstance(value, text_type):
+            value = value.strip()
+
         try:
             network = ip_network(value)
         except ValueError as e:
@@ -84,6 +98,9 @@ class MACAddressFormField(forms.Field):
         if isinstance(value, EUI):
             return value
 
+        if isinstance(value, text_type):
+            value = value.strip()
+
         try:
             return EUI(value, dialect=mac_unix_common)
         except (AddrFormatError, TypeError):
diff --git a/netfields/lookups.py b/netfields/lookups.py
index ea33afd..ebbcd62 100644
--- a/netfields/lookups.py
+++ b/netfields/lookups.py
@@ -135,6 +135,15 @@ class Family(Transform):
 
 
 class _PrefixlenMixin(object):
+    format_string = None
+
+    def as_sql(self, qn, connection):
+        assert self.format_string is not None, "Prefixlen lookups must specify a format_string"
+        lhs, lhs_params = self.process_lhs(qn, connection)
+        rhs, rhs_params = self.process_rhs(qn, connection)
+        params = lhs_params + rhs_params
+        return self.format_string % (lhs, rhs), params
+
     def process_lhs(self, qn, connection, lhs=None):
         lhs = lhs or self.lhs
         lhs_string, lhs_params = qn.compile(lhs)
@@ -147,19 +156,14 @@ class _PrefixlenMixin(object):
 
 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
+    format_string = '%s <= %s'
 
 
 class MinPrefixlen(_PrefixlenMixin, Lookup):
     lookup_name = 'min_prefixlen'
+    format_string = '%s >= %s'
 
-    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 Prefixlen(_PrefixlenMixin, Lookup):
+    lookup_name = 'prefixlen'
+    format_string = '%s = %s'
diff --git a/netfields/managers.py b/netfields/managers.py
index 6be708d..657e9e2 100644
--- a/netfields/managers.py
+++ b/netfields/managers.py
@@ -7,6 +7,12 @@ from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper
 from django.db.models import sql, query
 from django.db.models.fields import DateTimeField
 
+try:
+    str_type = unicode
+except NameError:
+    str_type = str
+
+
 NET_OPERATORS = DatabaseWrapper.operators.copy()
 
 for operator in ['contains', 'startswith', 'endswith']:
@@ -79,5 +85,5 @@ class NetManager(models.Manager):
             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)
+                kwargs[key] = str_type(val)
         return super(NetManager, self).filter(*args, **kwargs)
diff --git a/netfields/psycopg2_types.py b/netfields/psycopg2_types.py
index e5bdf90..b4be11c 100644
--- a/netfields/psycopg2_types.py
+++ b/netfields/psycopg2_types.py
@@ -10,7 +10,7 @@ class Macaddr(Inet):
         obj = psycopg2.extensions.adapt(self.addr)
         if hasattr(obj, 'prepare'):
             obj.prepare(self._conn)
-        return obj.getquoted() + psycopg2.extensions.b("::macaddr")
+        return obj.getquoted() + b"::macaddr"
  
 
 # Register array types for CIDR and MACADDR (Django already registers INET) 
diff --git a/netfields/rest_framework.py b/netfields/rest_framework.py
index 6223c6b..d5aa0c3 100644
--- a/netfields/rest_framework.py
+++ b/netfields/rest_framework.py
@@ -16,8 +16,8 @@ class NetfieldsField(serializers.CharField):
         """
         try:
             self.netfields_type(value).to_python(value)
-        except DjangoValidationError:
-            raise serializers.ValidationError("Invalid {} address.".format(self.address_type))
+        except DjangoValidationError as e:
+            raise serializers.ValidationError("Invalid {} address: {}".format(self.address_type, e.message))
 
 
 class InetAddressField(NetfieldsField):
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
index 8912b7a..70bc3b0 100755
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@ if sys.version_info.major == 2:
 
 setup(
     name='django-netfields',
-    version='0.7',
+    version='0.7.2',
     license='BSD',
     description='Django PostgreSQL netfields implementation',
     long_description=get_long_description(),
diff --git a/test/models.py b/test/models.py
index e82d10b..4381e1f 100644
--- a/test/models.py
+++ b/test/models.py
@@ -1,5 +1,5 @@
 from django.contrib.postgres.fields import ArrayField
-from django.db.models import Model
+from django.db.models import Model, ForeignKey
 
 from netfields import InetAddressField, CidrAddressField, MACAddressField, \
         NetManager
@@ -88,3 +88,13 @@ class MACArrayTestModel(Model):
 
     class Meta:
         db_table = 'macarray'
+
+
+class AggregateTestModel(Model):
+    pass
+
+
+class AggregateTestChildModel(Model):
+    parent = ForeignKey('AggregateTestModel', related_name='children')
+    network = CidrAddressField()
+    inet = InetAddressField()
diff --git a/test/tests/test_form_fields.py b/test/tests/test_form_fields.py
index ede8dbe..209de14 100644
--- a/test/tests/test_form_fields.py
+++ b/test/tests/test_form_fields.py
@@ -34,6 +34,11 @@ class TestInetAddressFormField(TestCase):
         form = self.form_class({'field': '10.0.0.1.2'})
         self.assertFalse(form.is_valid())
 
+    def test_form_ipv4_strip(self):
+        form = self.form_class({'field': ' 10.0.0.1 '})
+        self.assertTrue(form.is_valid())
+        self.assertEqual(form.cleaned_data['field'], ip_interface('10.0.0.1'))
+
     def test_form_ipv4_change(self):
         instance = InetTestModel.objects.create(field='10.1.2.3/24')
         form = self.form_class({'field': '10.1.2.4/24'}, instance=instance)
@@ -42,7 +47,7 @@ class TestInetAddressFormField(TestCase):
         instance = InetTestModel.objects.get(pk=instance.pk)
         self.assertEqual(instance.field, ip_interface('10.1.2.4/24'))
 
-    def test_form_ipv6(self):
+    def test_form_ipv6_valid(self):
         form = self.form_class({'field': '2001:0:1::2'})
         self.assertTrue(form.is_valid())
         self.assertEqual(form.cleaned_data['field'], ip_interface('2001:0:1::2'))
@@ -51,6 +56,11 @@ class TestInetAddressFormField(TestCase):
         form = self.form_class({'field': '2001:0::1::2'})
         self.assertFalse(form.is_valid())
 
+    def test_form_ipv6_strip(self):
+        form = self.form_class({'field': ' 2001:0:1::2 '})
+        self.assertTrue(form.is_valid())
+        self.assertEqual(form.cleaned_data['field'], ip_interface('2001:0:1::2'))
+
     def test_form_ipv6_change(self):
         instance = InetTestModel.objects.create(field='2001:0:1::2/64')
         form = self.form_class({'field': '2001:0:1::3/64'}, instance=instance)
@@ -79,6 +89,12 @@ class TestNoPrefixInetAddressFormField(TestCase):
         form = self.form_class({'field': '10.0.0.1.2'})
         self.assertFalse(form.is_valid())
 
+    def test_form_ipv4_strip(self):
+        form = self.form_class({'field': ' 10.0.0.1 '})
+        self.assertTrue(form.is_valid())
+        # Form always passes ip_interface. Model field will return the requested type
+        self.assertEqual(form.cleaned_data['field'], ip_interface('10.0.0.1'))
+
     def test_form_ipv4_change(self):
         instance = NoPrefixInetTestModel.objects.create(field='10.1.2.3/24')
         form = self.form_class({'field': '10.1.2.4/24'}, instance=instance)
@@ -87,7 +103,7 @@ class TestNoPrefixInetAddressFormField(TestCase):
         instance = NoPrefixInetTestModel.objects.get(pk=instance.pk)
         self.assertEqual(instance.field, ip_address('10.1.2.4'))
 
-    def test_form_ipv6(self):
+    def test_form_ipv6_valid(self):
         form = self.form_class({'field': '2001:0:1::2'})
         self.assertTrue(form.is_valid())
         # Form always passes ip_interface. Model field will return the requested type
@@ -97,6 +113,12 @@ class TestNoPrefixInetAddressFormField(TestCase):
         form = self.form_class({'field': '2001:0::1::2'})
         self.assertFalse(form.is_valid())
 
+    def test_form_ipv6_strip(self):
+        form = self.form_class({'field': ' 2001:0:1::2 '})
+        self.assertTrue(form.is_valid())
+        # Form always passes ip_interface. Model field will return the requested type
+        self.assertEqual(form.cleaned_data['field'], ip_interface('2001:0:1::2'))
+
     def test_form_ipv6_change(self):
         instance = NoPrefixInetTestModel.objects.create(field='2001:0:1::2/64')
         form = self.form_class({'field': '2001:0:1::3/64'}, instance=instance)
@@ -134,11 +156,16 @@ class TestCidrAddressFormField(TestCase):
         form = self.form_class({'field': '10.0.0.1.2/32'})
         self.assertFalse(form.is_valid())
 
+    def test_form_ipv4_strip(self):
+        form = self.form_class({'field': ' 10.0.1.0/24 '})
+        self.assertTrue(form.is_valid())
+        self.assertEqual(form.cleaned_data['field'], ip_network('10.0.1.0/24'))
+
     def test_form_ipv4_bits_to_right_of_mask(self):
         form = self.form_class({'field': '10.0.0.1.2/24'})
         self.assertFalse(form.is_valid())
 
-    def test_form_ipv6(self):
+    def test_form_ipv6_valid(self):
         form = self.form_class({'field': '2001:0:1::/64'})
         self.assertTrue(form.is_valid())
         self.assertEqual(form.cleaned_data['field'], ip_network('2001:0:1::/64'))
@@ -147,6 +174,11 @@ class TestCidrAddressFormField(TestCase):
         form = self.form_class({'field': '2001:0::1::2/128'})
         self.assertFalse(form.is_valid())
 
+    def test_form_ipv6_strip(self):
+        form = self.form_class({'field': ' 2001:0:1::/64 '})
+        self.assertTrue(form.is_valid())
+        self.assertEqual(form.cleaned_data['field'], ip_network('2001:0:1::/64'))
+
     def test_form_ipv6_bits_to_right_of_mask(self):
         form = self.form_class({'field': '2001:0::1::2/64'})
         self.assertFalse(form.is_valid())
@@ -207,6 +239,11 @@ class TestMacAddressFormField(TestCase):
         self.assertTrue(form.is_valid())
         self.assertEqual(form.cleaned_data['field'], self.mac)
 
+    def test_strip(self):
+        form = MacAddressTestModelForm({'field': ' 00:aa:2b:c3:dd:44 '})
+        self.assertTrue(form.is_valid())
+        self.assertEqual(form.cleaned_data['field'], self.mac)
+
     def test_invalid(self):
         form = MacAddressTestModelForm({'field': 'notvalid'})
         self.assertFalse(form.is_valid())
diff --git a/test/tests/test_rest_framework_fields.py b/test/tests/test_rest_framework_fields.py
index 093d37f..126065e 100644
--- a/test/tests/test_rest_framework_fields.py
+++ b/test/tests/test_rest_framework_fields.py
@@ -1,4 +1,4 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, unicode_literals
 
 from rest_framework import serializers
 
@@ -12,30 +12,36 @@ class FieldsTestCase(unittest.TestCase):
         class TestSerializer(serializers.Serializer):
             ip = fields.InetAddressField()
 
-        serializer = TestSerializer(data={'ip': '10.0.0.'})
+        address = '10.0.0.'
+        serializer = TestSerializer(data={'ip': address})
         with self.assertRaises(serializers.ValidationError) as e:
             serializer.is_valid(raise_exception=True)
-        self.assertEqual(e.exception.detail['ip'], ['Invalid IP address.'])
+        self.assertEqual(e.exception.detail['ip'],
+                         ["Invalid IP address: %r does not appear to be an IPv4 or IPv6 interface" % address])
+
 
     def test_validation_cidr_field(self):
 
         class TestSerializer(serializers.Serializer):
             cidr = fields.CidrAddressField()
 
-        serializer = TestSerializer(data={'cidr': '10.0.0.'})
+        address = '10.0.0.'
+        serializer = TestSerializer(data={'cidr': address})
         with self.assertRaises(serializers.ValidationError) as e:
             serializer.is_valid(raise_exception=True)
-        self.assertEqual(e.exception.detail['cidr'], ['Invalid CIDR address.'])
+        self.assertEqual(e.exception.detail['cidr'],
+                         ["Invalid CIDR address: %r does not appear to be an IPv4 or IPv6 network" % address])
 
     def test_validation_mac_field(self):
 
         class TestSerializer(serializers.Serializer):
             mac = fields.MACAddressField()
 
-        serializer = TestSerializer(data={'mac': 'de:'})
+        address = 'de:'
+        serializer = TestSerializer(data={'mac': address})
         with self.assertRaises(serializers.ValidationError) as e:
             serializer.is_valid(raise_exception=True)
-        self.assertEqual(e.exception.detail['mac'], ['Invalid MAC address.'])
+        self.assertEqual(e.exception.detail['mac'], ["Invalid MAC address: failed to detect EUI version: %r" % address])
 
     def test_validation_additional_validators(self):
         def validate(value):
@@ -44,7 +50,10 @@ class FieldsTestCase(unittest.TestCase):
         class TestSerializer(serializers.Serializer):
             ip = fields.InetAddressField(validators=[validate])
 
-        serializer = TestSerializer(data={'ip': 'de:'})
+        address = 'de:'
+        serializer = TestSerializer(data={'ip': address})
         with self.assertRaises(serializers.ValidationError) as e:
             serializer.is_valid(raise_exception=True)
-        self.assertItemsEqual(e.exception.detail['ip'], ['Invalid IP address.', 'Invalid.'])
+        self.assertItemsEqual(e.exception.detail['ip'],
+                              ["Invalid IP address: %r does not appear to be an IPv4 or IPv6 interface" % address,
+                               'Invalid.'])
diff --git a/test/tests/test_sql_fields.py b/test/tests/test_sql_fields.py
index a75c0ce..8b74363 100644
--- a/test/tests/test_sql_fields.py
+++ b/test/tests/test_sql_fields.py
@@ -32,7 +32,9 @@ from test.models import (
     UniqueCidrTestModel,
     NoPrefixInetTestModel,
     MACArrayTestModel,
-    MACTestModel
+    MACTestModel,
+    AggregateTestModel,
+    AggregateTestChildModel
 )
 
 
@@ -227,6 +229,9 @@ class BaseInetFieldTestCase(BaseInetTestCase):
     def test_query_filter_ipaddress(self):
         self.model.objects.filter(field=ip_interface('1.2.3.4'))
 
+    def test_query_filter_contains_ipnetwork(self):
+        self.model.objects.filter(field__net_contains=ip_network(u'2001::0/16'))
+
 
 class BaseCidrFieldTestCase(BaseInetTestCase):
     value1 = '10.0.0.1/32'
@@ -299,6 +304,12 @@ class BaseCidrFieldTestCase(BaseInetTestCase):
             self.select + 'WHERE masklen("table"."field") >= %s'
         )
 
+    def test_prefixlen(self):
+        self.assertSqlEquals(
+            self.qs.filter(field__prefixlen='16'),
+            self.select + 'WHERE masklen("table"."field") = %s'
+        )
+
 
 class TestInetField(BaseInetFieldTestCase, TestCase):
     def setUp(self):
@@ -566,3 +577,32 @@ class TestMACAddressFieldArray(TestCase):
         instance = MACArrayTestModel.objects.get(id=instance.id)
         self.assertEqual(instance.field, [EUI('00:aa:2b:c3:dd:44')])
         self.assertIsInstance(instance.field[0], EUI)
+
+
+class TestAggegate(TestCase):
+    @skipIf(VERSION < (1, 9), 'Postgres aggregates not supported in Django < 1.9')
+    def test_aggregate_inet(self):
+        from django.contrib.postgres.aggregates import ArrayAgg        
+        inet = IPv4Interface('10.20.30.20/32')
+        network = IPv4Network('10.10.10.10/32')
+        
+        parent = AggregateTestModel.objects.create()
+        inet_qs = AggregateTestModel.objects.annotate(agg_inet=ArrayAgg('children__inet'))
+        
+        self.assertEqual(inet_qs[0].agg_inet, [])
+
+        AggregateTestChildModel.objects.create(parent=parent, network=network, inet=inet)
+        self.assertEqual(inet_qs[0].agg_inet, [inet])
+        
+    @skipIf(VERSION < (1, 9), 'Postgres aggregates not supported in Django < 1.9')
+    def test_aggregate_network(self):
+        from django.contrib.postgres.aggregates import ArrayAgg  
+        inet = IPv4Interface('10.20.30.20/32')
+        network = IPv4Network('10.10.10.10/32')
+        
+        parent = AggregateTestModel.objects.create()
+        network_qs = AggregateTestModel.objects.annotate(agg_network=ArrayAgg('children__network'))
+        
+        self.assertEqual(network_qs[0].agg_network, [])
+        AggregateTestChildModel.objects.create(parent=parent, network=network, inet=inet)
+        self.assertEqual(network_qs[0].agg_network, [network])
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 2426078..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,114 +0,0 @@
-[tox]
-envlist=
-    py27-django18,
-    py33-django18,
-    py34-django18,
-    py35-django18,
-    py27-django19,
-    py34-django19,
-    py35-django19,
-    py27-django110,
-    py34-django110,
-    py35-django110,
-
-
-
-[testenv]
-commands=
-    python manage.py test {posargs}
-
-# Build configurations...
-
-[testenv:py27-django18]
-basepython=python2.7
-deps=
-    django>=1.8,<1.9
-    ipaddress
-    netaddr
-    psycopg2
-    djangorestframework
-    unittest2
-
-[testenv:py33-django18]
-basepython=python3.3
-deps=
-    django>=1.8,<1.9
... 79 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