From 7e1d558428a389f13958cb51b83d3205febadcf1 Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Wed, 14 May 2025 09:36:35 -0500 Subject: [PATCH 1/8] Fixes #19415: Increased Circuit/WirelessLink absolute distance upper limit Also adds form validation that provides a useful message to the user rather than a 500 error with potentially little information. --- netbox/circuits/forms/model_forms.py | 3 ++- netbox/netbox/models/mixins.py | 4 ++-- netbox/utilities/forms/mixins.py | 34 ++++++++++++++++++++++++++++ netbox/wireless/forms/model_forms.py | 3 ++- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/netbox/circuits/forms/model_forms.py b/netbox/circuits/forms/model_forms.py index 6f8ab783d92..ce09862ae2a 100644 --- a/netbox/circuits/forms/model_forms.py +++ b/netbox/circuits/forms/model_forms.py @@ -16,6 +16,7 @@ from utilities.forms.fields import ( CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, ) +from utilities.forms.mixins import DistanceValidationMixin from utilities.forms.rendering import FieldSet, InlineFields from utilities.forms.widgets import DatePicker, HTMXSelect, NumberWithOptions from utilities.templatetags.builtins.filters import bettertitle @@ -105,7 +106,7 @@ class Meta: ] -class CircuitForm(TenancyForm, NetBoxModelForm): +class CircuitForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm): provider = DynamicModelChoiceField( label=_('Provider'), queryset=Provider.objects.all(), diff --git a/netbox/netbox/models/mixins.py b/netbox/netbox/models/mixins.py index dc706c7c24a..9f2c3ca6dc6 100644 --- a/netbox/netbox/models/mixins.py +++ b/netbox/netbox/models/mixins.py @@ -55,7 +55,7 @@ def clean(self): class DistanceMixin(models.Model): distance = models.DecimalField( verbose_name=_('distance'), - max_digits=8, + max_digits=16, decimal_places=2, blank=True, null=True @@ -69,7 +69,7 @@ class DistanceMixin(models.Model): ) # Stores the normalized distance (in meters) for database ordering _abs_distance = models.DecimalField( - max_digits=10, + max_digits=18, decimal_places=4, blank=True, null=True diff --git a/netbox/utilities/forms/mixins.py b/netbox/utilities/forms/mixins.py index e89fbb520a4..ea16002a08c 100644 --- a/netbox/utilities/forms/mixins.py +++ b/netbox/utilities/forms/mixins.py @@ -3,6 +3,8 @@ from django import forms from django.utils.translation import gettext_lazy as _ +from utilities.conversion import to_meters + __all__ = ( 'CheckLastUpdatedMixin', ) @@ -44,3 +46,35 @@ def clean(self): "This object has been modified since the form was rendered. Please consult the object's change " "log for details." )) + + +class DistanceValidationMixin(forms.Form): + def clean(self): + super().clean() + + # validate max distance in meters based on model + # breakpoint() + distance = self.cleaned_data.get('distance', None) + unit = self.cleaned_data.get('distance_unit', None) + if distance and unit: + model_class = self._meta.model + distance_field = model_class._meta.get_field('distance') + max_digits = distance_field.max_digits - distance_field.decimal_places + max_distance = 10 ** max_digits + + abs_distance = to_meters(distance, unit) + + if abs_distance > max_distance: + raise forms.ValidationError(_( + "{distance} {unit} ({abs_distance} m) exceeds the maximum allowed distance for " + "{model._meta.verbose_name} distance. Distance must normalize to no more than " + "{max_distance} meters.".format( + distance=distance, + unit=unit, + abs_distance=abs_distance, + model=model_class, + max_distance=max_distance + ) + )) + + self.cleaned_data['_abs_distance'] = abs_distance diff --git a/netbox/wireless/forms/model_forms.py b/netbox/wireless/forms/model_forms.py index 56422ab5775..08f418e3c75 100644 --- a/netbox/wireless/forms/model_forms.py +++ b/netbox/wireless/forms/model_forms.py @@ -7,6 +7,7 @@ from netbox.forms import NetBoxModelForm from tenancy.forms import TenancyForm from utilities.forms.fields import CommentField, DynamicModelChoiceField, SlugField +from utilities.forms.mixins import DistanceValidationMixin from utilities.forms.rendering import FieldSet, InlineFields from wireless.models import * @@ -73,7 +74,7 @@ class Meta: } -class WirelessLinkForm(TenancyForm, NetBoxModelForm): +class WirelessLinkForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm): site_a = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, From 82c574cc5170472755b19dc11eabde280d4ef22b Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Wed, 14 May 2025 10:25:46 -0500 Subject: [PATCH 2/8] Include forgotten migration files --- ...it__abs_distance_alter_circuit_distance.py | 23 +++++++++++++++++++ ...ter_wirelesslink__abs_distance_and_more.py | 23 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py create mode 100644 netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py diff --git a/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py b/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py new file mode 100644 index 00000000000..bc3d1a6785d --- /dev/null +++ b/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2 on 2025-05-13 17:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('circuits', '0051_virtualcircuit_group_assignment'), + ] + + operations = [ + migrations.AlterField( + model_name='circuit', + name='_abs_distance', + field=models.DecimalField(blank=True, decimal_places=4, max_digits=18, null=True), + ), + migrations.AlterField( + model_name='circuit', + name='distance', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=16, null=True), + ), + ] diff --git a/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py b/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py new file mode 100644 index 00000000000..f25471bec49 --- /dev/null +++ b/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2 on 2025-05-13 17:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wireless', '0014_wirelesslangroup_comments'), + ] + + operations = [ + migrations.AlterField( + model_name='wirelesslink', + name='_abs_distance', + field=models.DecimalField(blank=True, decimal_places=4, max_digits=18, null=True), + ), + migrations.AlterField( + model_name='wirelesslink', + name='distance', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=16, null=True), + ), + ] From 4f0874cc3a5d78e9ec0a38161782f6874b440c3a Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Wed, 14 May 2025 10:32:46 -0500 Subject: [PATCH 3/8] Remove unnecessary comments --- netbox/utilities/forms/mixins.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/netbox/utilities/forms/mixins.py b/netbox/utilities/forms/mixins.py index ea16002a08c..ab90fbe1654 100644 --- a/netbox/utilities/forms/mixins.py +++ b/netbox/utilities/forms/mixins.py @@ -52,8 +52,6 @@ class DistanceValidationMixin(forms.Form): def clean(self): super().clean() - # validate max distance in meters based on model - # breakpoint() distance = self.cleaned_data.get('distance', None) unit = self.cleaned_data.get('distance_unit', None) if distance and unit: From 18b7a3187a9d8a501531bdbd4ade26217357c566 Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Wed, 14 May 2025 10:33:48 -0500 Subject: [PATCH 4/8] Remove more unnecessary comments --- .../0052_alter_circuit__abs_distance_alter_circuit_distance.py | 2 -- .../0015_alter_wirelesslink__abs_distance_and_more.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py b/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py index bc3d1a6785d..f5e3c3337a9 100644 --- a/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py +++ b/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py @@ -1,5 +1,3 @@ -# Generated by Django 5.2 on 2025-05-13 17:04 - from django.db import migrations, models diff --git a/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py b/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py index f25471bec49..a4709df81fc 100644 --- a/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py +++ b/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py @@ -1,5 +1,3 @@ -# Generated by Django 5.2 on 2025-05-13 17:04 - from django.db import migrations, models From b4e15b6730b4b9d316ac6b22a9ad6951e9af5fab Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Fri, 16 May 2025 10:02:38 -0500 Subject: [PATCH 5/8] Addresses PR feedback --- ...xtend_circuit_abs_distance_upper_limit.py} | 9 ++--- netbox/netbox/models/mixins.py | 7 ++-- netbox/utilities/forms/mixins.py | 38 +++++-------------- .../form_helpers/render_fieldset.html | 11 ++++-- netbox/utilities/templatetags/form_helpers.py | 7 ++-- ...wireless_link_abs_distance_upper_limit.py} | 9 ++--- 6 files changed, 31 insertions(+), 50 deletions(-) rename netbox/circuits/migrations/{0052_alter_circuit__abs_distance_alter_circuit_distance.py => 0052_extend_circuit_abs_distance_upper_limit.py} (61%) rename netbox/wireless/migrations/{0015_alter_wirelesslink__abs_distance_and_more.py => 0015_extend_wireless_link_abs_distance_upper_limit.py} (61%) diff --git a/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py b/netbox/circuits/migrations/0052_extend_circuit_abs_distance_upper_limit.py similarity index 61% rename from netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py rename to netbox/circuits/migrations/0052_extend_circuit_abs_distance_upper_limit.py index f5e3c3337a9..eba606b0334 100644 --- a/netbox/circuits/migrations/0052_alter_circuit__abs_distance_alter_circuit_distance.py +++ b/netbox/circuits/migrations/0052_extend_circuit_abs_distance_upper_limit.py @@ -1,3 +1,5 @@ +# Generated by Django 5.2.1 on 2025-05-16 14:42 + from django.db import migrations, models @@ -11,11 +13,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='circuit', name='_abs_distance', - field=models.DecimalField(blank=True, decimal_places=4, max_digits=18, null=True), - ), - migrations.AlterField( - model_name='circuit', - name='distance', - field=models.DecimalField(blank=True, decimal_places=2, max_digits=16, null=True), + field=models.DecimalField(blank=True, decimal_places=4, max_digits=13, null=True), ), ] diff --git a/netbox/netbox/models/mixins.py b/netbox/netbox/models/mixins.py index 9f2c3ca6dc6..46a85b126bb 100644 --- a/netbox/netbox/models/mixins.py +++ b/netbox/netbox/models/mixins.py @@ -1,3 +1,4 @@ + from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ @@ -55,10 +56,10 @@ def clean(self): class DistanceMixin(models.Model): distance = models.DecimalField( verbose_name=_('distance'), - max_digits=16, + max_digits=8, decimal_places=2, blank=True, - null=True + null=True, ) distance_unit = models.CharField( verbose_name=_('distance unit'), @@ -69,7 +70,7 @@ class DistanceMixin(models.Model): ) # Stores the normalized distance (in meters) for database ordering _abs_distance = models.DecimalField( - max_digits=18, + max_digits=13, decimal_places=4, blank=True, null=True diff --git a/netbox/utilities/forms/mixins.py b/netbox/utilities/forms/mixins.py index ab90fbe1654..05868106092 100644 --- a/netbox/utilities/forms/mixins.py +++ b/netbox/utilities/forms/mixins.py @@ -1,10 +1,10 @@ import time +from decimal import Decimal from django import forms +from django.core.validators import MaxValueValidator, MinValueValidator from django.utils.translation import gettext_lazy as _ -from utilities.conversion import to_meters - __all__ = ( 'CheckLastUpdatedMixin', ) @@ -49,30 +49,10 @@ def clean(self): class DistanceValidationMixin(forms.Form): - def clean(self): - super().clean() - - distance = self.cleaned_data.get('distance', None) - unit = self.cleaned_data.get('distance_unit', None) - if distance and unit: - model_class = self._meta.model - distance_field = model_class._meta.get_field('distance') - max_digits = distance_field.max_digits - distance_field.decimal_places - max_distance = 10 ** max_digits - - abs_distance = to_meters(distance, unit) - - if abs_distance > max_distance: - raise forms.ValidationError(_( - "{distance} {unit} ({abs_distance} m) exceeds the maximum allowed distance for " - "{model._meta.verbose_name} distance. Distance must normalize to no more than " - "{max_distance} meters.".format( - distance=distance, - unit=unit, - abs_distance=abs_distance, - model=model_class, - max_distance=max_distance - ) - )) - - self.cleaned_data['_abs_distance'] = abs_distance + distance = forms.DecimalField( + required=False, + validators=[ + MinValueValidator(Decimal(0)), + MaxValueValidator(Decimal(100000)), + ] + ) diff --git a/netbox/utilities/templates/form_helpers/render_fieldset.html b/netbox/utilities/templates/form_helpers/render_fieldset.html index ae8252b9757..0faf23c9249 100644 --- a/netbox/utilities/templates/form_helpers/render_fieldset.html +++ b/netbox/utilities/templates/form_helpers/render_fieldset.html @@ -6,7 +6,7 @@

