Compare commits

..

4 Commits

Author SHA1 Message Date
Jeremy Stretch
50d1d0a023 Fixes #21518: Ensure proper display of decimal custom fields with a zero value 2026-02-25 18:08:08 -05:00
Dave Bevan
74326edc20 Add new Ethernet types for 10GE and 40GE
Closes #21394
2026-02-25 16:34:00 -05:00
Grische
2ef21f7097 Fixes: #21456 - Improve config_context rendering with GraphQL (#21495) 2026-02-25 16:17:04 -05:00
Kartik
3adcdc34c3 clarify E501 enforcement 2026-02-25 15:33:25 -05:00
5 changed files with 21 additions and 27 deletions

View File

@@ -34,7 +34,8 @@ The following rules are ignored when linting.
##### [E501](https://docs.astral.sh/ruff/rules/line-too-long/): Line too long
NetBox does not enforce a hard restriction on line length, although a maximum length of 120 characters is strongly encouraged for Python code where possible. The maximum length does not apply to HTML templates or to automatically generated code (e.g. database migrations).
NetBox enforces a maximum line length of 120 characters for Python code using Ruff (E501).
The maximum length does not apply to HTML templates or to automatically generated code (e.g. database migrations).
##### [F403](https://docs.astral.sh/ruff/rules/undefined-local-with-import-star/): Undefined local with import star

View File

@@ -2,7 +2,6 @@ import re
import typing
from collections import OrderedDict
from drf_spectacular.contrib.django_filters import DjangoFilterExtension
from drf_spectacular.extensions import OpenApiSerializerExtension, OpenApiSerializerFieldExtension, _SchemaType
from drf_spectacular.openapi import AutoSchema
from drf_spectacular.plumbing import (
@@ -10,7 +9,6 @@ from drf_spectacular.plumbing import (
build_choice_field,
build_media_type_object,
build_object_type,
follow_field_source,
get_doc,
)
from drf_spectacular.types import OpenApiTypes
@@ -25,29 +23,6 @@ BULK_ACTIONS = ("bulk_destroy", "bulk_partial_update", "bulk_update")
WRITABLE_ACTIONS = ("PATCH", "POST", "PUT")
class NetBoxDjangoFilterExtension(DjangoFilterExtension):
"""
Overrides drf-spectacular's DjangoFilterExtension to fix a regression in v0.29.0 where
_get_model_field() incorrectly double-appends to_field_name when field_name already ends
with that value (e.g. field_name='tags__slug', to_field_name='slug' produces the invalid
path ['tags', 'slug', 'slug']). This caused hundreds of spurious warnings during schema
generation for filters such as TagFilter, TenancyFilterSet.tenant, and OwnerFilterMixin.owner.
See: https://github.com/netbox-community/netbox/issues/20787
https://github.com/tfranzel/drf-spectacular/issues/1475
"""
priority = 1
def _get_model_field(self, filter_field, model):
if not filter_field.field_name:
return None
path = filter_field.field_name.split('__')
to_field_name = filter_field.extra.get('to_field_name')
if to_field_name is not None and path[-1] != to_field_name:
path.append(to_field_name)
return follow_field_source(model, path, emit_warnings=False)
class FixTimeZoneSerializerField(OpenApiSerializerFieldExtension):
target_class = 'timezone_field.rest_framework.TimeZoneSerializerField'

View File

@@ -921,6 +921,7 @@ class InterfaceTypeChoices(ChoiceSet):
# 10 Gbps Ethernet
TYPE_10GE_BR_D = '10gbase-br-d'
TYPE_10GE_BR_U = '10gbase-br-u'
TYPE_10GE_CU = '10gbase-cu'
TYPE_10GE_CX4 = '10gbase-cx4'
TYPE_10GE_ER = '10gbase-er'
TYPE_10GE_LR = '10gbase-lr'
@@ -943,6 +944,7 @@ class InterfaceTypeChoices(ChoiceSet):
TYPE_40GE_FR4 = '40gbase-fr4'
TYPE_40GE_LR4 = '40gbase-lr4'
TYPE_40GE_SR4 = '40gbase-sr4'
TYPE_40GE_SR4_BD = '40gbase-sr4-bd'
# 50 Gbps Ethernet
TYPE_50GE_CR = '50gbase-cr'
@@ -1192,6 +1194,7 @@ class InterfaceTypeChoices(ChoiceSet):
(
(TYPE_10GE_BR_D, '10GBASE-BR-D (10GE BiDi Down)'),
(TYPE_10GE_BR_U, '10GBASE-BR-U (10GE BiDi Up)'),
(TYPE_10GE_CU, '10GBASE-CU (10GE DAC Passive Twinax)'),
(TYPE_10GE_CX4, '10GBASE-CX4 (10GE DAC)'),
(TYPE_10GE_ER, '10GBASE-ER (10GE)'),
(TYPE_10GE_LR, '10GBASE-LR (10GE)'),
@@ -1220,6 +1223,7 @@ class InterfaceTypeChoices(ChoiceSet):
(TYPE_40GE_FR4, '40GBASE-FR4 (40GE)'),
(TYPE_40GE_LR4, '40GBASE-LR4 (40GE)'),
(TYPE_40GE_SR4, '40GBASE-SR4 (40GE)'),
(TYPE_40GE_SR4_BD, '40GBASE-SR4 (40GE BiDi)'),
)
),
(

View File

@@ -22,7 +22,19 @@ if TYPE_CHECKING:
@strawberry.type
class ConfigContextMixin:
@strawberry_django.field
@classmethod
def get_queryset(cls, queryset, info: Info, **kwargs):
queryset = super().get_queryset(queryset, info, **kwargs)
# If `config_context` is requested, call annotate_config_context_data() on the queryset
selected = {f.name for f in info.selected_fields[0].selections}
if 'config_context' in selected and hasattr(queryset, 'annotate_config_context_data'):
return queryset.annotate_config_context_data()
return queryset
# Ensure `local_context_data` is fetched when `config_context` is requested
@strawberry_django.field(only=['local_context_data'])
def config_context(self) -> strawberry.scalars.JSON:
return self.get_config_context()

View File

@@ -2,6 +2,8 @@
{% load i18n %}
{% if customfield.type == 'integer' and value is not None %}
{{ value }}
{% elif customfield.type == 'decimal' and value is not None %}
{{ value }}
{% elif customfield.type == 'longtext' and value %}
{{ value|markdown }}
{% elif customfield.type == 'boolean' and value == True %}