-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
NetBox Edition
NetBox Community
NetBox Version
v4.4.2
Python Version
3.12
Steps to Reproduce
- Define a model with an array of integer ranges:
from django.db import models from django.contrib.postgres.fields import ArrayField, IntegerRangeField class DemoModel(models.Model): name = models.CharField(max_length=50) port_ranges = ArrayField(base_field=IntegerRangeField(), blank=True, default=list)
- Expose it in the API using the existing range serializer:
from netbox.api.serializers import NetBoxModelSerializer from netbox.api.fields import IntegerRangeSerializer class DemoSerializer(NetBoxModelSerializer): # Inclusive [start, end] pairs; list handled by many=True port_ranges = IntegerRangeSerializer(many=True, required=False) class Meta: model = DemoModel fields = ("id", "name", "port_ranges")
- Write an
APIViewTestCasethat creates/updates using inclusive pairs:from netbox.utilities.testing import APIViewTestCases class DemoAPIViewTestCase(APIViewTestCases.APIViewTestCase): model = DemoModel @classmethod def setUpTestData(cls): cls.create_data = [ {"name": "demo1", "port_ranges": [[22, 22], [443, 443]]} ]
- Run the tests.
Expected Behavior
When api=True, model_to_dict() should normalize ArrayField(IntegerRangeField) values to inclusive pairs [[lo, hi], ...], matching:
- the API serializer representation (
IntegerRangeSerializer) - the established
VLANGroup.vid_rangesrepresentation.
Observed Behavior
utilities.testing.api::test_update_object → assertInstanceEqual compares request data to model_to_dict(instance, api=True). For ArrayField(IntegerRangeField), the model dict includes psycopg ranges:
AssertionError:
- 'port_ranges': [Range(22, 23, '[)'), Range(443, 444, '[)')],
+ 'port_ranges': [[22, 22], [443, 443]],
Root Cause
PostgreSQL canonicalizes discrete integer ranges to half‑open [lo, hi). The test harness directly compares these psycopg Range objects against the inclusive JSON pairs submitted by the test case.
Proposed Fix
Normalize arrays of numeric ranges within the api=True branch of utilities.testing.base:model_to_dict():
# utilities/testing/base.py (inside model_to_dict(), within the `api` branch)
elif type(field) is ArrayField and issubclass(type(field.base_field), RangeField):
# Convert half-open [lo, hi) to inclusive [lo, hi-1]
model_dict[key] = [[r.lower, r.upper - 1] for r in value]This mirrors the non‑API branch (which already special‑cases range arrays for forms/CSV) and aligns the generic API tests with the inclusive JSON representation used by NetBox serializers.
Minimal End-to-End Example
- Model:
ArrayField(IntegerRangeField)as above. - Serializer:
IntegerRangeSerializer(many=True, required=False). - Test payload:
{"name": "demo1", "port_ranges": [[22, 22], [443, 443]]}. - Before fix: Fails:
Range(22, 23, '[)')vs[[22, 22]]. - After fix: Passes: both sides compare as
[[22, 22], [443, 443]].
Notes
- This change affects only the test utility normalization for
api=True. It does not alter database storage, API behavior, or public schemas. - It brings the generic test harness in line with the inclusive‑pair API contract already used elsewhere (e.g., VLAN ID ranges).