From b70f1211ab4f5233ca7bd75719e9cc65491556f8 Mon Sep 17 00:00:00 2001 From: Johannes Erwerle Date: Sat, 4 Oct 2025 23:44:22 +0200 Subject: [PATCH 1/9] Fixed wrong link in plugin filtersets documentation --- docs/plugins/development/filtersets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/development/filtersets.md b/docs/plugins/development/filtersets.md index e19b3a733..a4b9e1375 100644 --- a/docs/plugins/development/filtersets.md +++ b/docs/plugins/development/filtersets.md @@ -1,6 +1,6 @@ # Filters & Filter Sets -Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI or REST API. (Note that the GraphQL API uses a separate filter class.) NetBox employs the [django-filters2](https://django-tables2.readthedocs.io/en/latest/) library to define filter sets. +Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI or REST API. (Note that the GraphQL API uses a separate filter class.) NetBox employs the [django-filter](https://django-filter.readthedocs.io/en/stable/) library to define filter sets. ## FilterSet Classes From f5ed095738b283418e12a349b0cdfe9dbb5c7742 Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Mon, 6 Oct 2025 09:12:27 -0500 Subject: [PATCH 2/9] Fixes: #21040 - Registered denormalized fields (#20503) --- netbox/circuits/apps.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/netbox/circuits/apps.py b/netbox/circuits/apps.py index 4d5f177e2..7b29a1509 100644 --- a/netbox/circuits/apps.py +++ b/netbox/circuits/apps.py @@ -1,5 +1,7 @@ from django.apps import AppConfig +from netbox import denormalized + class CircuitsConfig(AppConfig): name = "circuits" @@ -8,6 +10,16 @@ class CircuitsConfig(AppConfig): def ready(self): from netbox.models.features import register_models from . import signals, search # noqa: F401 + from .models import CircuitTermination # Register models register_models(*self.get_models()) + + denormalized.register(CircuitTermination, '_site', { + '_region': 'region', + '_site_group': 'group', + }) + + denormalized.register(CircuitTermination, '_location', { + '_site': 'site', + }) From 99e367cbaf7e857d9cee5fb01fdf47b70545f1f5 Mon Sep 17 00:00:00 2001 From: Martin Hauser Date: Fri, 3 Oct 2025 17:31:53 +0200 Subject: [PATCH 3/9] docs(api): Correct IntegerRangeSerializer schema definition Adjusts the schema mapping for `IntegerRangeSerializer` by setting `match_subclasses` to `True` and refining the array definition. Adds an example field for clarity in generated OpenAPI documentation. Fixes #20494 --- netbox/core/api/schema.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/netbox/core/api/schema.py b/netbox/core/api/schema.py index 0c59da5a1..c61675cf3 100644 --- a/netbox/core/api/schema.py +++ b/netbox/core/api/schema.py @@ -282,18 +282,18 @@ class FixSerializedPKRelatedField(OpenApiSerializerFieldExtension): class FixIntegerRangeSerializerSchema(OpenApiSerializerExtension): target_class = 'netbox.api.fields.IntegerRangeSerializer' + match_subclasses = True def map_serializer(self, auto_schema: 'AutoSchema', direction: Direction) -> _SchemaType: + # One range = two integers; many=True will wrap this in an outer array return { 'type': 'array', 'items': { - 'type': 'array', - 'items': { - 'type': 'integer', - }, - 'minItems': 2, - 'maxItems': 2, + 'type': 'integer', }, + 'minItems': 2, + 'maxItems': 2, + 'example': [10, 20], } From d18bbe48c144111c47f08eed19d8acadc8b59815 Mon Sep 17 00:00:00 2001 From: Dmitry Smirnov Date: Sun, 5 Oct 2025 14:16:04 +0300 Subject: [PATCH 4/9] add tag copy_content and id 'job_data_output' --- netbox/templates/extras/htmx/script_result.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/netbox/templates/extras/htmx/script_result.html b/netbox/templates/extras/htmx/script_result.html index ac5d87ef2..3844c7857 100644 --- a/netbox/templates/extras/htmx/script_result.html +++ b/netbox/templates/extras/htmx/script_result.html @@ -44,8 +44,8 @@
+ hx-trigger="load" hx-select=".htmx-container" hx-swap="outerHTML"> + {% endif %} @@ -60,11 +60,12 @@ {% trans "Download" %} + {% copy_content "job_data_output" %} {% endif %} {% if job.data.output %} -
{{ job.data.output }}
+
{{ job.data.output }}
{% else %}
{% trans "None" %}
{% endif %} From faa89a53ffeb75b692012d315488154ec7912abc Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 05:02:29 +0000 Subject: [PATCH 5/9] Update source translation strings --- netbox/translations/en/LC_MESSAGES/django.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/translations/en/LC_MESSAGES/django.po b/netbox/translations/en/LC_MESSAGES/django.po index 265540c60..563110ae4 100644 --- a/netbox/translations/en/LC_MESSAGES/django.po +++ b/netbox/translations/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-10-02 05:01+0000\n" +"POT-Creation-Date: 2025-10-07 05:02+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -12755,7 +12755,7 @@ msgstr "" #: netbox/templates/extras/configtemplate.html:77 #: netbox/templates/extras/eventrule.html:66 #: netbox/templates/extras/exporttemplate.html:60 -#: netbox/templates/extras/htmx/script_result.html:69 +#: netbox/templates/extras/htmx/script_result.html:70 #: netbox/templates/extras/webhook.html:65 #: netbox/templates/extras/webhook.html:75 #: netbox/templates/inc/panel_table.html:13 From 05e26b82c177a42343a5fee64b0b91f9329c0e31 Mon Sep 17 00:00:00 2001 From: Matthew Papaleo Date: Tue, 7 Oct 2025 13:39:51 +1100 Subject: [PATCH 6/9] Fixes #20507 Contacts returned for ASN via graphql API --- netbox/ipam/graphql/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index e8f98eabe..a07316fe4 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -74,7 +74,7 @@ class BaseIPAddressFamilyType: filters=ASNFilter, pagination=True ) -class ASNType(NetBoxObjectType): +class ASNType(NetBoxObjectType, ContactsMixin): asn: BigInt rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None From d5e8480367ec3335a6328f97cbc454e7bb726687 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 7 Oct 2025 11:22:24 -0400 Subject: [PATCH 7/9] Update OpenAPI schema (#20519) --- contrib/openapi.json | 45 ++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/contrib/openapi.json b/contrib/openapi.json index 94cf6d659..33b5092af 100644 --- a/contrib/openapi.json +++ b/contrib/openapi.json @@ -214760,7 +214760,7 @@ }, "mark_utilized": { "type": "boolean", - "description": "Report space as 100% utilized" + "description": "Report space as fully utilized" } }, "required": [ @@ -214869,7 +214869,7 @@ }, "mark_utilized": { "type": "boolean", - "description": "Report space as 100% utilized" + "description": "Report space as fully utilized" } }, "required": [ @@ -215569,24 +215569,26 @@ "IntegerRange": { "type": "array", "items": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 2, - "maxItems": 2 - } + "type": "integer" + }, + "minItems": 2, + "maxItems": 2, + "example": [ + 10, + 20 + ] }, "IntegerRangeRequest": { "type": "array", "items": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 2, - "maxItems": 2 - } + "type": "integer" + }, + "minItems": 2, + "maxItems": 2, + "example": [ + 10, + 20 + ] }, "Interface": { "type": "object", @@ -228986,7 +228988,6 @@ }, "key": { "type": "string", - "writeOnly": true, "maxLength": 40, "minLength": 40 }, @@ -231880,7 +231881,7 @@ }, "mark_utilized": { "type": "boolean", - "description": "Report space as 100% utilized" + "description": "Report space as fully utilized" } } }, @@ -245221,6 +245222,11 @@ "format": "date-time", "nullable": true }, + "key": { + "type": "string", + "maxLength": 40, + "minLength": 40 + }, "write_enabled": { "type": "boolean", "description": "Permit create/update/delete operations using this key" @@ -245367,7 +245373,6 @@ }, "key": { "type": "string", - "writeOnly": true, "maxLength": 40, "minLength": 40 }, @@ -252203,7 +252208,7 @@ }, "mark_utilized": { "type": "boolean", - "description": "Report space as 100% utilized" + "description": "Report space as fully utilized" } }, "required": [ From 51528ae4296c8d119c7c45408ab0593b6311b306 Mon Sep 17 00:00:00 2001 From: Martin Hauser Date: Tue, 7 Oct 2025 17:47:01 +0200 Subject: [PATCH 8/9] fix(utilities): Enhance ranges_to_string for improved clarity (#20479) --- netbox/utilities/data.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/netbox/utilities/data.py b/netbox/utilities/data.py index 7b50d26b8..4dbbf38cf 100644 --- a/netbox/utilities/data.py +++ b/netbox/utilities/data.py @@ -137,8 +137,17 @@ def check_ranges_overlap(ranges): def ranges_to_string(ranges): """ - Generate a human-friendly string from a set of ranges. Intended for use with ArrayField. For example: - [[1, 100)], [200, 300)] => "1-99,200-299" + Converts a list of ranges into a string representation. + + This function takes a list of range objects and produces a string + representation of those ranges. Each range is represented as a + hyphen-separated pair of lower and upper bounds, with inclusive or + exclusive bounds adjusted accordingly. If the lower and upper bounds + of a range are the same, only the single value is added to the string. + Intended for use with ArrayField. + + Example: + [NumericRange(1, 5), NumericRange(8, 9), NumericRange(10, 12)] => "1-5,8,10-12" """ if not ranges: return '' @@ -146,7 +155,7 @@ def ranges_to_string(ranges): for r in ranges: lower = r.lower if r.lower_inc else r.lower + 1 upper = r.upper if r.upper_inc else r.upper - 1 - output.append(f'{lower}-{upper}') + output.append(f"{lower}-{upper}" if lower != upper else str(lower)) return ','.join(output) From b7cae045725e8316972c58493d8e6f4517c7a95f Mon Sep 17 00:00:00 2001 From: Martin Hauser Date: Tue, 7 Oct 2025 18:01:29 +0200 Subject: [PATCH 9/9] fix(api): Update NumericRange handling to use half-open intervals (#20478) --- netbox/netbox/api/fields.py | 2 +- netbox/utilities/data.py | 15 +++++++++++---- netbox/utilities/tests/test_data.py | 12 ++++++------ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/netbox/netbox/api/fields.py b/netbox/netbox/api/fields.py index db5ec184d..7dfd7d7eb 100644 --- a/netbox/netbox/api/fields.py +++ b/netbox/netbox/api/fields.py @@ -169,7 +169,7 @@ class IntegerRangeSerializer(serializers.Serializer): if type(data[0]) is not int or type(data[1]) is not int: raise ValidationError(_("Range boundaries must be defined as integers.")) - return NumericRange(data[0], data[1], bounds='[]') + return NumericRange(data[0], data[1] + 1, bounds='[)') def to_representation(self, instance): return instance.lower, instance.upper - 1 diff --git a/netbox/utilities/data.py b/netbox/utilities/data.py index 4dbbf38cf..1e7746197 100644 --- a/netbox/utilities/data.py +++ b/netbox/utilities/data.py @@ -161,9 +161,16 @@ def ranges_to_string(ranges): def string_to_ranges(value): """ - Given a string in the format "1-100, 200-300" return an list of NumericRanges. Intended for use with ArrayField. - For example: - "1-99,200-299" => [NumericRange(1, 100), NumericRange(200, 300)] + Converts a string representation of numeric ranges into a list of NumericRange objects. + + This function parses a string containing numeric values and ranges separated by commas (e.g., + "1-5,8,10-12") and converts it into a list of NumericRange objects. + In the case of a single integer, it is treated as a range where the start and end + are equal. The returned ranges are represented as half-open intervals [lower, upper). + Intended for use with ArrayField. + + Example: + "1-5,8,10-12" => [NumericRange(1, 6), NumericRange(8, 9), NumericRange(10, 13)] """ if not value: return None @@ -181,5 +188,5 @@ def string_to_ranges(value): upper = dash_range[1] else: return None - values.append(NumericRange(int(lower), int(upper), bounds='[]')) + values.append(NumericRange(int(lower), int(upper) + 1, bounds='[)')) return values diff --git a/netbox/utilities/tests/test_data.py b/netbox/utilities/tests/test_data.py index 7b313baf7..5d211c7bd 100644 --- a/netbox/utilities/tests/test_data.py +++ b/netbox/utilities/tests/test_data.py @@ -61,18 +61,18 @@ class RangeFunctionsTestCase(TestCase): self.assertEqual( string_to_ranges('10-19, 30-39, 100-199'), [ - NumericRange(10, 19, bounds='[]'), # 10-19 - NumericRange(30, 39, bounds='[]'), # 30-39 - NumericRange(100, 199, bounds='[]'), # 100-199 + NumericRange(10, 20, bounds='[)'), # 10-20 + NumericRange(30, 40, bounds='[)'), # 30-40 + NumericRange(100, 200, bounds='[)'), # 100-200 ] ) self.assertEqual( string_to_ranges('1-2, 5, 10-12'), [ - NumericRange(1, 2, bounds='[]'), # 1-2 - NumericRange(5, 5, bounds='[]'), # 5-5 - NumericRange(10, 12, bounds='[]'), # 10-12 + NumericRange(1, 3, bounds='[)'), # 1-3 + NumericRange(5, 6, bounds='[)'), # 5-6 + NumericRange(10, 13, bounds='[)'), # 10-13 ] )