jimfunk / django-postgresql-netfields

Proper INET and CIDR fields for Django running on PostgreSQL
BSD 3-Clause "New" or "Revised" License
152 stars 65 forks source link

Invalid query when combining with ArrayField and NULL values #128

Open sevdog opened 7 months ago

sevdog commented 7 months ago

When combining a field from this library with ArrayField if any of the values in the array is None (or should be translated to NULL) an invalid query is generated causing an error like the following:

django.db.utils.DataError: invalid input syntax for type ...

In example, if using InetAddressField the generated SQL is something like

SELECT '{192.168.1.1,None}'::inet[]::inet[];

Using django built-in GenericIPAddressField instead produces the right query:

SELECT '{192.168.1.1,NULL}'::inet[]::inet[];
Here is a sample model with tests to ensure this behaviour: ```python # models.py from django.contrib.postgres.fields import ArrayField from django.db import models from netfields import (CidrAddressField, InetAddressField, MACAddress8Field, MACAddressField) class ArrayModel(models.Model): ip_array = ArrayField(models.GenericIPAddressField(), null=True) inet_array = ArrayField(InetAddressField(), null=True) mac_array = ArrayField(MACAddressField(), null=True) mac8_array = ArrayField(MACAddress8Field(), null=True) cidr_array = ArrayField(CidrAddressField(), null=True) # tests.py from django.test import TestCase from .models import ArrayModel class NullsTestCase(TestCase): def test_ip(self): model = ArrayModel(ip_array=["192.168.1.1", None]) model.save() def test_inet(self): model = ArrayModel(inet_array=["192.168.1.1", None]) model.save() def test_mac(self): model = ArrayModel(mac_array=["FF:FF:FF:FF:FF:FF", None]) model.save() def test_mac8(self): model = ArrayModel(mac8_array=["FF:FF:FF:FF:FF:FF", None]) model.save() def test_cidr(self): model = ArrayModel(cidr_array=["192.168.1.0/24", None]) model.save() ```

I have seen this issue using Django 4.2 and psycopg 3.1.17.

sevdog commented 7 months ago

Looking into django code I see that GenericIPAddressField.get_db_prep_value internally calls connection.ops.adapt_ipaddressfield_value which behaves different from netfields:

https://github.com/django/django/blob/c7e986fc9f4848bd757d4b9b70a40586d2cee9fb/django/db/backends/postgresql/operations.py#L350-L353

Because if the value is falsy it just returns None and not Inet(None) (which is the result in netfields).