Coder Social home page Coder Social logo

django-postgresql-netfields's Introduction

Django PostgreSQL Netfields

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, InetAddressField, a CidrAddressField a MACAddressField, and a MACAddress8Field have been added. This library also provides a manager that allows for advanced IP based lookups 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 fields are represented as EUI types from the netaddr module.

Dependencies

This module requires Django >= 1.11, psycopg2 or psycopg, and netaddr.

Installation

$ 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.

from netfields import InetAddressField, NetManager

class Example(models.Model):
    inet = InetAddressField()
    # ...

    objects = NetManager()

CidrAddressField will store values in PostgreSQL as type CIDR. In Python, the value will be represented as an ipaddress.ip_network object.

from netfields import CidrAddressField, NetManager

class Example(models.Model):
    inet = CidrAddressField()
    # ...

    objects = NetManager()

MACAddressField will store values in PostgreSQL as type MACADDR. In Python, the value will be represented as a netaddr.EUI object. Note that the default text representation of EUI objects is not the same as that of the netaddr module. It is represented in a format that is more commonly used in network utilities and by network administrators (00:11:22:aa:bb:cc).

from netfields import MACAddressField, NetManager

class Example(models.Model):
    inet = MACAddressField()
    # ...

MACAddress8Field will store values in PostgreSQL as type MACADDR8. In Python, the value will be represented as a netaddr.EUI object. As with MACAddressField, the representation is the common one (00:11:22:aa:bb:cc:dd:ee).

from netfields import MACAddress8Field, NetManager

class Example(models.Model):
    inet = MACAddress8Field()
    # ...

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.

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

MACAddress8Field Functions

Postgres Function Django Function Return Type Description
trunc(T) Trunc T set last 5 bytes to zero
macaddr8_set7bit(T) Macaddr8Set7bit T set 7th bit to one. Used to generate link-local IPv6 addresses

Indexes

As of Django 2.2, indexes can be created for InetAddressField and CidrAddressField extra lookups directly on the model.

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.

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);"
        ),
        # ...
    ]
  • 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

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-postgresql-netfields's People

Contributors

adamcik avatar andir avatar benoit9126 avatar c-w avatar codeout avatar eide avatar ekohl avatar etene avatar fxfitz avatar gforcada avatar hcastilho avatar higherorderfunctor avatar hylje avatar jaychoo avatar jimfunk avatar jmacul2 avatar jsenecal avatar jwilhelm-godaddy avatar kbirkeland avatar key avatar mikelane avatar orf avatar oyvindkolbu avatar robertobarreda avatar sevdog avatar shenek avatar smclenithan avatar tfromme avatar wsot avatar yuvadm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-postgresql-netfields's Issues

Searches using startswith, istartswith, endswith, iendswith fail on lastest code

I've been AFK for a bit but have gotten back into using this library and updated my project to use this and I am getting some issues. Searches with startswith, endswith and their case insensitive counterparts are returning no records. I dug into the code and it appears to not be implemented correctly. Lookups are not extending the respective core Django lookup.

class EndsWith(NetFieldDecoratorMixin, BuiltinLookup):
    lookup_name = 'endswith'


class IEndsWith(NetFieldDecoratorMixin, BuiltinLookup):
    lookup_name = 'iendswith'


class StartsWith(NetFieldDecoratorMixin, BuiltinLookup):
    lookup_name = 'startswith'


class IStartsWith(NetFieldDecoratorMixin, BuiltinLookup):
    lookup_name = 'istartswith'

When I change the code in lookups to what is below, things appear to be running fine.

class EndsWith(NetFieldDecoratorMixin, EndsWith):
    lookup_name = 'endswith'


class IEndsWith(NetFieldDecoratorMixin, EndsWith):
    lookup_name = 'iendswith'


class StartsWith(NetFieldDecoratorMixin, StartsWith):
    lookup_name = 'startswith'


class IStartsWith(NetFieldDecoratorMixin, IStartsWith):
    lookup_name = 'istartswith'

I am unclear why the the previous lookup commit is extending "BuiltinLookup" in these cases. I cant find documentation on Django's website that indicates that this is best practice. https://docs.djangoproject.com/en/1.9/howto/custom-lookups/

It indicated that you should just extend the core class as was done when creating the "Family" lookup.

I verified the generated SQL code and indeed, the SQL is missing the necessary "%" before and after the LIKE operator to generate the proper SQL.

IMO, I think it best to refactor this to just extend the proper classes so the work as intended. Please tell me if I am missing something. Otherwise I'll create a patch. I am quite surprised no one has caught this until now. This change was introduced last April. :(

Prefixlen lookups should be handled all in one, rather than three separate lookups

As brought up in #61:

