diff --git a/netbox/ipam/forms/model_forms.py b/netbox/ipam/forms/model_forms.py index d3159320d..c8c488329 100644 --- a/netbox/ipam/forms/model_forms.py +++ b/netbox/ipam/forms/model_forms.py @@ -250,31 +250,22 @@ class PrefixForm(TenancyForm, ScopedForm, PrimaryModelForm): self.fields['vlan'].widget.attrs.pop('data-dynamic-params', None) -class PrefixBulkAddForm(TenancyForm, NetBoxModelForm): - vrf = DynamicModelChoiceField( - queryset=VRF.objects.all(), - required=False, - label=_('VRF') - ) - role = DynamicModelChoiceField( - label=_('Role'), - queryset=Role.objects.all(), - required=False, - quick_add=True - ) +class PrefixBulkAddForm(PrefixForm): + """ + Subclass of PrefixForm for bulk creation. The prefix field is inherited + but excluded from fieldsets — it is populated programmatically by BulkCreateView + from the expanded pattern. + """ fieldsets = ( - FieldSet('status', 'role', 'vrf', 'is_pool', 'mark_utilized', 'description', 'tags', name=_('Prefix')), + FieldSet( + 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags', name=_('Prefix') + ), + FieldSet('scope_type', 'scope', name=_('Scope')), + FieldSet('vlan', name=_('VLAN Assignment')), FieldSet('tenant_group', 'tenant', name=_('Tenancy')), ) - class Meta: - model = Prefix - fields = [ - 'prefix', 'vrf', 'status', 'role', 'is_pool', 'mark_utilized', 'description', 'tenant_group', 'tenant', - 'tags', - ] - class IPRangeForm(TenancyForm, PrimaryModelForm): vrf = DynamicModelChoiceField( diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index ec18412a4..9db40a0b0 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -225,6 +225,7 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): form = None model_form = None pattern_target = '' + htmx_template_name = 'htmx/bulk_add_form.html' def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'add') @@ -280,6 +281,12 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): form = self.form() model_form = self.model_form(initial=initial) + # HTMX partial: only re-render the model form fields + if htmx_partial(request): + return render(request, self.htmx_template_name, { + 'model_form': model_form, + }) + return render(request, self.template_name, self._get_context(request, form, model_form)) def post(self, request): @@ -288,6 +295,12 @@ class BulkCreateView(GetReturnURLMixin, BaseMultiObjectView): form = self.form(request.POST) model_form = self.model_form(request.POST) + # HTMX partial: only re-render the model form fields + if htmx_partial(request): + return render(request, self.htmx_template_name, { + 'model_form': model_form, + }) + if form.is_valid(): logger.debug("Form validation was successful") diff --git a/netbox/templates/generic/bulk_add.html b/netbox/templates/generic/bulk_add.html index ebc7bc0be..b9c3999a1 100644 --- a/netbox/templates/generic/bulk_add.html +++ b/netbox/templates/generic/bulk_add.html @@ -20,14 +20,16 @@ {% endblock %} -{% block form %} +{% block pre_form_fields %}

{% trans "Pattern" %}

{% render_field form.pattern %}
+{% endblock pre_form_fields %} +{% block form %} {% if model_form.fieldsets %} {% for fieldset in model_form.fieldsets %} {% render_fieldset model_form fieldset %} @@ -46,4 +48,4 @@ {% render_custom_fields model_form %} {% endif %} -{% endblock %} +{% endblock form %} diff --git a/netbox/templates/generic/object_edit.html b/netbox/templates/generic/object_edit.html index 3e0a096fa..c24c27929 100644 --- a/netbox/templates/generic/object_edit.html +++ b/netbox/templates/generic/object_edit.html @@ -61,6 +61,7 @@ Context:
{% csrf_token %} + {% block pre_form_fields %}{% endblock pre_form_fields %}
{% block form %} {% include 'htmx/form.html' %} diff --git a/netbox/templates/htmx/bulk_add_form.html b/netbox/templates/htmx/bulk_add_form.html new file mode 100644 index 000000000..7ea742b30 --- /dev/null +++ b/netbox/templates/htmx/bulk_add_form.html @@ -0,0 +1,22 @@ +{% load helpers %} +{% load form_helpers %} +{% load i18n %} + +{% if model_form.fieldsets %} + {% for fieldset in model_form.fieldsets %} + {% render_fieldset model_form fieldset %} + {% endfor %} +{% else %} +
+ {% render_form model_form %} +
+{% endif %} + +{% if model_form.custom_fields %} +
+
+

{% trans "Custom Fields" %}

+
+ {% render_custom_fields model_form %} +
+{% endif %} diff --git a/netbox/templates/ipam/inc/prefix_edit_header.html b/netbox/templates/ipam/inc/prefix_edit_header.html index db8c12d46..bc63defe9 100644 --- a/netbox/templates/ipam/inc/prefix_edit_header.html +++ b/netbox/templates/ipam/inc/prefix_edit_header.html @@ -3,8 +3,8 @@