django-netfields-1.2.4/0000755000175000001440000000000014120406214015221 5ustar jfunkusers00000000000000django-netfields-1.2.4/AUTHORS0000644000175000001440000000030412140306116016266 0ustar jfunkusers00000000000000Written 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 django-netfields-1.2.4/CHANGELOG0000644000175000001440000001322714120406070016440 0ustar jfunkusers00000000000000- 1.2.4 * Change InetAddressField form value based on prefix parameter (Travis MacDonald) - 1.2.3 * Change __prefixlen to be a Transform instead of a Lookup (Christopher Aubut) * Add package name to README installation section (Yuval Adam) * Fix formatting error in README (Gil Forcada Codinachs) * Add NetModelSerializer to rest_framework (Devid) * Handle more invalid macs in DRF MACAddressField (Étienne Noss) * Avoid deprecation warning on Django 2.x - 1.2.2 * Additional Django 3 compatibility (Étienne Noss) - 1.2.1 * django.utils.six is deprecated in Django 3.0 (jaychoo) - 1.2.0 * Django 3.0 support (Erik de Wildt) * Expose INET related PostgreSQL functions. See README.rst for usage (Christopher Aubut) - 1.1.1 * Fix improper conversion of networks to addresses in net lookups. Bug introduced in 1.1.0 * Added example of using a network field as an index (Christopher Aubut) - 1.1.0 * Add rhs lookup expression support (Christopher Aubut) * Update REST Framework InetAddressField to avoid using the wrong type when store_prefix is True (Nick Huber) - 1.0.1 * Added a specific error to form and REST framework fields for host addresses passed to network fields - 1.0.0 * Rewrote the REST framework fields to conform to recommendations + The proper types are now set on unsaved models and passed to validators. Any validators or pre-save code that assumes the values are strings may need to be adjusted + Simplified the validation error messages * Fix deprecation warnings (Mike Lane) * Test against Django 2.2 (Øyvind Kolbu) - 0.10.0 * Added support for __host lookups, for matching the host part of an address regardless of prefix length - 0.9.0 * Added support for Django 2.1 * Some Django 3.0 compatibility * BREAKING CHANGE: If using ArrayAgg to return groups of network addresses, None values will be included as per the Django documentation. Previously, the None values were filtered out, but that appears to have been a mistake in the implementation. If your application depends on the filtered None values, it will need to be updated accordingly * Prefer "error_messages" rather than "default_error_messages" in form fields (Shintaro Kojima) - 0.8 * Added support for Django 2.0 - 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) * 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 django-netfields-1.2.4/LICENSE0000644000175000001440000000277412140251021016232 0ustar jfunkusers00000000000000Copyright (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. django-netfields-1.2.4/MANIFEST.in0000644000175000001440000000021012140311316016746 0ustar jfunkusers00000000000000include AUTHORS include CHANGELOG include LICENSE include README.rst include requirements.txt include manage.py include testsettings.py django-netfields-1.2.4/PKG-INFO0000644000175000001440000003371614120406214016330 0ustar jfunkusers00000000000000Metadata-Version: 1.1 Name: django-netfields Version: 1.2.4 Summary: Django PostgreSQL netfields implementation Home-page: https://github.com/jimfunk/django-postgresql-netfields Author: James Oakley Author-email: jfunk@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. Installation ------------ .. code-block:: bash $ pip install django-netfields 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. .. code-block:: python 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. .. code-block:: python 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``). .. code-block:: python 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 ``__host`` matches the host part of an address regardless of prefix length ``__prefixlen`` matches the prefix length part of an address These correspond with the operators and functions from http://www.postgresql.org/docs/9.4/interactive/functions-net.html ``CidrAddressField`` includes two extra lookups (these will be depreciated in the future by ``__prefixlen``): ``__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 Database Functions '''''''''''''''''' `Postgres network address functions `_ are exposed via the ``netfields.functions`` module. They can be used to extract additional information from these fields or to construct complex queries. .. code-block:: python from django.db.models import F from netfields import CidrAddressField, NetManager from netfields.functions import Family, Masklen class Example(models.Model): inet = CidrAddressField() # ... ipv4_with_num_ips = ( Example.objects.annotate( family=Family(F('inet')), num_ips=2 ** (32 - Masklen(F('inet'))) # requires Django >2.0 to resolve ) .filter(family=4) ) **CidrAddressField and InetAddressField Functions** +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | Postgres Function | Django Function | Return Type | Description | +================================+==================+======================+================================================================+ | abbrev(``T``) | Abbrev | ``TextField`` | abbreviated display format as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | broadcast(``T``) | Broadcast | ``InetAddressField`` | broadcast address for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | family(``T``) | Family | ``IntegerField`` | extract family of address; 4 for IPv4, 6 for IPv6 | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | host(``T``) | Host | ``TextField`` | extract IP address as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | hostmask(``T``) | Hostmask | ``InetAddressField`` | construct host mask for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | masklen(``T``) | Masklen | ``IntegerField`` | extract netmask length | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | netmask(``T``) | Netmask | ``InetAddressField`` | construct netmask for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | network(``T``) | Network | ``CidrAddressField`` | extract network part of address | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | set_masklen(``T``, int) | SetMasklen | ``T`` | set netmask length for inet value | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | text(``T``) | AsText | ``TextField`` | extract IP address and netmask length as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | inet_same_family(``T``, ``T``) | IsSameFamily | ``BooleanField`` | are the addresses from the same family? | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | inet_merge(``T``, ``T``) | Merge | ``CidrAddressField`` | the smallest network which includes both of the given networks | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ **MACAddressField Functions** +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | Postgres Function | Django Function | Return Type | Description | +================================+==================+======================+================================================================+ | trunc(``T``) | Trunc | ``T`` | set last 3 bytes to zero | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ Indexes ''''''' As of Django 2.2, indexes can be created for ``InetAddressField`` and ``CidrAddressField`` extra lookups directly on the model. .. code-block:: python from django.contrib.postgres.indexes import GistIndex from netfields import CidrAddressField, NetManager class Example(models.Model): inet = CidrAddressField() # ... class Meta: indexes = ( GistIndex( fields=('inet',), opclasses=('inet_ops',), name='app_example_inet_idx' ), ) For earlier versions of Django, a custom migration can be used to install an index. .. code-block:: python from django.db import migrations class Migration(migrations.Migration): # ... operations = [ # ... migrations.RunSQL( "CREATE INDEX app_example_inet_idx ON app_example USING GIST (inet inet_ops);" ), # ... ] Errata ------ * In Django < 1.9.6 types returned in ArrayFields are strings and not ipaddress types. See https://code.djangoproject.com/ticket/25143 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 originally 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 django-netfields-1.2.4/README.rst0000644000175000001440000002653113750762473016743 0ustar jfunkusers00000000000000Django 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. Installation ------------ .. code-block:: bash $ pip install django-netfields 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. .. code-block:: python 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. .. code-block:: python 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``). .. code-block:: python 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 ``__host`` matches the host part of an address regardless of prefix length ``__prefixlen`` matches the prefix length part of an address These correspond with the operators and functions from http://www.postgresql.org/docs/9.4/interactive/functions-net.html ``CidrAddressField`` includes two extra lookups (these will be depreciated in the future by ``__prefixlen``): ``__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 Database Functions '''''''''''''''''' `Postgres network address functions `_ are exposed via the ``netfields.functions`` module. They can be used to extract additional information from these fields or to construct complex queries. .. code-block:: python from django.db.models import F from netfields import CidrAddressField, NetManager from netfields.functions import Family, Masklen class Example(models.Model): inet = CidrAddressField() # ... ipv4_with_num_ips = ( Example.objects.annotate( family=Family(F('inet')), num_ips=2 ** (32 - Masklen(F('inet'))) # requires Django >2.0 to resolve ) .filter(family=4) ) **CidrAddressField and InetAddressField Functions** +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | Postgres Function | Django Function | Return Type | Description | +================================+==================+======================+================================================================+ | abbrev(``T``) | Abbrev | ``TextField`` | abbreviated display format as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | broadcast(``T``) | Broadcast | ``InetAddressField`` | broadcast address for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | family(``T``) | Family | ``IntegerField`` | extract family of address; 4 for IPv4, 6 for IPv6 | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | host(``T``) | Host | ``TextField`` | extract IP address as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | hostmask(``T``) | Hostmask | ``InetAddressField`` | construct host mask for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | masklen(``T``) | Masklen | ``IntegerField`` | extract netmask length | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | netmask(``T``) | Netmask | ``InetAddressField`` | construct netmask for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | network(``T``) | Network | ``CidrAddressField`` | extract network part of address | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | set_masklen(``T``, int) | SetMasklen | ``T`` | set netmask length for inet value | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | text(``T``) | AsText | ``TextField`` | extract IP address and netmask length as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | inet_same_family(``T``, ``T``) | IsSameFamily | ``BooleanField`` | are the addresses from the same family? | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | inet_merge(``T``, ``T``) | Merge | ``CidrAddressField`` | the smallest network which includes both of the given networks | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ **MACAddressField Functions** +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | Postgres Function | Django Function | Return Type | Description | +================================+==================+======================+================================================================+ | trunc(``T``) | Trunc | ``T`` | set last 3 bytes to zero | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ Indexes ''''''' As of Django 2.2, indexes can be created for ``InetAddressField`` and ``CidrAddressField`` extra lookups directly on the model. .. code-block:: python from django.contrib.postgres.indexes import GistIndex from netfields import CidrAddressField, NetManager class Example(models.Model): inet = CidrAddressField() # ... class Meta: indexes = ( GistIndex( fields=('inet',), opclasses=('inet_ops',), name='app_example_inet_idx' ), ) For earlier versions of Django, a custom migration can be used to install an index. .. code-block:: python from django.db import migrations class Migration(migrations.Migration): # ... operations = [ # ... migrations.RunSQL( "CREATE INDEX app_example_inet_idx ON app_example USING GIST (inet inet_ops);" ), # ... ] Errata ------ * In Django < 1.9.6 types returned in ArrayFields are strings and not ipaddress types. See https://code.djangoproject.com/ticket/25143 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 originally 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 :-) django-netfields-1.2.4/django_netfields.egg-info/0000755000175000001440000000000014120406214022212 5ustar jfunkusers00000000000000django-netfields-1.2.4/django_netfields.egg-info/PKG-INFO0000644000175000001440000003371614120406214023321 0ustar jfunkusers00000000000000Metadata-Version: 1.1 Name: django-netfields Version: 1.2.4 Summary: Django PostgreSQL netfields implementation Home-page: https://github.com/jimfunk/django-postgresql-netfields Author: James Oakley Author-email: jfunk@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. Installation ------------ .. code-block:: bash $ pip install django-netfields 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. .. code-block:: python 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. .. code-block:: python 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``). .. code-block:: python 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 ``__host`` matches the host part of an address regardless of prefix length ``__prefixlen`` matches the prefix length part of an address These correspond with the operators and functions from http://www.postgresql.org/docs/9.4/interactive/functions-net.html ``CidrAddressField`` includes two extra lookups (these will be depreciated in the future by ``__prefixlen``): ``__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 Database Functions '''''''''''''''''' `Postgres network address functions `_ are exposed via the ``netfields.functions`` module. They can be used to extract additional information from these fields or to construct complex queries. .. code-block:: python from django.db.models import F from netfields import CidrAddressField, NetManager from netfields.functions import Family, Masklen class Example(models.Model): inet = CidrAddressField() # ... ipv4_with_num_ips = ( Example.objects.annotate( family=Family(F('inet')), num_ips=2 ** (32 - Masklen(F('inet'))) # requires Django >2.0 to resolve ) .filter(family=4) ) **CidrAddressField and InetAddressField Functions** +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | Postgres Function | Django Function | Return Type | Description | +================================+==================+======================+================================================================+ | abbrev(``T``) | Abbrev | ``TextField`` | abbreviated display format as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | broadcast(``T``) | Broadcast | ``InetAddressField`` | broadcast address for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | family(``T``) | Family | ``IntegerField`` | extract family of address; 4 for IPv4, 6 for IPv6 | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | host(``T``) | Host | ``TextField`` | extract IP address as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | hostmask(``T``) | Hostmask | ``InetAddressField`` | construct host mask for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | masklen(``T``) | Masklen | ``IntegerField`` | extract netmask length | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | netmask(``T``) | Netmask | ``InetAddressField`` | construct netmask for network | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | network(``T``) | Network | ``CidrAddressField`` | extract network part of address | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | set_masklen(``T``, int) | SetMasklen | ``T`` | set netmask length for inet value | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | text(``T``) | AsText | ``TextField`` | extract IP address and netmask length as text | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | inet_same_family(``T``, ``T``) | IsSameFamily | ``BooleanField`` | are the addresses from the same family? | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | inet_merge(``T``, ``T``) | Merge | ``CidrAddressField`` | the smallest network which includes both of the given networks | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ **MACAddressField Functions** +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ | Postgres Function | Django Function | Return Type | Description | +================================+==================+======================+================================================================+ | trunc(``T``) | Trunc | ``T`` | set last 3 bytes to zero | +--------------------------------+------------------+----------------------+----------------------------------------------------------------+ Indexes ''''''' As of Django 2.2, indexes can be created for ``InetAddressField`` and ``CidrAddressField`` extra lookups directly on the model. .. code-block:: python from django.contrib.postgres.indexes import GistIndex from netfields import CidrAddressField, NetManager class Example(models.Model): inet = CidrAddressField() # ... class Meta: indexes = ( GistIndex( fields=('inet',), opclasses=('inet_ops',), name='app_example_inet_idx' ), ) For earlier versions of Django, a custom migration can be used to install an index. .. code-block:: python from django.db import migrations class Migration(migrations.Migration): # ... operations = [ # ... migrations.RunSQL( "CREATE INDEX app_example_inet_idx ON app_example USING GIST (inet inet_ops);" ), # ... ] Errata ------ * In Django < 1.9.6 types returned in ArrayFields are strings and not ipaddress types. See https://code.djangoproject.com/ticket/25143 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 originally 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 django-netfields-1.2.4/django_netfields.egg-info/SOURCES.txt0000644000175000001440000000151214120406214024075 0ustar jfunkusers00000000000000AUTHORS 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/compat.py netfields/fields.py netfields/forms.py netfields/functions.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/migrations/0001_initial.py test/migrations/__init__.py test/tests/__init__.py test/tests/test_form_fields.py test/tests/test_functions.py test/tests/test_rest_framework_fields.py test/tests/test_sql_fields.pydjango-netfields-1.2.4/django_netfields.egg-info/dependency_links.txt0000644000175000001440000000000114120406214026260 0ustar jfunkusers00000000000000 django-netfields-1.2.4/django_netfields.egg-info/not-zip-safe0000644000175000001440000000000112140300150024430 0ustar jfunkusers00000000000000 django-netfields-1.2.4/django_netfields.egg-info/requires.txt0000644000175000001440000000004214120406214024606 0ustar jfunkusers00000000000000django>=1.8 ipaddress netaddr six django-netfields-1.2.4/django_netfields.egg-info/top_level.txt0000644000175000001440000000001714120406214024742 0ustar jfunkusers00000000000000netfields test django-netfields-1.2.4/manage.py0000755000175000001440000000036612140251021017025 0ustar jfunkusers00000000000000#!/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) django-netfields-1.2.4/netfields/0000755000175000001440000000000014120406214017176 5ustar jfunkusers00000000000000django-netfields-1.2.4/netfields/__init__.py0000644000175000001440000000032212755620125021317 0ustar jfunkusers00000000000000from netfields.managers import NetManager from netfields.fields import (InetAddressField, CidrAddressField, MACAddressField) default_app_config = 'netfields.apps.NetfieldsConfig' django-netfields-1.2.4/netfields/apps.py0000644000175000001440000000526213750762473020544 0ustar jfunkusers00000000000000import 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, Prefixlen, Regex, StartsWith, HostMatches, ) 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) CidrAddressField.register_lookup(Prefixlen) CidrAddressField.register_lookup(HostMatches) 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) InetAddressField.register_lookup(Prefixlen) InetAddressField.register_lookup(HostMatches) django-netfields-1.2.4/netfields/compat.py0000644000175000001440000000051014114122144021027 0ustar jfunkusers00000000000000from django import VERSION if VERSION[0] < 2: from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper else: from django.db.backends.postgresql.base import DatabaseWrapper if VERSION[0] <= 2: from django.utils.six import with_metaclass, text_type else: from six import with_metaclass, text_type django-netfields-1.2.4/netfields/fields.py0000644000175000001440000001542314120405613021025 0ustar jfunkusers00000000000000from django.core.exceptions import ValidationError from django.db import models from ipaddress import ip_interface, ip_network from netaddr import EUI from netaddr.core import AddrFormatError from netfields.compat import DatabaseWrapper, with_metaclass, text_type from netfields.forms import InetAddressFormField, NoPrefixInetAddressFormField, CidrAddressFormField, MACAddressFormField from netfields.mac import mac_unix_common from netfields.psycopg2_types import Inet, Macaddr 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 _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, *args): if isinstance(value, list): # Aggregation detected, return a list of values. This is no longer # necessary in Django 2.1 return [self.to_python(v) for v in value] 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 hasattr(value, '_prepare'): try: # Django 1.8 return value._prepare() except TypeError: # Django 1.9 return value._prepare(self) if (lookup_type in NET_OPERATORS and NET_OPERATORS[lookup_type] not in NET_TEXT_OPERATORS): if (lookup_type.startswith('net_') or lookup_type.endswith('prefixlen')) and value is not None: 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): # 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)) 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): if prepared: return [value] if (lookup_type.startswith('net_') or lookup_type.endswith('prefixlen')) and value is not None: return str(value) return [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): if self.store_prefix_length: return InetAddressFormField return NoPrefixInetAddressFormField 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, *args): 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): # 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)) def formfield(self, **kwargs): defaults = {'form_class': MACAddressFormField} defaults.update(kwargs) return super(MACAddressField, self).formfield(**defaults) django-netfields-1.2.4/netfields/forms.py0000644000175000001440000000623414120405613020705 0ustar jfunkusers00000000000000from ipaddress import ip_address, ip_interface, ip_network, _IPAddressBase, _BaseNetwork from netaddr import EUI, AddrFormatError from django import forms from django.core.exceptions import ValidationError from netfields.compat import text_type from netfields.mac import mac_unix_common class InetAddressFormField(forms.Field): widget = forms.TextInput 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 if isinstance(value, text_type): value = value.strip() try: return ip_interface(value) except ValueError: raise ValidationError(self.error_messages['invalid']) class NoPrefixInetAddressFormField(forms.Field): widget = forms.TextInput default_error_messages = { 'invalid': u'Enter a valid IP address.', } def __init__(self, *args, **kwargs): super(NoPrefixInetAddressFormField, self).__init__(*args, **kwargs) def to_python(self, value): if not value: return None if isinstance(value, _IPAddressBase): return value if isinstance(value, text_type): value = value.strip() try: return ip_address(value) except ValueError: raise ValidationError(self.error_messages['invalid']) class CidrAddressFormField(forms.Field): widget = forms.TextInput default_error_messages = { 'invalid': u'Enter a valid CIDR address.', 'network': u'Must be a network 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 if isinstance(value, text_type): value = value.strip() try: network = ip_network(value) except ValueError as e: if 'has host bits' in e.args[0]: raise ValidationError(self.error_messages['network']) raise ValidationError(self.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 if isinstance(value, text_type): value = value.strip() 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 django-netfields-1.2.4/netfields/functions.py0000644000175000001440000000417313571242063021576 0ustar jfunkusers00000000000000"""Postgres network address functions. https://www.postgresql.org/docs/11/functions-net.html """ from django.db.models import BooleanField, Func, IntegerField, TextField from .fields import CidrAddressField, InetAddressField class Abbrev(Func): """Function to abbreviate field as text.""" arity = 1 function = 'ABBREV' output_field = TextField() class Broadcast(Func): """Function to extract broadcast address for network.""" arity = 1 function = 'BROADCAST' output_field = InetAddressField() class Family(Func): """Function to extract family of address; 4 for IPv4, 6 for IPv6.""" arity = 1 function = 'FAMILY' output_field = IntegerField() class Host(Func): """Function to extract IP address as text.""" arity = 1 function = 'HOST' output_field = TextField() class Hostmask(Func): """Function to construct host mask for network.""" arity = 1 function = 'HOSTMASK' output_field = InetAddressField() class Masklen(Func): """Function to extract netmask length.""" arity = 1 function = 'MASKLEN' output_field = IntegerField() class Netmask(Func): """Function to construct netmask for network.""" arity = 1 function = 'NETMASK' output_field = InetAddressField() class Network(Func): """Function to extract network part of address.""" arity = 1 function = 'NETWORK' output_field = CidrAddressField() class SetMasklen(Func): """Function to set netmask length.""" arity = 2 function = 'SET_MASKLEN' class AsText(Func): """Function to extract IP address and netmask length as text.""" arity = 1 function = 'TEXT' output_field = TextField() class IsSameFamily(Func): """Function to test that addresses are from the same family.""" arity = 2 function = 'INET_SAME_FAMILY' output_field = BooleanField() class Merge(Func): """Function to calculate the smallest network which includes both of the given networks. """ arity = 2 function = 'INET_MERGE' output_field = CidrAddressField() class Trunc(Func): arity = 1 function = 'TRUNC' django-netfields-1.2.4/netfields/lookups.py0000644000175000001440000001335713750762473021301 0ustar jfunkusers00000000000000import warnings 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 hasattr(self.rhs, 'resolve_expression'): return self.rhs 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 hasattr(self.rhs, 'resolve_expression'): return self.rhs 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 HostMatches(AddressLookup, Lookup): lookup_name = 'host' 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 'HOST(%s) = HOST(%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): format_string = None def as_sql(self, qn, connection): warnings.warn( 'min_prefixlen and max_prefixlen will be depreciated in the future; ' 'use prefixlen__gte and prefixlen__lte respectively', DeprecationWarning ) 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) 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' format_string = '%s <= %s' class MinPrefixlen(_PrefixlenMixin, Lookup): lookup_name = 'min_prefixlen' format_string = '%s >= %s' class Prefixlen(Transform): lookup_name = 'prefixlen' def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) return "masklen(%s)" % lhs, params @property def output_field(self): return IntegerField() django-netfields-1.2.4/netfields/mac.py0000644000175000001440000000023612140251021020303 0ustar jfunkusers00000000000000import netaddr class mac_unix_common(netaddr.mac_eui48): """Common form of UNIX MAC address dialect class""" word_sep = ':' word_fmt = '%.2x' django-netfields-1.2.4/netfields/managers.py0000644000175000001440000000111713356670455021371 0ustar jfunkusers00000000000000from django.db import models from ipaddress import _BaseNetwork try: str_type = unicode except NameError: str_type = str class NetManager(models.Manager): use_for_related_fields = True 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_type(val) return super(NetManager, self).filter(*args, **kwargs) django-netfields-1.2.4/netfields/models.py0000644000175000001440000000000012755620125021034 0ustar jfunkusers00000000000000django-netfields-1.2.4/netfields/psycopg2_types.py0000644000175000001440000000145713061763326022566 0ustar jfunkusers00000000000000import 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() + 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) django-netfields-1.2.4/netfields/rest_framework.py0000644000175000001440000000456213750762473022635 0ustar jfunkusers00000000000000from __future__ import absolute_import from ipaddress import ip_interface, ip_network, ip_address from netaddr import EUI from netaddr.core import AddrFormatError from rest_framework import serializers from netfields.compat import text_type from netfields.mac import mac_unix_common from netfields import fields class InetAddressField(serializers.Field): default_error_messages = { 'invalid': 'Invalid IP address.' } def __init__(self, store_prefix=True, *args, **kwargs): self.store_prefix = store_prefix super(InetAddressField, self).__init__(*args, **kwargs) def to_representation(self, value): if value is None: return value return text_type(value) def to_internal_value(self, data): if data is None: return data try: if self.store_prefix: return ip_interface(data) else: return ip_address(data) except ValueError: self.fail('invalid') class CidrAddressField(serializers.Field): default_error_messages = { 'invalid': 'Invalid CIDR address.', 'network': 'Must be a network address.', } def to_representation(self, value): if value is None: return value return text_type(value) def to_internal_value(self, data): if data is None: return data try: return ip_network(data) except ValueError as e: if 'has host bits' in e.args[0]: self.fail('network') self.fail('invalid') class MACAddressField(serializers.Field): default_error_messages = { 'invalid': 'Invalid MAC address.' } def to_representation(self, value): if value is None: return value return text_type(value) def to_internal_value(self, data): if data is None: return data try: return EUI(data, dialect=mac_unix_common) except (AddrFormatError, TypeError): self.fail('invalid') class NetModelSerializer(serializers.ModelSerializer): pass NetModelSerializer.serializer_field_mapping[fields.InetAddressField] = InetAddressField NetModelSerializer.serializer_field_mapping[fields.CidrAddressField] = CidrAddressField NetModelSerializer.serializer_field_mapping[fields.MACAddressField] = MACAddressField django-netfields-1.2.4/requirements.txt0000644000175000001440000000003513476236622020524 0ustar jfunkusers00000000000000django>=1.8 netaddr psycopg2 django-netfields-1.2.4/setup.cfg0000644000175000001440000000004614120406214017042 0ustar jfunkusers00000000000000[egg_info] tag_build = tag_date = 0 django-netfields-1.2.4/setup.py0000755000175000001440000000227214120406105016740 0ustar jfunkusers00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from distutils.core import setup from setuptools import find_packages import os import sys def get_long_description(): path = os.path.join(os.path.dirname(__file__), 'README.rst') with open(path) as f: return f.read() requirements = [ 'netaddr', 'django>=1.8', 'six', ] if sys.version_info.major == 2: requirements.append('ipaddress') setup( name='django-netfields', version='1.2.4', license='BSD', description='Django PostgreSQL netfields implementation', long_description=get_long_description(), url='https://github.com/jimfunk/django-postgresql-netfields', author=u'James Oakley', author_email='jfunk@funktronics.ca', packages=find_packages(), include_package_data=True, zip_safe=False, install_requires=requirements, classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Utilities', ], ) django-netfields-1.2.4/test/0000755000175000001440000000000014120406214016200 5ustar jfunkusers00000000000000django-netfields-1.2.4/test/__init__.py0000644000175000001440000000000012755620125020312 0ustar jfunkusers00000000000000django-netfields-1.2.4/test/migrations/0000755000175000001440000000000014120406214020354 5ustar jfunkusers00000000000000django-netfields-1.2.4/test/migrations/0001_initial.py0000644000175000001440000001331313723246541023035 0ustar jfunkusers00000000000000# Generated by Django 3.0.7 on 2020-08-31 14:20 import django.contrib.postgres.fields from django.db import migrations, models import django.db.models.deletion import netfields.fields class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='AggregateTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('network', netfields.fields.CidrAddressField(blank=True, default=None, max_length=43, null=True)), ('inet', netfields.fields.InetAddressField(blank=True, default=None, max_length=39, null=True)), ], ), migrations.CreateModel( name='CidrArrayTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', django.contrib.postgres.fields.ArrayField(base_field=netfields.fields.CidrAddressField(max_length=43), blank=True, null=True, size=None)), ], options={ 'db_table': 'cidrarray', }, ), migrations.CreateModel( name='CidrTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', netfields.fields.CidrAddressField(max_length=43)), ], options={ 'db_table': 'cidr', }, ), migrations.CreateModel( name='InetArrayTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', django.contrib.postgres.fields.ArrayField(base_field=netfields.fields.InetAddressField(max_length=39), blank=True, null=True, size=None)), ], options={ 'db_table': 'inetarray', }, ), migrations.CreateModel( name='InetTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', netfields.fields.InetAddressField(max_length=39)), ], options={ 'db_table': 'inet', }, ), migrations.CreateModel( name='MACArrayTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', django.contrib.postgres.fields.ArrayField(base_field=netfields.fields.MACAddressField(), blank=True, null=True, size=None)), ], options={ 'db_table': 'macarray', }, ), migrations.CreateModel( name='MACTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', netfields.fields.MACAddressField(null=True)), ], options={ 'db_table': 'mac', }, ), migrations.CreateModel( name='NoPrefixInetTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', netfields.fields.InetAddressField(max_length=39)), ], options={ 'db_table': 'noprefixinet', }, ), migrations.CreateModel( name='NullCidrTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', netfields.fields.CidrAddressField(max_length=43, null=True)), ], options={ 'db_table': 'nullcidr', }, ), migrations.CreateModel( name='NullInetTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', netfields.fields.InetAddressField(max_length=39, null=True)), ], options={ 'db_table': 'nullinet', }, ), migrations.CreateModel( name='UniqueCidrTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', netfields.fields.CidrAddressField(max_length=43, unique=True)), ], options={ 'db_table': 'uniquecidr', }, ), migrations.CreateModel( name='UniqueInetTestModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('field', netfields.fields.InetAddressField(max_length=39, unique=True)), ], options={ 'db_table': 'uniqueinet', }, ), migrations.CreateModel( name='AggregateTestChildModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('network', netfields.fields.CidrAddressField(max_length=43)), ('inet', netfields.fields.InetAddressField(max_length=39)), ('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='children', to='test.AggregateTestModel')), ], ), ] django-netfields-1.2.4/test/migrations/__init__.py0000644000175000001440000000000013723246541022470 0ustar jfunkusers00000000000000django-netfields-1.2.4/test/models.py0000644000175000001440000000425213571242063020051 0ustar jfunkusers00000000000000from django.contrib.postgres.fields import ArrayField from django.db.models import Model, ForeignKey, CASCADE from netfields import InetAddressField, CidrAddressField, MACAddressField, \ NetManager class InetTestModel(Model): field = InetAddressField() objects = NetManager() class Meta: db_table = 'inet' class NullInetTestModel(Model): field = InetAddressField(null=True) objects = NetManager() class Meta: db_table = 'nullinet' class UniqueInetTestModel(Model): field = InetAddressField(unique=True) objects = NetManager() class Meta: db_table = 'uniqueinet' class NoPrefixInetTestModel(Model): field = InetAddressField(store_prefix_length=False) objects = NetManager() class Meta: db_table = 'noprefixinet' class CidrTestModel(Model): field = CidrAddressField() objects = NetManager() class Meta: db_table = 'cidr' class NullCidrTestModel(Model): field = CidrAddressField(null=True) objects = NetManager() class Meta: db_table = 'nullcidr' class UniqueCidrTestModel(Model): field = CidrAddressField(unique=True) objects = NetManager() class Meta: db_table = 'uniquecidr' class MACTestModel(Model): field = MACAddressField(null=True) objects = NetManager() class Meta: db_table = 'mac' class InetArrayTestModel(Model): field = ArrayField(InetAddressField(), blank=True, null=True) class Meta: db_table = 'inetarray' class CidrArrayTestModel(Model): field = ArrayField(CidrAddressField(), blank=True, null=True) class Meta: db_table = 'cidrarray' class MACArrayTestModel(Model): field = ArrayField(MACAddressField(), blank=True, null=True) class Meta: db_table = 'macarray' class AggregateTestModel(Model): network = CidrAddressField(blank=True, null=True, default=None) inet = InetAddressField(blank=True, null=True, default=None) class AggregateTestChildModel(Model): parent = ForeignKey( 'AggregateTestModel', related_name='children', on_delete=CASCADE, ) network = CidrAddressField() inet = InetAddressField() django-netfields-1.2.4/test/tests/0000755000175000001440000000000014120406214017342 5ustar jfunkusers00000000000000django-netfields-1.2.4/test/tests/__init__.py0000644000175000001440000000000012755620125021454 0ustar jfunkusers00000000000000django-netfields-1.2.4/test/tests/test_form_fields.py0000644000175000001440000001755014120405613023256 0ustar jfunkusers00000000000000from __future__ import unicode_literals from ipaddress import ip_address, ip_interface, ip_network from netaddr import EUI from django.forms import ModelForm from django.test import TestCase from test.models import ( CidrTestModel, InetTestModel, UniqueInetTestModel, UniqueCidrTestModel, NoPrefixInetTestModel, MACTestModel ) from netfields.mac import mac_unix_common class InetAddressTestModelForm(ModelForm): class Meta: model = InetTestModel exclude = [] class TestInetAddressFormField(TestCase): form_class = InetAddressTestModelForm def test_form_ipv4_valid(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_invalid(self): 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) self.assertTrue(form.is_valid()) form.save() instance = InetTestModel.objects.get(pk=instance.pk) self.assertEqual(instance.field, ip_interface('10.1.2.4/24')) 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')) def test_form_ipv6_invalid(self): 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) self.assertTrue(form.is_valid()) form.save() instance = InetTestModel.objects.get(pk=instance.pk) self.assertEqual(instance.field, ip_interface('2001:0:1::3/64')) class NoPrefixInetAddressTestModelForm(ModelForm): class Meta: model = NoPrefixInetTestModel exclude = [] class TestNoPrefixInetAddressFormField(TestCase): form_class = NoPrefixInetAddressTestModelForm def test_form_ipv4_valid(self): form = self.form_class({'field': '10.0.0.1'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['field'], ip_address('10.0.0.1')) def test_form_ipv4_invalid(self): form = self.form_class({'field': '10.0.0.1.2'}) self.assertFalse(form.is_valid()) def test_form_ipv4_prefix_invalid(self): instance = NoPrefixInetTestModel.objects.create(field='10.1.2.3/24') form = self.form_class({'field': '10.1.2.4/24'}, instance=instance) self.assertFalse(form.is_valid()) 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_address('2001:0:1::2')) def test_form_ipv6_invalid(self): form = self.form_class({'field': '2001:0::1::2'}) self.assertFalse(form.is_valid()) def test_form_ipv6_prefix_invalid(self): instance = NoPrefixInetTestModel.objects.create(field='2001:0:1::2/64') form = self.form_class({'field': '2001:0:1::3/64'}, instance=instance) self.assertFalse(form.is_valid()) class UniqueInetAddressTestModelForm(ModelForm): class Meta: model = UniqueInetTestModel exclude = [] class TestUniqueInetAddressFormField(TestInetAddressFormField): form_class = UniqueInetAddressTestModelForm class CidrAddressTestModelForm(ModelForm): class Meta: model = CidrTestModel exclude = [] class TestCidrAddressFormField(TestCase): form_class = CidrAddressTestModelForm def test_form_ipv4_valid(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_invalid(self): 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_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')) def test_form_ipv6_invalid(self): 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()) class UniqueCidrAddressTestModelForm(ModelForm): class Meta: model = UniqueCidrTestModel exclude = [] class TestUniqueCidrAddressFormField(TestCidrAddressFormField): form_class = UniqueCidrAddressTestModelForm class MacAddressTestModelForm(ModelForm): class Meta: model = MACTestModel exclude = [] class TestMacAddressFormField(TestCase): def setUp(self): self.mac = EUI('00:aa:2b:c3:dd:44', dialect=mac_unix_common) def test_unix(self): form = MacAddressTestModelForm({'field': '0:AA:2b:c3:dd:44'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['field'], self.mac) def test_unix_common(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_eui48(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_cisco(self): form = MacAddressTestModelForm({'field': '00aa.2bc3.dd44'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['field'], self.mac) def test_24bit_colon(self): form = MacAddressTestModelForm({'field': '00aa2b:c3dd44'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['field'], self.mac) def test_24bit_hyphen(self): form = MacAddressTestModelForm({'field': '00aa2b-c3dd44'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['field'], self.mac) def test_bare(self): form = MacAddressTestModelForm({'field': '00aa2b:c3dd44'}) 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()) django-netfields-1.2.4/test/tests/test_functions.py0000644000175000001440000002251313571242063022777 0ustar jfunkusers00000000000000from __future__ import unicode_literals from django import VERSION from ipaddress import ip_interface, ip_network from netaddr import EUI from django.db.models import Case, F, When from django.test import TestCase from unittest import skipIf from netfields.functions import ( Abbrev, Broadcast, Family, Host, Hostmask, Masklen, Netmask, Network, SetMasklen, AsText, IsSameFamily, Merge, Trunc ) from test.models import ( AggregateTestChildModel, AggregateTestModel, CidrTestModel, InetTestModel, MACTestModel ) class TestInetFieldFunctions(TestCase): def setUp(self): InetTestModel.objects.create(field='10.1.0.1/16') InetTestModel.objects.create(field='2001:4f8:3:ba::1/64') def test_abbreviate(self): qs = InetTestModel.objects.annotate(abbrv=Abbrev(F('field'))) self.assertEqual(qs[0].abbrv, '10.1.0.1/16') self.assertEqual(qs[1].abbrv, '2001:4f8:3:ba::1/64') def test_broadcast(self): qs = InetTestModel.objects.annotate(broadcast=Broadcast(F('field'))) self.assertEqual(qs[0].broadcast, ip_interface('10.1.255.255/16')) self.assertEqual(qs[1].broadcast, ip_interface('2001:4f8:3:ba:ffff:ffff:ffff:ffff/64')) def test_family(self): qs = InetTestModel.objects.annotate(family=Family(F('field'))) self.assertEqual(qs[0].family, 4) self.assertEqual(qs[1].family, 6) def test_host(self): qs = InetTestModel.objects.annotate(host=Host(F('field'))) self.assertEqual(qs[0].host, '10.1.0.1') self.assertEqual(qs[1].host, '2001:4f8:3:ba::1') def test_hostmask(self): qs = InetTestModel.objects.annotate(hostmask=Hostmask(F('field'))) self.assertEqual(qs[0].hostmask, ip_interface('0.0.255.255')) self.assertEqual(qs[1].hostmask, ip_interface('::ffff:ffff:ffff:ffff')) def test_masklen(self): qs = InetTestModel.objects.annotate(masklen=Masklen(F('field'))) self.assertEqual(qs[0].masklen, 16) self.assertEqual(qs[1].masklen, 64) def test_netmask(self): qs = InetTestModel.objects.annotate(netmask=Netmask(F('field'))) self.assertEqual(qs[0].netmask, ip_interface('255.255.0.0')) self.assertEqual(qs[1].netmask, ip_interface('ffff:ffff:ffff:ffff::')) def test_network(self): qs = InetTestModel.objects.annotate(network=Network(F('field'))) self.assertEqual(qs[0].network, ip_network('10.1.0.0/16')) self.assertEqual(qs[1].network, ip_network('2001:4f8:3:ba::/64')) def test_set_masklen(self): ( InetTestModel.objects .annotate(family=Family(F('field'))) .update( field=Case( When(family=4, then=SetMasklen(F('field'), 24)), When(family=6, then=SetMasklen(F('field'), 120)) ) ) ) qs = InetTestModel.objects.all() self.assertEqual(qs[0].field, ip_interface('10.1.0.1/24')) self.assertEqual(qs[1].field, ip_interface('2001:4f8:3:ba::1/120')) def test_as_text(self): qs = InetTestModel.objects.annotate(text=AsText(F('field'))) self.assertEqual(qs[0].text, '10.1.0.1/16') self.assertEqual(qs[1].text, '2001:4f8:3:ba::1/64') def test_is_same_family(self): parent = AggregateTestModel.objects.create(inet='0.0.0.0/0') AggregateTestChildModel.objects.create( parent=parent, inet='10.1.0.1/16', network='10.1.0.0/16' ) AggregateTestChildModel.objects.create( parent=parent, inet='2001:4f8:3:ba::1/64', network='2001:4f8:3:ba::/64' ) qs = ( AggregateTestChildModel.objects.annotate( is_same_family=IsSameFamily(F('inet'), F('parent__inet')) ) .order_by('id') ) self.assertEqual(qs[0].is_same_family, True) self.assertEqual(qs[1].is_same_family, False) def test_merge(self): parent = AggregateTestModel.objects.create(inet='10.0.0.0/24') AggregateTestChildModel.objects.create( parent=parent, inet='10.0.1.0/24', network='10.0.0.0/23' ) parent = AggregateTestModel.objects.create(inet='2001:4f8:3:ba::/64') AggregateTestChildModel.objects.create( parent=parent, inet='2001:4f8:3:bb::/64', network='2001:4f8:3:ba::/63' ) qs = ( AggregateTestChildModel.objects.annotate( merged=Merge(F('inet'), F('parent__inet')) ) ) self.assertEqual(qs[0].merged, qs[0].network) self.assertEqual(qs[1].merged, qs[1].network) class TestCidrFieldFunctions(TestCase): def setUp(self): CidrTestModel.objects.create(field='10.1.0.0/16') CidrTestModel.objects.create(field='2001:4f8:3:ba::/64') def test_abbreviate(self): qs = CidrTestModel.objects.annotate(abbrv=Abbrev(F('field'))) self.assertEqual(qs[0].abbrv, '10.1/16') self.assertEqual(qs[1].abbrv, '2001:4f8:3:ba/64') def test_broadcast(self): qs = CidrTestModel.objects.annotate(broadcast=Broadcast(F('field'))) self.assertEqual(qs[0].broadcast, ip_interface('10.1.255.255/16')) self.assertEqual(qs[1].broadcast, ip_interface('2001:4f8:3:ba:ffff:ffff:ffff:ffff/64')) def test_family(self): qs = CidrTestModel.objects.annotate(family=Family(F('field'))) self.assertEqual(qs[0].family, 4) self.assertEqual(qs[1].family, 6) def test_host(self): qs = CidrTestModel.objects.annotate(host=Host(F('field'))) self.assertEqual(qs[0].host, '10.1.0.0') self.assertEqual(qs[1].host, '2001:4f8:3:ba::') def test_hostmask(self): qs = CidrTestModel.objects.annotate(hostmask=Hostmask(F('field'))) self.assertEqual(qs[0].hostmask, ip_interface('0.0.255.255')) self.assertEqual(qs[1].hostmask, ip_interface('::ffff:ffff:ffff:ffff')) def test_masklen(self): qs = CidrTestModel.objects.annotate(masklen=Masklen(F('field'))) self.assertEqual(qs[0].masklen, 16) self.assertEqual(qs[1].masklen, 64) def test_netmask(self): qs = CidrTestModel.objects.annotate(netmask=Netmask(F('field'))) self.assertEqual(qs[0].netmask, ip_interface('255.255.0.0')) self.assertEqual(qs[1].netmask, ip_interface('ffff:ffff:ffff:ffff::')) def test_network(self): qs = CidrTestModel.objects.annotate(network=Network(F('field'))) self.assertEqual(qs[0].network, ip_network('10.1.0.0/16')) self.assertEqual(qs[1].network, ip_network('2001:4f8:3:ba::/64')) def test_set_masklen(self): ( CidrTestModel.objects .annotate(family=Family(F('field'))) .update( field=Case( When(family=4, then=SetMasklen(F('field'), 24)), When(family=6, then=SetMasklen(F('field'), 120)) ) ) ) qs = CidrTestModel.objects.all() self.assertEqual(qs[0].field, ip_network('10.1.0.0/24')) self.assertEqual(qs[1].field, ip_network('2001:4f8:3:ba::/120')) def test_as_text(self): qs = CidrTestModel.objects.annotate(text=AsText(F('field'))) self.assertEqual(qs[0].text, '10.1.0.0/16') self.assertEqual(qs[1].text, '2001:4f8:3:ba::/64') def test_is_same_family(self): parent = AggregateTestModel.objects.create(network='0.0.0.0/0') AggregateTestChildModel.objects.create( parent=parent, inet= '10.1.0.1/16', network='10.1.0.0/16' ) AggregateTestChildModel.objects.create( parent=parent, inet='2001:4f8:3:ba::1/64', network='2001:4f8:3:ba::/64' ) qs = ( AggregateTestChildModel.objects.annotate( is_same_family=IsSameFamily(F('network'), F('parent__network')) ) .order_by('id') ) self.assertEqual(qs[0].is_same_family, True) self.assertEqual(qs[1].is_same_family, False) def test_merge(self): parent = AggregateTestModel.objects.create(network='10.0.0.0/24') AggregateTestChildModel.objects.create( parent=parent, inet='10.0.1.0/24', network='10.0.0.0/23' ) parent = AggregateTestModel.objects.create(network='2001:4f8:3:ba::/64') AggregateTestChildModel.objects.create( parent=parent, inet='2001:4f8:3:bb::/64', network='2001:4f8:3:ba::/63' ) qs = ( AggregateTestChildModel.objects.annotate( merged=Merge(F('network'), F('parent__network')) ) ) self.assertEqual(qs[0].merged, qs[0].network) self.assertEqual(qs[1].merged, qs[1].network) @skipIf(VERSION < (2, 0), 'Django unable to resolve type of num_ips to be IntegerField until 2.0.') def test_read_me_example(self): qs = ( CidrTestModel.objects.annotate( family=Family(F('field')), num_ips=2 ** (32 - Masklen(F('field'))), ) .filter(family=4) ) self.assertEqual(qs[0].num_ips, 65536) class TestMacFieldFunctions(TestCase): def setUp(self): MACTestModel.objects.create(field='aa:bb:cc:dd:ee:ff') def test_trunc(self): qs = MACTestModel.objects.annotate(trunc=Trunc(F('field'))) self.assertEqual(qs[0].trunc, EUI('aa:bb:cc:00:00:00')) django-netfields-1.2.4/test/tests/test_rest_framework_fields.py0000644000175000001440000000732513750762473025366 0ustar jfunkusers00000000000000from __future__ import absolute_import, unicode_literals from rest_framework import serializers import unittest2 as unittest from netfields import rest_framework as fields class FieldsTestCase(unittest.TestCase): def test_validation_inet_field(self): class TestSerializer(serializers.Serializer): ip = fields.InetAddressField() 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."]) def test_validation_cidr_field(self): class TestSerializer(serializers.Serializer): cidr = fields.CidrAddressField() 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."]) def test_network_validation_cidr_field(self): class TestSerializer(serializers.Serializer): cidr = fields.CidrAddressField() address = '10.0.0.1/24' serializer = TestSerializer(data={'cidr': address}) with self.assertRaises(serializers.ValidationError) as e: serializer.is_valid(raise_exception=True) self.assertEqual(e.exception.detail['cidr'], ["Must be a network address."]) def test_validation_mac_field(self): class TestSerializer(serializers.Serializer): mac = fields.MACAddressField() for invalid_address in ("de:", {"not": "a mac"}): with self.subTest(invalid_address=invalid_address): serializer = TestSerializer(data={'mac': invalid_address}) with self.assertRaises(serializers.ValidationError) as e: serializer.is_valid(raise_exception=True) self.assertEqual(e.exception.detail['mac'], ["Invalid MAC address."]) def test_inet_validation_additional_validators(self): def validate(value): raise serializers.ValidationError('Invalid.') class TestSerializer(serializers.Serializer): ip = fields.InetAddressField(validators=[validate]) address = '1.2.3.4/24' 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.']) def test_cidr_validation_additional_validators(self): def validate(value): raise serializers.ValidationError('Invalid.') class TestSerializer(serializers.Serializer): ip = fields.CidrAddressField(validators=[validate]) address = '1.2.3.0/24' 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.']) def test_mac_validation_additional_validators(self): def validate(value): raise serializers.ValidationError('Invalid.') class TestSerializer(serializers.Serializer): ip = fields.MACAddressField(validators=[validate]) address = '01:23:45:67:89:ab' 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.']) django-netfields-1.2.4/test/tests/test_sql_fields.py0000644000175000001440000006016514114122144023110 0ustar jfunkusers00000000000000from __future__ import unicode_literals import warnings import django from django import VERSION from django.core.exceptions import ValidationError from ipaddress import ( IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network, ip_address, ip_interface, ip_network, ) from netaddr import EUI from django.db import IntegrityError from django.db.models import F from django.core.exceptions import EmptyResultSet, FieldError from django.test import TestCase from unittest import skipIf from test.models import ( CidrArrayTestModel, CidrTestModel, InetArrayTestModel, InetTestModel, NullCidrTestModel, NullInetTestModel, UniqueInetTestModel, UniqueCidrTestModel, NoPrefixInetTestModel, MACArrayTestModel, MACTestModel, AggregateTestModel, AggregateTestChildModel ) class BaseSqlTestCase(object): select = u'SELECT "table"."id", "table"."field" FROM "table" ' def assertSqlEquals(self, qs, sql): sql = sql.replace('"table"', '"%s"' % self.table) self.assertEqual( qs.query.get_compiler(qs.db).as_sql()[0].strip().lower(), sql.strip().lower() ) def compile_queryset(self, qs): qs.query.get_compiler(qs.db).as_sql() def test_init_with_blank(self): self.model() def test_isnull_true_lookup(self): self.assertSqlEquals( self.qs.filter(field__isnull=True), self.select + 'WHERE "table"."field" IS NULL' ) def test_isnull_false_lookup(self): self.assertSqlEquals( self.qs.filter(field__isnull=False), self.select + 'WHERE "table"."field" IS NOT NULL' ) def test_save(self): self.model(field=self.value1).save() def test_equals_lookup(self): self.assertSqlEquals( self.qs.filter(field=self.value1), self.select + 'WHERE "table"."field" = %s' ) def test_exact_lookup(self): self.assertSqlEquals( self.qs.filter(field__exact=self.value1), self.select + 'WHERE "table"."field" = %s' ) def test_in_lookup(self): self.assertSqlEquals( self.qs.filter(field__in=[self.value1, self.value2]), self.select + 'WHERE "table"."field" IN (%s, %s)' ) def test_in_single_lookup(self): self.assertSqlEquals( self.qs.filter(field__in=[self.value1]), self.select + 'WHERE "table"."field" IN (%s)' ) def test_in_empty_lookup(self): with self.assertRaises(EmptyResultSet): self.qs.filter(field__in=[]).query.get_compiler(self.qs.db).as_sql() def test_gt_lookup(self): self.assertSqlEquals( self.qs.filter(field__gt=self.value1), self.select + 'WHERE "table"."field" > %s' ) def test_gte_lookup(self): self.assertSqlEquals( self.qs.filter(field__gte=self.value1), self.select + 'WHERE "table"."field" >= %s' ) def test_lt_lookup(self): self.assertSqlEquals( self.qs.filter(field__lt=self.value1), self.select + 'WHERE "table"."field" < %s' ) def test_lte_lookup(self): self.assertSqlEquals( self.qs.filter(field__lte=self.value1), self.select + 'WHERE "table"."field" <= %s' ) def test_range_lookup(self): self.assertSqlEquals( self.qs.filter(field__range=(self.value1, self.value3)), self.select + 'WHERE "table"."field" BETWEEN %s AND %s' ) class BaseInetTestCase(BaseSqlTestCase): def test_save_object(self): self.model(field=self.value1).save() def test_save_with_text_fails(self): self.assertRaises(ValidationError, self.model.objects.create, field='abc') def test_iexact_lookup(self): self.assertSqlEquals( self.qs.filter(field__iexact=self.value1), self.select + 'WHERE UPPER("table"."field"::text) = UPPER(%s)' ) def test_search_lookup_fails(self): if VERSION >= (2, 0): expected = FieldError else: expected = NotImplementedError with self.assertRaises(expected): self.compile_queryset(self.qs.filter(field__search='10')) def test_year_lookup_fails(self): with self.assertRaises(FieldError): self.compile_queryset(self.qs.filter(field__year=1)) def test_month_lookup_fails(self): with self.assertRaises(FieldError): self.compile_queryset(self.qs.filter(field__month=1)) def test_day_lookup_fails(self): with self.assertRaises(FieldError): self.compile_queryset(self.qs.filter(field__day=1)) def test_net_contained(self): self.assertSqlEquals( self.qs.filter(field__net_contained='10.0.0.0/24'), self.select + 'WHERE "table"."field" << %s' ) def test_net_contained_or_equals(self): self.assertSqlEquals( self.qs.filter(field__net_contained_or_equal='10.0.0.0/24'), self.select + 'WHERE "table"."field" <<= %s' ) def test_net_overlaps(self): self.assertSqlEquals( self.qs.filter(field__net_overlaps='10.0.0.0/24'), self.select + 'WHERE "table"."field" && %s', ) def test_family_lookup(self): self.assertSqlEquals( self.qs.filter(field__family=4), self.select + 'WHERE family("table"."field") = %s' ) def test_host_lookup_sql(self): self.assertSqlEquals( self.qs.filter(field__host="10.0.0.1"), self.select + 'WHERE HOST("table"."field") = HOST(%s)' ) def test_prefixlen_exact_lookup_sql(self): self.assertSqlEquals( self.qs.filter(field__prefixlen='16'), self.select + 'WHERE MASKLEN("table"."field") = %s' ) def test_prefixlen_in_lookup_sql(self): self.assertSqlEquals( self.qs.filter(field__prefixlen__in=['16', '24']), self.select + 'WHERE MASKLEN("table"."field") IN (%s, %s)' ) def test_prefixlen_in_single_lookup_sql(self): self.assertSqlEquals( self.qs.filter(field__prefixlen__in=['16']), self.select + 'WHERE MASKLEN("table"."field") IN (%s)' ) def test_prefixlen_in_empty_lookup_sql(self): with self.assertRaises(EmptyResultSet): self.qs.filter(field__prefixlen__in=[]).query.get_compiler(self.qs.db).as_sql() def test_prefixlen_gt_lookup_sql(self): self.assertSqlEquals( self.qs.filter(field__prefixlen__gt="16"), self.select + 'WHERE MASKLEN("table"."field") > %s' ) def test_prefixlen_gte_lookup_sql(self): self.assertSqlEquals( self.qs.filter(field__prefixlen__gte="16"), self.select + 'WHERE MASKLEN("table"."field") >= %s' ) def test_prefixlen_lt_lookup_sql(self): self.assertSqlEquals( self.qs.filter(field__prefixlen__lt="16"), self.select + 'WHERE MASKLEN("table"."field") < %s' ) def test_prefixlen_lte_lookup_sql(self): self.assertSqlEquals( self.qs.filter(field__prefixlen__lte="16"), self.select + 'WHERE MASKLEN("table"."field") <= %s' ) def test_query_filter_f_expression(self): self.model.objects.filter(field=F('field')) @skipIf(VERSION < (1, 11), 'Subquery added in Django 1.11. https://docs.djangoproject.com/en/1.11/ref/models/expressions/#subquery-expressions') def test_query_filter_subquery(self): from django.db.models import OuterRef, Subquery self.model.objects.annotate( samefield=Subquery( self.model.objects .filter( field=OuterRef('field') ) .values('field')[:1] ) ) class BaseInetFieldTestCase(BaseInetTestCase): value1 = '10.0.0.1' value2 = '10.0.0.2/24' value3 = '10.0.0.10' def test_startswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__startswith='10.'), self.select + 'WHERE HOST("table"."field") LIKE %s' ) def test_istartswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__istartswith='10.'), self.select + 'WHERE HOST("table"."field") LIKE UPPER(%s)' ) def test_endswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__endswith='.1'), self.select + 'WHERE HOST("table"."field") LIKE %s' ) def test_iendswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__iendswith='.1'), self.select + 'WHERE HOST("table"."field") LIKE UPPER(%s)' ) def test_regex_lookup(self): self.assertSqlEquals( self.qs.filter(field__regex='10'), self.select + 'WHERE HOST("table"."field") ~ %s' ) def test_iregex_lookup(self): self.assertSqlEquals( self.qs.filter(field__iregex='10'), self.select + 'WHERE HOST("table"."field") ~* %s' ) def test_query_filter_str(self): self.model.objects.filter(field='1.2.3.4') 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' value2 = '10.0.2.0/24' value3 = '10.5.0.0/16' def test_startswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__startswith='10.'), self.select + 'WHERE TEXT("table"."field") LIKE %s' ) def test_istartswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__istartswith='10.'), self.select + 'WHERE TEXT("table"."field") LIKE UPPER(%s)' ) def test_endswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__endswith='.1'), self.select + 'WHERE TEXT("table"."field") LIKE %s' ) def test_iendswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__iendswith='.1'), self.select + 'WHERE TEXT("table"."field") LIKE UPPER(%s)' ) def test_regex_lookup(self): self.assertSqlEquals( self.qs.filter(field__regex='10'), self.select + 'WHERE TEXT("table"."field") ~ %s' ) def test_iregex_lookup(self): self.assertSqlEquals( self.qs.filter(field__iregex='10'), self.select + 'WHERE TEXT("table"."field") ~* %s' ) def test_net_contains_lookup(self): self.assertSqlEquals( self.qs.filter(field__net_contains='10.0.0.1'), self.select + 'WHERE "table"."field" >> %s' ) def test_net_contains_or_equals(self): self.assertSqlEquals( self.qs.filter(field__net_contains_or_equals='10.0.0.1'), self.select + 'WHERE "table"."field" >>= %s' ) def test_query_filter_str(self): self.model.objects.filter(field='1.2.3.0/24') def test_query_filter_ipnetwork(self): self.model.objects.filter(field=ip_network('1.2.3.0/24')) def test_max_prefixlen(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") self.assertSqlEquals( self.qs.filter(field__max_prefixlen='16'), self.select + 'WHERE masklen("table"."field") <= %s' ) assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) def test_min_prefixlen(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") self.assertSqlEquals( self.qs.filter(field__min_prefixlen='16'), self.select + 'WHERE masklen("table"."field") >= %s' ) assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) class TestInetField(BaseInetFieldTestCase, TestCase): def setUp(self): self.model = InetTestModel self.qs = self.model.objects.all() self.table = 'inet' def test_save_blank_fails(self): self.assertRaises(IntegrityError, self.model(field='').save) def test_save_none_fails(self): self.assertRaises(IntegrityError, self.model(field=None).save) def test_save_nothing_fails(self): self.assertRaises(IntegrityError, self.model().save) def test_save_accepts_bytes(self): self.model(field=b'1.1.1.1/24').save() def test_retrieves_ipv4_ipinterface_type(self): instance = self.model.objects.create(field='10.1.2.3/24') instance = self.model.objects.get(pk=instance.pk) self.assertIsInstance(instance.field, IPv4Interface) def test_retrieves_ipv6_ipinterface_type(self): instance = self.model.objects.create(field='2001:db8::1/32') instance = self.model.objects.get(pk=instance.pk) self.assertIsInstance(instance.field, IPv6Interface) def test_save_preserves_prefix_length(self): instance = self.model.objects.create(field='10.1.2.3/24') instance = self.model.objects.get(pk=instance.pk) self.assertEqual(str(instance.field), '10.1.2.3/24') def test_host(self): instance = self.model.objects.create(field='10.1.2.3/24') instance = self.model.objects.get(field__host='10.1.2.3') self.assertEqual(str(instance.field), '10.1.2.3/24') instance = self.model.objects.get(field__host='10.1.2.3/27') self.assertEqual(str(instance.field), '10.1.2.3/24') class TestInetFieldNullable(BaseInetFieldTestCase, TestCase): def setUp(self): self.model = NullInetTestModel self.qs = self.model.objects.all() self.table = 'nullinet' def test_save_blank(self): self.model().save() def test_save_none(self): self.model(field=None).save() def test_save_nothing_fails(self): self.model().save() class TestInetFieldUnique(BaseInetFieldTestCase, TestCase): def setUp(self): self.model = UniqueInetTestModel self.qs = self.model.objects.all() self.table = 'uniqueinet' def test_save_nonunique(self): self.model(field='1.2.3.4').save() self.assertRaises(IntegrityError, self.model(field='1.2.3.4').save) class TestInetFieldNoPrefix(BaseInetFieldTestCase, TestCase): def setUp(self): self.model = NoPrefixInetTestModel self.qs = self.model.objects.all() self.table = 'noprefixinet' def test_save_truncates_prefix_length(self): instance = self.model.objects.create(field='10.1.2.3/24') instance = self.model.objects.get(pk=instance.pk) self.assertEqual(str(instance.field), '10.1.2.3') def test_retrieves_ipv4_ipaddress_type(self): instance = self.model.objects.create(field='10.1.2.3/24') instance = self.model.objects.get(pk=instance.pk) self.assertIsInstance(instance.field, IPv4Address) def test_retrieves_ipv6_ipaddress_type(self): instance = self.model.objects.create(field='2001:db8::1/32') instance = self.model.objects.get(pk=instance.pk) self.assertIsInstance(instance.field, IPv6Address) def test_net_contained_network(self): self.model.objects.create(field='10.1.2.1') self.model.objects.create(field='10.1.3.1') query = self.model.objects.filter(field__net_contained='10.1.2.0/24') self.assertEqual(query.count(), 1) self.assertEqual(query[0].field, ip_address('10.1.2.1')) class TestCidrField(BaseCidrFieldTestCase, TestCase): def setUp(self): self.model = CidrTestModel self.qs = self.model.objects.all() self.table = 'cidr' def test_save_blank_fails(self): self.assertRaises(IntegrityError, self.model(field='').save) def test_save_none_fails(self): self.assertRaises(IntegrityError, self.model(field=None).save) def test_save_nothing_fails(self): self.assertRaises(IntegrityError, self.model().save) def test_retrieves_ipv4_ipnetwork_type(self): instance = self.model.objects.create(field='10.1.2.0/24') instance = self.model.objects.get(pk=instance.pk) self.assertIsInstance(instance.field, IPv4Network) def test_retrieves_ipv6_ipnetwork_type(self): instance = self.model.objects.create(field='2001:db8::0/32') instance = self.model.objects.get(pk=instance.pk) self.assertIsInstance(instance.field, IPv6Network) def test_host(self): instance = self.model.objects.create(field='10.1.2.0/24') instance = self.model.objects.get(field__host='10.1.2.0') self.assertEqual(str(instance.field), '10.1.2.0/24') instance = self.model.objects.get(field__host='10.1.2.0/27') self.assertEqual(str(instance.field), '10.1.2.0/24') class TestCidrFieldNullable(BaseCidrFieldTestCase, TestCase): def setUp(self): self.model = NullCidrTestModel self.qs = self.model.objects.all() self.table = 'nullcidr' def test_save_blank(self): self.model().save() def test_save_none(self): self.model(field=None).save() def test_save_nothing_fails(self): self.model().save() class TestCidrFieldUnique(BaseCidrFieldTestCase, TestCase): def setUp(self): self.model = UniqueCidrTestModel self.qs = self.model.objects.all() self.table = 'uniquecidr' def test_save_nonunique(self): self.model(field='1.2.3.0/24').save() self.assertRaises(IntegrityError, self.model(field='1.2.3.0/24').save) class BaseMacTestCase(BaseSqlTestCase): value1 = '00:aa:2b:c3:dd:44' value2 = '00:aa:2b:c3:dd:45' value3 = '00:aa:2b:c3:dd:ff' def test_save_object(self): self.model(field=EUI(self.value1)).save() def test_iexact_lookup(self): self.assertSqlEquals( self.qs.filter(field__iexact=self.value1), self.select + 'WHERE UPPER("table"."field"::text) = UPPER(%s)' ) def test_startswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__startswith='00:'), self.select + 'WHERE "table"."field"::text LIKE %s' ) def test_istartswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__istartswith='00:'), self.select + 'WHERE UPPER("table"."field"::text) LIKE UPPER(%s)' ) def test_endswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__endswith=':ff'), self.select + 'WHERE "table"."field"::text LIKE %s' ) def test_iendswith_lookup(self): self.assertSqlEquals( self.qs.filter(field__iendswith=':ff'), self.select + 'WHERE UPPER("table"."field"::text) LIKE UPPER(%s)' ) def test_regex_lookup(self): self.assertSqlEquals( self.qs.filter(field__regex='00'), self.select + 'WHERE "table"."field"::text ~ %s' ) def test_iregex_lookup(self): self.assertSqlEquals( self.qs.filter(field__iregex='00'), self.select + 'WHERE "table"."field"::text ~* %s' ) def test_query_filter_f_expression(self): self.model.objects.filter(field=F('field')) @skipIf(VERSION < (1, 11), 'Subquery added in Django 1.11. https://docs.djangoproject.com/en/1.11/ref/models/expressions/#subquery-expressions') def test_query_filter_subquery(self): from django.db.models import OuterRef, Subquery self.model.objects.annotate( samefield=Subquery( self.model.objects .filter( field=OuterRef('field') ) .values('field')[:1] ) ) class TestMacAddressField(BaseMacTestCase, TestCase): def setUp(self): self.model = MACTestModel self.qs = self.model.objects.all() self.table = 'mac' def test_save_blank(self): self.model().save() def test_save_none(self): self.model(field=None).save() def test_save_nothing_fails(self): self.model().save() def test_invalid_fails(self): self.assertRaises(ValidationError, lambda: self.model(field='foobar').save()) def test_retrieves_eui_type(self): instance = self.model.objects.create(field='00:aa:2b:c3:dd:44') instance = self.model.objects.get(pk=instance.pk) self.assertIsInstance(instance.field, EUI) class TestInetAddressFieldArray(TestCase): def test_save_null(self): InetArrayTestModel().save() def test_save_single_item(self): InetArrayTestModel(field=['10.1.1.1/24']).save() def test_save_multiple_items(self): InetArrayTestModel(field=['10.1.1.1', '10.1.1.2']).save() @skipIf(VERSION < (1, 10), 'ArrayField does not return correct types in Django < 1.10. https://code.djangoproject.com/ticket/25143') def test_retrieves_ipv4_ipinterface_type(self): instance = InetArrayTestModel(field=['10.1.1.1/24']) instance.save() instance = InetArrayTestModel.objects.get(id=instance.id) self.assertEqual(instance.field, [IPv4Interface('10.1.1.1/24')]) self.assertIsInstance(instance.field[0], IPv4Interface) class TestCidrAddressFieldArray(TestCase): def test_save_null(self): CidrArrayTestModel().save() def test_save_single_item(self): CidrArrayTestModel(field=['10.1.1.0/24']).save() def test_save_multiple_items(self): CidrArrayTestModel(field=['10.1.1.0/24', '10.1.2.0/24']).save() @skipIf(VERSION < (1, 10), 'ArrayField does not return correct types in Django < 1.10. https://code.djangoproject.com/ticket/25143') def test_retrieves_ipv4_ipnetwork_type(self): instance = CidrArrayTestModel(field=['10.1.1.0/24']) instance.save() instance = CidrArrayTestModel.objects.get(id=instance.id) self.assertEqual(instance.field, [IPv4Network('10.1.1.0/24')]) self.assertIsInstance(instance.field[0], IPv4Network) class TestMACAddressFieldArray(TestCase): def test_save_null(self): MACArrayTestModel().save() def test_save_single_item(self): MACArrayTestModel(field=['00:aa:2b:c3:dd:44']).save() def test_save_multiple_items(self): MACArrayTestModel(field=['00:aa:2b:c3:dd:44', '00:aa:2b:c3:dd:45']).save() @skipIf(VERSION < (1, 10), 'ArrayField does not return correct types in Django < 1.10. https://code.djangoproject.com/ticket/25143') def test_retrieves_eui_type(self): instance = MACArrayTestModel(field=['00:aa:2b:c3:dd:44']) instance.save() 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, [None]) 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, [None]) AggregateTestChildModel.objects.create(parent=parent, network=network, inet=inet) self.assertEqual(network_qs[0].agg_network, [network]) django-netfields-1.2.4/testsettings.py0000644000175000001440000000064112755620125020347 0ustar jfunkusers00000000000000DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'netfields', } } INSTALLED_APPS = ( 'netfields', 'test', ) MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ) SECRET_KEY = "notimportant"