Fixes #20542: Add form prefix to POST handler in ObjectEditView (#20550)

Commit d22246688 added form prefix support to the `GET` handler to fix
Markdown preview functionality in quick add modals. The form prefix
allows Django to properly namespace field names and IDs when rendering
forms within the quick add modal context.

However, the corresponding change was not made to the `POST` handler. This
created a mismatch where form fields were rendered with the `quickadd-`
prefix during `GET` requests, but the `POST` handler instantiated forms
without the prefix. When users submitted quick add forms, Django looked
for unprefixed field names like `address` and `status` in the `POST` data,
but the actual submitted data used prefixed names like `quickadd-address`
and `quickadd-status`. This caused validation to fail immediately with
"This field is required" errors for all required fields, making every
quick add form unusable.

The fix adds the same prefix detection logic to the `POST` handler that was
added to the `GET` handler, checking for the `_quickadd` parameter in the
query string and applying the `quickadd` prefix when present. This ensures
consistent form field naming between rendering and validation.

A regression test has been added to `MACAddressTestCase` to verify that MAC
addresses can be successfully created via the quick add modal, preventing
this issue from recurring. This test should be promoted to a template
test whenever it becomes possible to determine if a model should support
quick-add functionality.
This commit is contained in:
Jason Novinger 2025-10-09 16:42:59 -05:00 committed by GitHub
parent 7719b98697
commit f0523611d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 2 deletions

View File

@ -7,13 +7,14 @@ from django.test import override_settings, tag
from django.urls import reverse from django.urls import reverse
from netaddr import EUI from netaddr import EUI
from core.models import ObjectType
from dcim.choices import * from dcim.choices import *
from dcim.constants import * from dcim.constants import *
from dcim.models import * from dcim.models import *
from ipam.models import ASN, RIR, VLAN, VRF from ipam.models import ASN, RIR, VLAN, VRF
from netbox.choices import CSVDelimiterChoices, ImportFormatChoices, WeightUnitChoices from netbox.choices import CSVDelimiterChoices, ImportFormatChoices, WeightUnitChoices
from tenancy.models import Tenant from tenancy.models import Tenant
from users.models import User from users.models import ObjectPermission, User
from utilities.testing import ViewTestCases, create_tags, create_test_device, post_data from utilities.testing import ViewTestCases, create_tags, create_test_device, post_data
from wireless.models import WirelessLAN from wireless.models import WirelessLAN
@ -3728,3 +3729,29 @@ class MACAddressTestCase(ViewTestCases.PrimaryObjectViewTestCase):
cls.bulk_edit_data = { cls.bulk_edit_data = {
'description': 'New description', 'description': 'New description',
} }
@tag('regression') # Issue #20542
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'], EXEMPT_EXCLUDE_MODELS=[])
def test_create_macaddress_via_quickadd(self):
"""
Test creating a MAC address via quick-add modal (e.g., from Interface form).
Regression test for issue #20542 where form prefix was missing in POST handler.
"""
obj_perm = ObjectPermission(name='Test permission', actions=['add'])
obj_perm.save()
obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
# Simulate quick-add form submission with 'quickadd-' prefix
formatted_data = post_data(self.form_data)
quickadd_data = {f'quickadd-{k}': v for k, v in formatted_data.items()}
quickadd_data['_quickadd'] = 'True'
initial_count = self._get_queryset().count()
url = f"{self._get_url('add')}?_quickadd=True&target=id_primary_mac_address"
response = self.client.post(url, data=quickadd_data)
# Should successfully create the MAC address and return the quick_add_created template
self.assertHttpStatus(response, 200)
self.assertIn(b'quick-add-object', response.content)
self.assertEqual(initial_count + 1, self._get_queryset().count())

View File

@ -281,7 +281,8 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView):
obj = self.alter_object(obj, request, args, kwargs) obj = self.alter_object(obj, request, args, kwargs)
form = self.form(data=request.POST, files=request.FILES, instance=obj) form_prefix = 'quickadd' if request.GET.get('_quickadd') else None
form = self.form(data=request.POST, files=request.FILES, instance=obj, prefix=form_prefix)
restrict_form_fields(form, request.user) restrict_form_fields(form, request.user)
if form.is_valid(): if form.is_valid():