{{ heading }}

{% endif %} - {% for layout, title, items in rows %} + {% for layout, title, items, has_errors in rows %} {% if layout == 'field' %} {# Single form field #} @@ -25,12 +25,17 @@

{{ heading }}

{% elif layout == 'inline' %} {# Multiple form fields on the same line #} -
+
{% for field in items %} -
+
{{ field }}
{% trans field.label %}
+ {% if field.errors %} +
+ {% for error in field.errors %}{{ error }}{% if not forloop.last %}
{% endif %}{% endfor %} +
+ {% endif %}
{% endfor %}
diff --git a/netbox/utilities/templatetags/form_helpers.py b/netbox/utilities/templatetags/form_helpers.py index ec53fe97c17..9c90a0a370d 100644 --- a/netbox/utilities/templatetags/form_helpers.py +++ b/netbox/utilities/templatetags/form_helpers.py @@ -53,6 +53,7 @@ def render_fieldset(form, fieldset): Render a group set of fields. """ rows = [] + for item in fieldset.items: # Multiple fields side-by-side @@ -61,7 +62,7 @@ def render_fieldset(form, fieldset): form[name] for name in item.fields if name in form.fields ] rows.append( - ('inline', item.label, fields) + ('inline', item.label, fields, any(f.errors for f in fields)) ) # Tabbed groups of fields @@ -78,7 +79,7 @@ def render_fieldset(form, fieldset): if not any(tab['active'] for tab in tabs): tabs[0]['active'] = True rows.append( - ('tabs', None, tabs) + ('tabs', None, tabs, False) ) elif type(item) is ObjectAttribute: @@ -95,7 +96,7 @@ def render_fieldset(form, fieldset): if field.name in getattr(form, 'nullable_fields', []): field._nullable = True rows.append( - ('field', None, [field]) + ('field', None, [field], False) ) return { diff --git a/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py b/netbox/wireless/migrations/0015_extend_wireless_link_abs_distance_upper_limit.py similarity index 61% rename from netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py rename to netbox/wireless/migrations/0015_extend_wireless_link_abs_distance_upper_limit.py index a4709df81fc..1d8d9a7ed26 100644 --- a/netbox/wireless/migrations/0015_alter_wirelesslink__abs_distance_and_more.py +++ b/netbox/wireless/migrations/0015_extend_wireless_link_abs_distance_upper_limit.py @@ -1,3 +1,5 @@ +# Generated by Django 5.2.1 on 2025-05-16 14:42 + from django.db import migrations, models @@ -11,11 +13,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='wirelesslink', name='_abs_distance', - field=models.DecimalField(blank=True, decimal_places=4, max_digits=18, null=True), - ), - migrations.AlterField( - model_name='wirelesslink', - name='distance', - field=models.DecimalField(blank=True, decimal_places=2, max_digits=16, null=True), + field=models.DecimalField(blank=True, decimal_places=4, max_digits=13, null=True), ), ] From a3adb6de482f131d07bf0abb8f0afc4fbcbeccc2 Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Fri, 16 May 2025 10:13:30 -0500 Subject: [PATCH 6/8] Gah, remove django migration header comment --- .../migrations/0052_extend_circuit_abs_distance_upper_limit.py | 2 -- .../0015_extend_wireless_link_abs_distance_upper_limit.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/netbox/circuits/migrations/0052_extend_circuit_abs_distance_upper_limit.py b/netbox/circuits/migrations/0052_extend_circuit_abs_distance_upper_limit.py index eba606b0334..abc54f627e2 100644 --- a/netbox/circuits/migrations/0052_extend_circuit_abs_distance_upper_limit.py +++ b/netbox/circuits/migrations/0052_extend_circuit_abs_distance_upper_limit.py @@ -1,5 +1,3 @@ -# Generated by Django 5.2.1 on 2025-05-16 14:42 - from django.db import migrations, models diff --git a/netbox/wireless/migrations/0015_extend_wireless_link_abs_distance_upper_limit.py b/netbox/wireless/migrations/0015_extend_wireless_link_abs_distance_upper_limit.py index 1d8d9a7ed26..86e8c6af57e 100644 --- a/netbox/wireless/migrations/0015_extend_wireless_link_abs_distance_upper_limit.py +++ b/netbox/wireless/migrations/0015_extend_wireless_link_abs_distance_upper_limit.py @@ -1,5 +1,3 @@ -# Generated by Django 5.2.1 on 2025-05-16 14:42 - from django.db import migrations, models From cb82810b2741e287e40e11e134da1d5c6164f659 Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Fri, 16 May 2025 11:32:51 -0500 Subject: [PATCH 7/8] Clean up new has_field_errors mechanism, fix issue with ObjectAttribute --- .../templates/form_helpers/render_fieldset.html | 4 ++-- netbox/utilities/templatetags/form_helpers.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/netbox/utilities/templates/form_helpers/render_fieldset.html b/netbox/utilities/templates/form_helpers/render_fieldset.html index 0faf23c9249..a9d980f4d33 100644 --- a/netbox/utilities/templates/form_helpers/render_fieldset.html +++ b/netbox/utilities/templates/form_helpers/render_fieldset.html @@ -6,7 +6,7 @@

{{ heading }}

{% endif %} - {% for layout, title, items, has_errors in rows %} + {% for layout, title, items, has_field_errors in rows %} {% if layout == 'field' %} {# Single form field #} @@ -25,7 +25,7 @@

{{ heading }}

{% elif layout == 'inline' %} {# Multiple form fields on the same line #} -
+
{% for field in items %}
diff --git a/netbox/utilities/templatetags/form_helpers.py b/netbox/utilities/templatetags/form_helpers.py index 9c90a0a370d..b5f83c565b2 100644 --- a/netbox/utilities/templatetags/form_helpers.py +++ b/netbox/utilities/templatetags/form_helpers.py @@ -51,6 +51,8 @@ def widget_type(field): def render_fieldset(form, fieldset): """ Render a group set of fields. + + The signature for row tuples is (layout, title, items, has_field_errors). """ rows = [] @@ -75,18 +77,21 @@ def render_fieldset(form, fieldset): 'fields': [form[name] for name in tab['fields'] if name in form.fields] } for tab in item.tabs ] + has_field_errors = any( + field.errors for tab in tabs for field in tab['fields'] + ) # If none of the tabs has been marked as active, activate the first one if not any(tab['active'] for tab in tabs): tabs[0]['active'] = True rows.append( - ('tabs', None, tabs, False) + ('tabs', None, tabs, has_field_errors) ) elif type(item) is ObjectAttribute: value = getattr(form.instance, item.name) label = value._meta.verbose_name if hasattr(value, '_meta') else item.name rows.append( - ('attribute', label.title(), [value]) + ('attribute', label.title(), [value], False) ) # A single form field @@ -96,7 +101,7 @@ def render_fieldset(form, fieldset): if field.name in getattr(form, 'nullable_fields', []): field._nullable = True rows.append( - ('field', None, [field], False) + ('field', None, [field], bool(field.errors)) ) return { From c68b739d0a0965ee0d65d35be70cb7281d177fcd Mon Sep 17 00:00:00 2001 From: Jason Novinger Date: Fri, 16 May 2025 15:12:56 -0500 Subject: [PATCH 8/8] Address PR feedback, revert changes to render_fieldset template tag --- netbox/netbox/models/mixins.py | 2 +- netbox/utilities/forms/mixins.py | 1 + .../templates/form_helpers/render_fieldset.html | 6 +++--- netbox/utilities/templatetags/form_helpers.py | 14 ++++---------- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/netbox/netbox/models/mixins.py b/netbox/netbox/models/mixins.py index 46a85b126bb..13af8aaf55b 100644 --- a/netbox/netbox/models/mixins.py +++ b/netbox/netbox/models/mixins.py @@ -1,7 +1,7 @@ - from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import gettext_lazy as _ + from netbox.choices import * from utilities.conversion import to_grams, to_meters diff --git a/netbox/utilities/forms/mixins.py b/netbox/utilities/forms/mixins.py index 05868106092..ca0f64e542e 100644 --- a/netbox/utilities/forms/mixins.py +++ b/netbox/utilities/forms/mixins.py @@ -7,6 +7,7 @@ __all__ = ( 'CheckLastUpdatedMixin', + 'DistanceValidationMixin', ) diff --git a/netbox/utilities/templates/form_helpers/render_fieldset.html b/netbox/utilities/templates/form_helpers/render_fieldset.html index a9d980f4d33..1821a3cb70e 100644 --- a/netbox/utilities/templates/form_helpers/render_fieldset.html +++ b/netbox/utilities/templates/form_helpers/render_fieldset.html @@ -6,7 +6,7 @@

{{ heading }}

{% endif %} - {% for layout, title, items, has_field_errors in rows %} + {% for layout, title, items in rows %} {% if layout == 'field' %} {# Single form field #} @@ -25,10 +25,10 @@

{{ heading }}

{% elif layout == 'inline' %} {# Multiple form fields on the same line #} -
+
{% for field in items %} -
+
{{ field }}
{% trans field.label %}
{% if field.errors %} diff --git a/netbox/utilities/templatetags/form_helpers.py b/netbox/utilities/templatetags/form_helpers.py index b5f83c565b2..ec53fe97c17 100644 --- a/netbox/utilities/templatetags/form_helpers.py +++ b/netbox/utilities/templatetags/form_helpers.py @@ -51,11 +51,8 @@ def widget_type(field): def render_fieldset(form, fieldset): """ Render a group set of fields. - - The signature for row tuples is (layout, title, items, has_field_errors). """ rows = [] - for item in fieldset.items: # Multiple fields side-by-side @@ -64,7 +61,7 @@ def render_fieldset(form, fieldset): form[name] for name in item.fields if name in form.fields ] rows.append( - ('inline', item.label, fields, any(f.errors for f in fields)) + ('inline', item.label, fields) ) # Tabbed groups of fields @@ -77,21 +74,18 @@ def render_fieldset(form, fieldset): 'fields': [form[name] for name in tab['fields'] if name in form.fields] } for tab in item.tabs ] - has_field_errors = any( - field.errors for tab in tabs for field in tab['fields'] - ) # If none of the tabs has been marked as active, activate the first one if not any(tab['active'] for tab in tabs): tabs[0]['active'] = True rows.append( - ('tabs', None, tabs, has_field_errors) + ('tabs', None, tabs) ) elif type(item) is ObjectAttribute: value = getattr(form.instance, item.name) label = value._meta.verbose_name if hasattr(value, '_meta') else item.name rows.append( - ('attribute', label.title(), [value], False) + ('attribute', label.title(), [value]) ) # A single form field @@ -101,7 +95,7 @@ def render_fieldset(form, fieldset): if field.name in getattr(form, 'nullable_fields', []): field._nullable = True rows.append( - ('field', None, [field], bool(field.errors)) + ('field', None, [field]) ) return {