django-postgresql-netfields
django-postgresql-netfields copied to clipboard
Invalid query when combining with ArrayField and NULL values
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:
# 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.
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).