DynamicModelChoiceField doesn't render error message on submit #10898

Closed
opened 2025-12-29 21:37:27 +01:00 by adam · 4 comments
Owner

Originally created by @chii0815 on GitHub (Mar 16, 2025).

Originally assigned to: @jeremystretch on GitHub.

Deployment Type

Self-hosted

NetBox Version

v.4.2.5

Python Version

3.11

Steps to Reproduce

While developing a plugin I realized that the DynamicModelChoiceField doesn't display an error if required=True and the nothing is selected. The form just doesn't submit and the view doesn't give a hint why.

  1. create a form with two choice fields (one django.forms.ModelChoiceField and one DynamicModelChoiceField)
  2. set both to be required

Expected Behavior

When both are empty the form should show an error message on submit

Observed Behavior

only the forms.ModelChoiceField shows an error message

Image

Originally created by @chii0815 on GitHub (Mar 16, 2025). Originally assigned to: @jeremystretch on GitHub. ### Deployment Type Self-hosted ### NetBox Version v.4.2.5 ### Python Version 3.11 ### Steps to Reproduce While developing a plugin I realized that the DynamicModelChoiceField doesn't display an error if `required=True` and the nothing is selected. The form just doesn't submit and the view doesn't give a hint why. 1. create a form with two choice fields (one django.forms.ModelChoiceField and one DynamicModelChoiceField) 2. set both to be required ### Expected Behavior When both are empty the form should show an error message on submit ### Observed Behavior only the forms.ModelChoiceField shows an error message ![Image](https://github.com/user-attachments/assets/459d1426-4054-40b8-8482-71aba22e22e0)
adam added the type: bugstatus: acceptedseverity: low labels 2025-12-29 21:37:27 +01:00
adam closed this issue 2025-12-29 21:37:27 +01:00
Author
Owner

@chii0815 commented on GitHub (Mar 16, 2025):

this is my py file for this form

from django import forms
from circuits.models import Provider
from tenancy.models import Tenant
from netbox.forms import NetBoxModelForm
from django.utils.translation import gettext_lazy as _
from utilities.forms.fields import DynamicModelChoiceField
from ..models import CVS

class CVSForm(NetBoxModelForm):

    provider = forms.ModelChoiceField(
        queryset = Provider.objects.all(),
        required = True,
        label = _('provider')
    )

    tenant = DynamicModelChoiceField(
        queryset = Tenant.objects.all(),
        required = True,
        selector= True,
        label= _('tenant')
    )  

    class Meta:
        model = CVS
        fields = ('name', 'provider', 'tenant', 'tags')

    def clean(self):
        super().clean()
@chii0815 commented on GitHub (Mar 16, 2025): this is my py file for this form ``` from django import forms from circuits.models import Provider from tenancy.models import Tenant from netbox.forms import NetBoxModelForm from django.utils.translation import gettext_lazy as _ from utilities.forms.fields import DynamicModelChoiceField from ..models import CVS class CVSForm(NetBoxModelForm): provider = forms.ModelChoiceField( queryset = Provider.objects.all(), required = True, label = _('provider') ) tenant = DynamicModelChoiceField( queryset = Tenant.objects.all(), required = True, selector= True, label= _('tenant') ) class Meta: model = CVS fields = ('name', 'provider', 'tenant', 'tags') def clean(self): super().clean() ```
Author
Owner

@chii0815 commented on GitHub (Mar 16, 2025):

this also happens on netboxes dcim model device-types:

Image

@chii0815 commented on GitHub (Mar 16, 2025): this also happens on netboxes dcim model device-types: ![Image](https://github.com/user-attachments/assets/4c48cbcb-3d36-4363-bedd-6f036d26c565)
Author
Owner

@jnovinger commented on GitHub (Apr 10, 2025):

I've tracked this down to the core cause, which is that this is being caused by the underlying APISelect widget. Any form field that uses APISelect, either through DynamicModelChoiceField or some other way, is affected by this bug.

The tl;dr is that APISelect uses its own template, netbox/utilities/templates/widgets/apiselect.html which ends up wrapping the base django/forms/widgets/select.html template from Django core in a <div class="d-flex">...</div> and sets up either the object selector modal and/or the quick add modal.

It' this extra div that causes the problem, because it changes the DOM tree structure just enough that when our form submit handler marks the element as invalid, it doesn't see a sibling element with the class="invalid-feedback" which it should make visible.

You can observe this behavior in a couple of ways:

  1. Remove the wrapping <div>...</div>, which breaks the display of the modal buttons
  2. Add a widget=Select(), which comes from django.forms.widgets, and doesn't break the modal buttons but just flat out will not display them since it uses a different template.

Alright, enough for now. I think I'm going to might have to fix this for my work on #8423.

@jnovinger commented on GitHub (Apr 10, 2025): I've tracked this down to the core cause, which is that this is being caused by the underlying `APISelect` widget. Any form field that uses `APISelect`, either through `DynamicModelChoiceField` or some other way, is affected by this bug. The **tl;dr** is that `APISelect` uses its own template, `netbox/utilities/templates/widgets/apiselect.html` which ends up wrapping the base `django/forms/widgets/select.html` template from Django core in a `<div class="d-flex">...</div>` and sets up either the object selector modal and/or the quick add modal. It' this extra div that causes the problem, because it changes the DOM tree structure just enough that when our form submit handler marks the element as invalid, it doesn't see a sibling element with the `class="invalid-feedback"` which it should make visible. You can observe this behavior in a couple of ways: 1. Remove the wrapping `<div>...</div>`, which breaks the display of the modal buttons 2. Add a `widget=Select()`, which comes from `django.forms.widgets`, and doesn't break the modal buttons but just flat out will not display them since it uses a different template. Alright, enough for now. I think I'm ~~going to~~ might have to fix this for my work on #8423.
Author
Owner

@jeremystretch commented on GitHub (Aug 25, 2025):

This can be reproduced natively by attempting to add a provider account without specifying a provider.

@jeremystretch commented on GitHub (Aug 25, 2025): This can be reproduced natively by attempting to add a provider account without specifying a provider.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#10898