This brought to mind a thought: Instead of having 3 lookups: __prefixlen, __max_prefixlen, and __min_prefixlen, wouldn't it make sense to be able to just have a single __prefixlen and be able to chain other operations onto it? So for example you could have:

  • MyModel.objects.filter(network__prefixlen=24)
  • MyModel.objects.filter(network__prefixlen__gte=24)
  • MyModel.objects.filter(network__prefixlen__lte=24)

I don't recall the exact implementation details for something like this, but what are your thoughts?

Currently, using __gte or __lte produces the following error:

django.core.exceptions.FieldError: Unsupported lookup 'prefixlen' for CidrAddressField or join on the field not permitted.

Trying to save a model with a CidrAddressField from Django Admin failes

I was trying to save a model in Django Admin, and it failed saying

TypeError: unexpected type <type 'list'> for addr arg

Model

class IpRule(models.Model):
    description = models.CharField(max_length=255)
    active = models.BooleanField(default=True)
    port = models.IntegerField(default=22)
    cidr_ip = CidrAddressField()

Stacktrace

Traceback (most recent call last):
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/contrib/staticfiles/handlers.py", line 67, in __call__
    return self.application(environ, start_response)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/ambition_messenger/server/django_runserver.py", line 88, in application
    return _django_app(environ, start_response)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 206, in __call__
    response = self.get_response(request)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 194, in get_response
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 229, in handle_uncaught_exception
    return debug.technical_500_response(request, *exc_info)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django_extensions/management/technical_response.py", line 5, in null_technical_500_response
    six.reraise(exc_type, exc_value, tb)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 112, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/contrib/admin/options.py", line 465, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/utils/decorators.py", line 99, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/views/decorators/cache.py", line 52, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/contrib/admin/sites.py", line 198, in inner
    return view(request, *args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/utils/decorators.py", line 29, in _wrapper
    return bound_func(*args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/utils/decorators.py", line 99, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/utils/decorators.py", line 25, in bound_func
    return func(self, *args2, **kwargs2)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/transaction.py", line 371, in inner
    return func(*args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/contrib/admin/options.py", line 1244, in change_view
    if form.is_valid():
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/forms/forms.py", line 129, in is_valid
    return self.is_bound and not bool(self.errors)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/forms/forms.py", line 121, in errors
    self.full_clean()
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/forms/forms.py", line 275, in full_clean
    self._post_clean()
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/forms/models.py", line 419, in _post_clean
    self.validate_unique()
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/forms/models.py", line 428, in validate_unique
    self.instance.validate_unique(exclude=exclude)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/base.py", line 754, in validate_unique
    errors = self._perform_unique_checks(unique_checks)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/base.py", line 838, in _perform_unique_checks
    qs = model_class._default_manager.filter(**lookup_kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/manager.py", line 163, in filter
    return self.get_queryset().filter(*args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/query.py", line 593, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/query.py", line 611, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1204, in add_q
    clause = self._add_q(where_part, used_aliases)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1240, in _add_q
    current_negated=current_negated)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1131, in build_filter
    clause.add(constraint, AND)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/utils/tree.py", line 104, in add
    data = self._prepare_data(data)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/sql/where.py", line 79, in _prepare_data
    value = obj.prepare(lookup_type, value)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/django/db/models/sql/where.py", line 352, in prepare
    return self.field.get_prep_lookup(lookup_type, value)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/netfields/fields.py", line 32, in get_prep_lookup
    return self.get_prep_value(value)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/netfields/fields.py", line 41, in get_prep_value
    return unicode(self.to_python(value))
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/netfields/fields.py", line 21, in to_python
    return self.python_type()(value)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/netaddr/ip/__init__.py", line 907, in __init__
    implicit_prefix, flags)
  File "/Users/micahhausler/code/secret-project/venv/lib/python2.7/site-packages/netaddr/ip/__init__.py", line 802, in parse_ip_network
    raise TypeError('unexpected type %s for addr arg' % type(addr))
TypeError: unexpected type <type 'list'> for addr arg

Pass on maintainership

In essence I've never been a user of this lib myself. It was initially written with the intention of using it in http://nav.uninett.no, but since I changed jobs I never got around to integrating it.

Any way, the point is really that this project deserves more love and better followup than I'm able to provide. So @jimfunk or @ekohl would any of you be interested in taking over the repo and pypi package for this?

Limit fields to a specific family (IPv6, IPv4, or both)

It could be a good idea to add a named parameter to fields in order to restrict them to a specific address family.

Of course, I'm not talking about an address family restriction on a database level but on a model level. In this way a model could include only IPv4 (or IPv6) address fields.

By default, a field could accept both IPv6 and IPv4 addresses (or networks) or if given a named parameter (family for instance), it will validate the value passed to it, checking it it from the right address family.

I can work on a PR if this issue is proposal is legit.

Validation for CIDR is not correct

I've received an error when I tried to save a CIDR like '64.34.117.2/20', and I got a database error, says the CIDR is not correct.
Then I check the source code of this plug-in, and found the piece of code here:

try:
    return IPNetwork(value)
except (AddrFormatError, TypeError), e:
    raise ValidationError(str(e))

I highly doubt that this is doing the right thing, it only check if we can convert the IP value to a valid IPNetwork object, instead of a valid CIDR.

postgresql_psycopg2 deprecation warning (django-netfields 1.2.2)

/Users/i1t/.pyenv/versions/3.6.4/lib/python3.6/site-packages/netfields/compat.py:4: RemovedInDjango30Warning: The django.db.backends.postgresql_psycopg2 module is deprecated in favor of django.db.backends.postgresql.
from django.db.backends.postgresql_psycopg2.base import DatabaseWrapper

ValidationError instead of Exceptions?

If I insert incorrect values in the fields (in the django admin) I get exceptions instead of regular validation errors.

It is possible to raise validation errors instead?

I can help out as soon as I start understanding more about it.

Use IPAddress object for fields that represent a single IP

I am experiencing an issue where my model can have an IP field that can be a network represented by a CIDR address or a single IP.

class MyModel:
    inet = CidrAddressField()

When creating an object with just a single IP (i.e 156.25.3.189), the object is created but the field returns a string:

k = MyModel.objects.create(inet='156.25.3.189')
k.inet
'156.25.3.189'

It would be nice if the conversion could be done to a IP Address object instead. Would this be possible?

Gracefully degrade to char fields on non-postgresql

Gracefully degrading to char fields on non-postgresql database backends would be useful for testing purposes (for instance, I often test on sqlite). postgres-specific operations could be emulated using the ipaddress type.

Adding a CidrAddressField to a model causes a 'blank' admin page

I have a very simple model:

class Network(BaseModel):
    block = CidrAddressField()
    office = models.ForeignKey(Office, null=True, blank=True)
    description = models.CharField(max_length=255, null=True, blank=True)

...and it is registered in admin.py:

@admin.register(Network)
class NetworkAdmin(BaseAdmin):
    list_display = ['block', 'office', 'description']
    fields = ['office', 'block', 'description']

When I attempt to view it in the admin section, I see the following:
screenshot from 2015-01-14 13 02 50

When I change the NetworkAdmin object to remove the 'block' field like so:

@admin.register(Network)
class NetworkAdmin(BaseAdmin):
    list_display = ['block', 'office', 'description']
    fields = ['office', 'description']

I see:
screenshot from 2015-01-14 13 04 00

The 'list view' display in admin works fine displaying the 'block' field, it just doesn't show up when I want to edit.

I've tried overriding the form generated by admin to specifically use the AdminTextInputWidget for the 'block' field, and it still shows up as a blank page.

Am I missing something?

Lookup by host

I've been playing with this project for the past few hours, and have been at a loss due to what seems like a major missing feature (or a completely undocumented feature)...

It should be possible to have a lookup such as __net_host or __net_address for direct IP lookups against an InetAddressField which has a CIDR prefix. This is already supported by PostgreSQL with WHERE HOST(my_inet_field) = '1.2.3.4'

Without this, I would have to store the IP address separately from the subnet (which defeats one of my main reasons for using the INET type) to be able to query them effectively by individual IP, without knowing the CIDR prefix beforehand.

Something which would operate similar to this:

SELECT  id, hostname, external_ip FROM servers;

   id    | hostname | external_ip
---------+----------+-------------
10       |     test | 1.2.3.4/22
11       |    test2 | 1.2.3.44/22

SELECT id, hostname, external_ip FROM servers WHERE HOST(external_ip) = '1.2.3.4';

   id    | hostname | external_ip
---------+----------+-------------
10       |     test | 1.2.3.4/22

It could be used like so:

Server.objects.get(external_ip__net_host='1.2.3.4')

Neither startswith nor endswith work for this, as they risk running into similar IP addresses, e.g. a query for 1.2.3.4 could bring results for 1.2.3.44 (starts with), or 11.2.3.4 (ends with).

setup.py

Hello,
I've created a trivial setup.py to make this an rpm. Could you add it to your project? The version number and classifiers sections needs to be changed I think. I can provide a suse friendly spec file for rpm users as well. Also please note that I'm not a django ninja so if this request is not appropriate you can simply ignore this request.

diff -Naur django-postgresql-netfields-0.05.org/setup.py django-postgresql-netfields-0.05/setup.py
--- django-postgresql-netfields-0.05.org/setup.py 1969-12-31 19:00:00.000000000 -0500
+++ django-postgresql-netfields-0.05/setup.py 2011-12-12 08:56:17.338927906 -0500
@@ -0,0 +1,20 @@
+from distutils.core import setup
+
+setup(name='django-postgresql-netfields',

  •  version='0.05',
    
  •  description='Django postgresql netfields implementation',
    
  •  author='Thomas Admacik',
    
  •  author_email='[email protected]',
    
  •  url='https://github.com/adamcik/django-postgresql-netfields',
    
  •  download_url='https://github.com/adamcik/django-postgresql-netfields/zipball/master',
    
  •  packages=['netfields'],
    
  •  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'],
    
  •  )
    

Compatibility issues with Django 2.0a1

I've started testing a local application for compatibility with Django 2.0a1, and found that netfields is encountering the following error:

  File ".tox/py36-django20/lib/python3.6/site-packages/netfields/__init__.py", line 1, in <module>
    from netfields.managers import NetManager
  File ".tox/py36-django20/lib/python3.6/site-packages/netfields/managers.py", line 35, in <module>
    class NetQuery(sql.Query):
  File ".tox/py36-django20/lib/python3.6/site-packages/netfields/managers.py", line 36, in NetQuery
    query_terms = sql.Query.query_terms.copy()
AttributeError: type object 'Query' has no attribute 'query_terms'

This is with django-netfields==0.7.2.

Python field type is str instead of ipaddress.ip_address

I have a model something like this that makes use of a InetAddressField. When referencing the field from a model method, i.e. self.field it returns a str instead of an IPv4Address instance. Wouldn't the expected behavior be to return a type of ipaddress.ip_address like the README says? Here's a crude unittest I wrote that exhibits this behavior (causes a AttributeError: 'str' object has no attribute 'is_loopback' error).

Doesn't work with the ArrayField

Hi!

I've got a model where I use netfields in array field:

from django.contrib.postgres.fields import ArrayField

class Service(models.Model):
    name = models.CharField(unique=True, max_length=64)
    address_classes = ArrayField(netfields.CidrAddressField(), default=[], 
            blank=True)

Unfortunately there is PostgreSQL error when I edit this with admin interface:

ERROR:  column "address_classes" is of type inet[] but expression is of type text[] at character 177
HINT:  You will need to rewrite or cast the expression.
STATEMENT:  UPDATE "base_service" SET "name" = 'some_name', "address_classes" = ARRAY['10.0.0.0/24', '10.1.0.0/16', '10.2.0.0/20'] WHERE "base_service"."id" = 1

I tested it with Django 1.9.4.

Deprecation Warnings for Django v3.0

Just saw this in my CI pipeline today. Thought I would let you all know:

/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py:995: RemovedInDjango30Warning: Remove the context parameter from InetAddressField.from_db_value(). Support for it will be removed in Django 3.0.
  RemovedInDjango30Warning,
/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py:995: RemovedInDjango30Warning: Remove the context parameter from InetAddressField.from_db_value(). Support for it will be removed in Django 3.0.
  RemovedInDjango30Warning,
/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py:995: RemovedInDjango30Warning: Remove the context parameter from InetAddressField.from_db_value(). Support for it will be removed in Django 3.0.
  RemovedInDjango30Warning,
/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py:995: RemovedInDjango30Warning: Remove the context parameter from InetAddressField.from_db_value(). Support for it will be removed in Django 3.0.
  RemovedInDjango30Warning,

Create a demo app

Create a demo app using contrib.admin to show how code works and test it in use.

I believe there's a typo on the front page / readme

On both the Git Repo page and PyPI page, the section that talks about the MACAddressField, I believe has a typo, as the given example references "CidrAddressField", and should probably be "MACAddressField". Excerpt follows:

MACAddressField will store values in PostgreSQL as type MACADDR. In Python, the value will be represented as a netaddr.EUI object. Note that the default text representation of EUI objects is not the same as that of the netaddr module. It is represented in a format that is more commonly used in network utilities and by network administrators (00:11:22:aa:bb:cc).

from netfields import CidrAddressField, NetManager

class Example(models.Model):
inet = CidrAddressField() #

loaddata failure with natural keys but successful without them

Hello,

On a Django 3.1.7 setup, I've got a models like this:
`class LANManager(models.Manager):
def get_by_natural_key(self, slug_name):
return self.get(slug_name=slug_name)

class LAN(models.Model):
slug_name = models.SlugField(max_length=64, unique=True)
network_address = CidrAddressField()
net_objects = NetManager()
objects = LANManager()

def __str__(self):
    return f'{self.slug_name}'

def natural_key(self):
    return (self.slug_name,)

class DHCPRangeManager(models.Manager):
def get_by_natural_key(self, ip_min, ip_max):
return self.get(ip_min=ip_min, ip_max=ip_max)

class DHCPRange(models.Model):
ip_min = InetAddressField(store_prefix_length=False)
ip_max = InetAddressField(store_prefix_length=False)
lan = models.ForeignKey(LAN, on_delete=models.PROTECT, related_name='dhcp_ranges')
net_objects = NetManager()
objects = DHCPRangeManager()

def __str__(self):
    return f'{str(self.ip_min)}-{str(self.ip_max)}'

def natural_key(self):
    return (self.ip_min, self.ip_max)`

I can positively run
python manage.py dumpdata --format=yaml app.lan app.dhcprange > app.fixtures.foo python manage.py loaddata foo python manage.py dumpdata --format=yaml --natural-primary --natural-foreign app.lan app.dhcprange > app.fixtures.foo2

But the next one fails:
python manage.py loaddata app.fixtures.foo2
with:
["โ€œ['toip_lan']โ€ value must be an integer."]

If I remove DHCPRange data (ie the with a foreign key ) from fixture file, it works.
Fixture file contents looked OK.

  1. Is netfield compliant with Django 3.1 ?
  2. Can I rewrite my models to work around this ?
  3. Suggestions ?

Best regards

netfields is incompatible with Django 1.9: ImportError on "from django.db.models.lookups import default_lookups"

From diff it seems that in Django 1.9, filling 'default_lookups' dictionary was replaced with Field.register_lookup(<lookup_class>).

Full trace:

Traceback (most recent call last):
File "/opt/ep/project/manage.py", line 10, in
execute_from_command_line(sys.argv)
File "/opt/ep/env/3/lib/python3.4/site-packages/django/core/management/init.py", line 350, in execute_from_command_line
utility.execute()
File "/opt/ep/env/3/lib/python3.4/site-packages/django/core/management/init.py", line 324, in execute
django.setup()
File "/opt/ep/env/3/lib/python3.4/site-packages/django/init.py", line 18, in setup
apps.populate(settings.INSTALLED_APPS)
File "/opt/ep/env/3/lib/python3.4/site-packages/django/apps/registry.py", line 85, in populate
app_config = AppConfig.create(entry)
File "/opt/ep/env/3/lib/python3.4/site-packages/django/apps/config.py", line 116, in create
mod = import_module(mod_path)
File "/opt/ep/env/3/lib/python3.4/importlib/init.py", line 109, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "", line 2254, in _gcd_import
File "", line 2237, in _find_and_load
File "", line 2226, in _find_and_load_unlocked
File "", line 1200, in _load_unlocked
File "", line 1129, in _exec
File "", line 1471, in exec_module
File "", line 321, in _call_with_frames_removed
File "/opt/ep/env/3/lib/python3.4/site-packages/netfields/apps.py", line 3, in
from django.db.models.lookups import default_lookups
ImportError: cannot import name 'default_lookups'

Missing tags for 0.7.x releases

It seems that there is no tag for 0.7.1 and 0.7.2 releases? Would you mind adding them so I can use them for a Debian package?

Consider int based storage for other DBs

Using inet_aton etc one can convert the ip addresses to their binary equivalent, this will allow for our extended look up semantics by using binary operators in the queries.

Update and replace PyPI package

Have been trying the lib with Django 1.6 (a sorely needed feature, text fields don't cut it for IP addresses!). The official version 0.2.1 in PyPI is from May 2013, and doesn't have the Django 1.6 support rolled in yet. Would it be possible to update the version number and update the PyPI package?

please switch from py2-ipaddress to ipaddress

Many Python distributions now have problems with pip's new strict security requirements and so end up following the steps here:

http://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning

Sadly, one of these pulls in this backport as a dependency:

https://pypi.python.org/pypi/ipaddress

Sadly, this is incompatible with the backport you are using:

https://pypi.python.org/pypi/py2-ipaddress

...but both distributions install an ipaddress.py, resulting in heisenbugs when the wrong package ends up being installed last.

I think some changes would be required, but it might make the world a bit of a less crazy place...

Bug in commit #29

Previous commit broke __in functionality when passing in a list with one value.

ie = Model.objects.filter(netfield__in=['127.0.0.1'])

Since the fix changes the list to a string if it only has one element, it passes the string onto django queryset lookups and breaks.

Can someone tell me why this fix was made in the first place. I can't figure out what it was trying to fix.

Unsupported lookup 'net_contains_or_equals' for CidrAddressField or join in the field not permitted

I have a Network model defined as so:

from netfields import CidrAddressField, NetManager

class Network(BaseModel):
    block = CidrAddressField()
    description = models.CharField(max_length=255, null=True, blank=True) 
    objects = NetManager()

When I call:

Network.objects.filter(block__net_contains_or_equals='10.142.0.0/22')

Django 1.7 throws:

FieldError: Unsupported lookup 'net_contains_or_equals' for CidrAddressField or join on the field not permitted.

I am using the latest commit from git.

'strict' parameter for network

By default, some network like 1.2.3.4/24 is not a valid in python ipaddress module unless strict=False is set.

>>> ipaddress.IPv4Network("1.2.3.4/24")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/virusdefender/.pyenv/versions/3.7.0/lib/python3.7/ipaddress.py", line 1536, in __init__
    raise ValueError('%s has host bits set' % self)
ValueError: 1.2.3.4/24 has host bits set

>>> ipaddress.IPv4Network("1.2.3.4/24", strict=False)
IPv4Network('1.2.3.0/24')

However it can be used in pg database.

So there may be some problems due to different validation logic

# cidr = InetAddressField(null=True)
>>> SomeModel.objects.filter(cidr__net_overlaps="1.2.3.4/24")
...
...

  File "/Users/virusdefender/Desktop/workspace/waf/venv/lib/python3.7/site-packages/netfields/lookups.py", line 65, in get_prep_lookup
    return str(ipaddress.ip_network(self.rhs))
  File "/Users/virusdefender/.pyenv/versions/3.7.0/lib/python3.7/ipaddress.py", line 74, in ip_network
    return IPv4Network(address, strict)
  File "/Users/virusdefender/.pyenv/versions/3.7.0/lib/python3.7/ipaddress.py", line 1536, in __init__
    raise ValueError('%s has host bits set' % self)
ValueError: 1.2.3.4/24 has host bits set

Request to support rhs lookup expressions

Would it be possible to support resolving RHS expressions in the custom lookups?

# query to find devices whose IP is not assigned to an interface
(
  Device.objects
  .prefetch_related('interface')
  .filter(
      ~Q(interface__interface_address__net_contains=F('ip_address'))
  )
  .values('name', 'ip_address')
)

They appear to pass through as a literal.

Col(networks_device, networks.Device.ip_address) does not appear to be an IPv4 or IPv6 interface.

Looking at the django source, they appear to have some boilerplate that may work.

    def get_prep_lookup(self):
        if hasattr(self.rhs, 'resolve_expression'):
            return self.rhs
        if self.prepare_rhs and hasattr(self.lhs.output_field, 'get_prep_value'):
            return self.lhs.output_field.get_prep_value(self.rhs)
        return self.rhs

If I get some time at the end of my sprint, I will see if I can work on this, but I wanted to at least get it documented.

Thank you.

How to use Django-Netfields with Django packages requiring a custom manager

Hello,

Some Django packages like Django-Polymorphic make use of custom managers.

As Django-Netfields also requires use of NetManager custom manager, one way to satisfy multiple requirements
is to have several managers and use appropriate one, depending on circumstances.

`from netfields import NetManager, InetAddressField
from Foo import FooManager, FooModel

class PersonManager(FooManager):
def get_by_natural_key(self, first_name, last_name):
return self.get(first_name=first_name, last_name=last_name)

class Person(FooModel):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
inet = InetAddressField()

objects = PersonManager()
objects_netfields = NetManager()

n1 = IPv4Network('192.168.1.0/24')
o1 = Person.objects.get(last_name='Doe')
o2 = Person.objects_netfields.get(inet__net_contained_or_equal=n1)`

Is there a way to avoid having several managers ?
I was thinking of something like bellow but any alternative would be welcome.

`from netfields import NetManager, InetAddressField, Whatever
from Foo import FooManager, FooModel

class PersonManager(FooManager, Whatever):
def get_by_natural_key(self, first_name, last_name):
return self.get(first_name=first_name, last_name=last_name)

class Person(FooModel):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
inet = InetAddressField()

objects = PersonManager()

n1 = IPv4Network('192.168.1.0/24')
o1 = Person.objects.get(last_name='Doe')
o2 = Person.objects.get(inet__net_contained_or_equal=n1)`

Thoughts ? Suggestions ?

Best regards

Can't search on CidrAddressField fields in the admin (Unsupported lookup 'icontains')

When trying to search on a CidrAddressField in the admin, it raises an exception:

FieldError at /admin/resources/ipsubnet/
Unsupported lookup 'icontains' for CidrAddressField or join on the field not permitted.

IPSubnet basically has a CidrAddressField and a few other fields:

class IPSubnet(models.Model):
    inet = CidrAddressField()
    objects = NetManager()

The admin looks like this:

class IPSubnetAdmin(admin.ModelAdmin):
    search_fields = ('inet__icontains',)

The full traceback is here: http://paste.aliens-lyon.fr/Fz4

The same exception happens for many values of search_fields (inet__contains, inet__iexact, inet__net_contains, etc). However, it works fine when searching just on inet, which seems to default to icontains.

Note that everything works fine in the Django shell:

>>> IPSubnet.objects.filter(inet__icontains='100')
[<IPSubnet: 2a00:5881:4000:100::/56>]
>>> IPSubnet.objects.filter(inet__iexact='2a00:5881:4000:100:0000:0000:0000:0000/56')
[<IPSubnet: 2a00:5881:4000:100::/56>]

I'm using Django 1.7 with Python 2.7.

Incorrect error in forms

If an invalid address is entered into a form, the error is improperly generated. For example, entering 'foobar' results in:

u'foobar' does not appear to be an IPv4 or IPv6 interface

The error should not show the 'u' prefix and the error message should make more sense to users.

'module' object has no attribute 'util'

Django==1.7.7
IPy==0.81
Twisted==11.1.0
amqp==1.4.6
anyjson==0.3.3
billiard==3.3.0.19
celery==3.1.17
django-celery==3.1.16
django-extensions==1.5.2
django-netfields==0.2.2
django-tagging==0.3.4
exabgp==3.4.9
ipython==3.0.0
kombu==3.0.24
netaddr==0.7.13
psycopg2==2.6
py-radix==0.7.0
pynfdump==0.3dev
python-dateutil==2.4.1
python-memcached==1.54
pytz==2015.2
redis==2.10.3
six==1.9.0
txAMQP==0.6.2
whisper==0.9.13
wsgiref==0.1.2
zope.interface==4.1.2

Pulling from master, I get the following error when the netfields are in my models.py class. When removed from the class, the admin page works as intended:

Environment:


Request Method: GET
Request URL: http://10.30.63.46:8081/admin/core/device/add/

Django Version: 1.7.7
Python Version: 2.7.8
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'core',
 'flow',
 'bgpd',
 'probe')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware')


Template error:
In template /opt/bgp-optimizer/lib/python2.7/site-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 19
   'module' object has no attribute 'util'
   9 :             {% for field in line %}


   10 :                 <div{% if not line.fields|length_is:'1' %} class="field-box{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}"{% elif field.is_checkbox %} class="checkbox-row"{% endif %}>


   11 :                     {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %}


   12 :                     {% if field.is_checkbox %}


   13 :                         {{ field.field }}{{ field.label_tag }}


   14 :                     {% else %}


   15 :                         {{ field.label_tag }}


   16 :                         {% if field.is_readonly %}


   17 :                             <p>{{ field.contents }}</p>


   18 :                         {% else %}


   19 :                              {{ field.field }} 


   20 :                         {% endif %}


   21 :                     {% endif %}


   22 :                     {% if field.field.help_text %}


   23 :                         <p class="help">{{ field.field.help_text|safe }}</p>


   24 :                     {% endif %}


   25 :                 </div>


   26 :             {% endfor %}


   27 :         </div>


   28 :     {% endfor %}


   29 : </fieldset>


Traceback:
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  137.                 response = response.render()
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/response.py" in render
  103.             self.content = self.rendered_content
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/response.py" in rendered_content
  80.         content = template.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  148.             return self._render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  126.         return compiled_parent._render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  126.         return compiled_parent._render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  65.                 result = block.nodelist.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  65.                 result = block.nodelist.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  201.                             nodelist.append(node.render(context))
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  150.                 return template.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  148.             return self._render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  201.                             nodelist.append(node.render(context))
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  201.                             nodelist.append(node.render(context))
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  312.                 return nodelist.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  312.                 return nodelist.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/template/debug.py" in render
  93.             output = force_text(output)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/utils/encoding.py" in force_text
  85.                 s = six.text_type(s)
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/forms/forms.py" in __str__
  508.         return self.as_widget()
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django/forms/forms.py" in as_widget
  560.         return force_text(widget.render(name, self.value(), attrs=attrs))
File "/opt/bgp-optimizer/lib/python2.7/site-packages/django_netfields-0.2.2-py2.7.egg/netfields/forms.py" in render
  21.         return mark_safe(u'<input%s />' % forms.util.flatatt(final_attrs))

Exception Type: AttributeError at /admin/core/device/add/
Exception Value: 'module' object has no attribute 'util'

Handle whitespace around input

Eg, ' 0.0.0.0/0' and '0.0.0.0/0 ' will not validate in the CIDRfield. Both area pretty easy sins to commit if you're cutting/pasting CIDRs.

I've been looking through the fields.py and forms.py modules and it looks like a few

if isinstance(value, str):
    value = value.strip()

should cover this. I haven't read enough of the surrounding code yet to know if that'll blow something else up. I'm happy to open a PR if this is a good approach.

extra lookups don`t work

Django 3.2.7
Python 3.9.5

class Network(models.Model):
    cidr = CidrAddressField(db_index=True, verbose_name=_('cidr'), help_text=_('network cidr'))
    objects = NetManager()


python3 manage.py shell

>>> from netw.models import Network
>>> Network.objects.filter(cidr__startswith='10.58')
<QuerySet [<Network: Loopback*, 10.58.0.1/32>, <Network: Loopback*, 10.58.0.2/32>, <Network: Loopback*, 10.58.0.3/32>, <Network: Managment*, 10.58.8.0/22>, <Network: SCUD*, 10.58.12.0/22>, <Network: Printers*, 10.58.16.0/22>, <Network: Phones*, 10.58.20.0/22>, <Network: VideoCam*, 10.58.24.0/22>, <Network: Terminals*, 10.58.28.0/22>, <Network: Workers*, 10.58.32.0/22>, <Network: Workers2*, 10.58.36.0/22>, <Network: Install*, 10.58.40.0/22>, <Network: OpenPoint*, 10.58.44.0/22>]>
>>> Network.objects.filter(cidr__net_contained='10.58.0.0/16')
Traceback (most recent call last):
  File "/usr/lib/python3.9/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<console>", line 1, in <module>
  File "/home/maxy/.local/lib/python3.9/site-packages/netfields/managers.py", line 19, in filter
    return super(NetManager, self).filter(*args, **kwargs)
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/query.py", line 941, in filter
    return self._filter_or_exclude(False, args, kwargs)
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/query.py", line 961, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/query.py", line 968, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1393, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1412, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1347, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1187, in build_lookup
    lhs = self.try_transform(lhs, lookup_name)
  File "/home/maxy/.local/lib/python3.9/site-packages/django/db/models/sql/query.py", line 1226, in try_transform
    raise FieldError(
django.core.exceptions.FieldError: Unsupported lookup 'net_contained' for CidrAddressField or join on the field not permitted, perhaps you meant contains or icontains?


Problem with ArrayField and ModelSerializer

There are some demo code

https://github.com/virusdefender/django-postgresql-netfields/commit/cf21f8fdd351f19ada3d10a3d574921055170367

I added three tests, but the last two tests will fail

https://travis-ci.org/virusdefender/django-postgresql-netfields/jobs/470819231

======================================================================
ERROR: test_serialize_inet_array (test.tests.test_rest_framework_fields.FieldsTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/virusdefender/django-postgresql-netfields/test/tests/test_rest_framework_fields.py", line 83, in test_serialize_inet_array
    self.assertEqual(TestSerializer(instance).data, {"field": ["1.2.3.0/24"]})
  File "/home/travis/build/virusdefender/django-postgresql-netfields/.tox/py36-django18/lib/python3.6/site-packages/rest_framework/serializers.py", line 534, in data
    ret = super(Serializer, self).data
  File "/home/travis/build/virusdefender/django-postgresql-netfields/.tox/py36-django18/lib/python3.6/site-packages/rest_framework/serializers.py", line 263, in data
    self._data = self.to_representation(self.instance)
  File "/home/travis/build/virusdefender/django-postgresql-netfields/.tox/py36-django18/lib/python3.6/site-packages/rest_framework/serializers.py", line 501, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/home/travis/build/virusdefender/django-postgresql-netfields/.tox/py36-django18/lib/python3.6/site-packages/rest_framework/fields.py", line 1574, in to_representation
    return [self.child.to_representation(item) if item is not None else None for item in data]
  File "/home/travis/build/virusdefender/django-postgresql-netfields/.tox/py36-django18/lib/python3.6/site-packages/rest_framework/fields.py", line 1574, in <listcomp>
    return [self.child.to_representation(item) if item is not None else None for item in data]
  File "/home/travis/build/virusdefender/django-postgresql-netfields/.tox/py36-django18/lib/python3.6/site-packages/rest_framework/fields.py", line 1791, in to_representation
    value = value_from_object(self.model_field, obj)
  File "/home/travis/build/virusdefender/django-postgresql-netfields/.tox/py36-django18/lib/python3.6/site-packages/rest_framework/compat.py", line 150, in value_from_object
    return field._get_val_from_obj(obj)
  File "/home/travis/build/virusdefender/django-postgresql-netfields/.tox/py36-django18/lib/python3.6/site-packages/django/db/models/fields/__init__.py", line 843, in _get_val_from_obj
    return getattr(obj, self.attname)
AttributeError: 'str' object has no attribute 'field'
======================================================================

Incompatible with Django 3

'ENGINE': 'django.db.backends.postgresql_psycopg2', is deprecated and has been removed in Django 3 making this no longer compatible with Django 3.

There's an open PR open to fix the issue:
#96

Unsupported lookup 'net_overlaps' for CidrAddressField

models.py:

class LAN(models.Model): network_address = CidrAddressField() objects = NetManager()

within shell_plus session:
`

from ipaddress import IPv4Network
LAN.objects.create(network_address=IPv4Network('192.168.2.0/24'))
net2 = IPv4Network('192.168.2.64/26')
LAN.objects.filter(network_address__net_overlaps=net2)
...
django.core.exceptions.FieldError: Unsupported lookup 'net_overlaps' for CidrAddressField or join on the field not permitted.
`
Django is 3.0.7 and django-netfields is 1.2.2.

Thoughts ?

Switch to ipaddr

ipaddr is slatted for stdlib inclusion, drop IPy in favor of this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.