mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-20 04:12:25 -06:00
* Introduce custom form widget templates to apply CSS classes * Apply both mandatory and optional CSS classes to form widgets * Omit required & placeholder attrs * Move annotation of field validation failures to CSS * Remove BootstrapMixin class * Remove obsolete ComponentTemplateImportForm class * Remove obsolete custom forms for login & password change * Clean up obsolete accommodations for 'required' widget attr
This commit is contained in:
@@ -10,10 +10,9 @@ from core.forms.mixins import SyncedDataMixin
|
||||
from utilities.choices import CSVDelimiterChoices, ImportFormatChoices, ImportMethodChoices
|
||||
from utilities.constants import CSV_DELIMITERS
|
||||
from utilities.forms.utils import parse_csv
|
||||
from .mixins import BootstrapMixin
|
||||
|
||||
|
||||
class BulkImportForm(BootstrapMixin, SyncedDataMixin, forms.Form):
|
||||
class BulkImportForm(SyncedDataMixin, forms.Form):
|
||||
import_method = forms.ChoiceField(
|
||||
choices=ImportMethodChoices,
|
||||
required=False
|
||||
|
||||
@@ -2,7 +2,6 @@ import re
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import gettext as _
|
||||
from .mixins import BootstrapMixin
|
||||
|
||||
__all__ = (
|
||||
'BulkEditForm',
|
||||
@@ -14,7 +13,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class ConfirmationForm(BootstrapMixin, forms.Form):
|
||||
class ConfirmationForm(forms.Form):
|
||||
"""
|
||||
A generic confirmation form. The form is not valid unless the `confirm` field is checked.
|
||||
"""
|
||||
@@ -29,14 +28,14 @@ class ConfirmationForm(BootstrapMixin, forms.Form):
|
||||
)
|
||||
|
||||
|
||||
class BulkEditForm(BootstrapMixin, forms.Form):
|
||||
class BulkEditForm(forms.Form):
|
||||
"""
|
||||
Provides bulk edit support for objects.
|
||||
"""
|
||||
nullable_fields = ()
|
||||
|
||||
|
||||
class BulkRenameForm(BootstrapMixin, forms.Form):
|
||||
class BulkRenameForm(forms.Form):
|
||||
"""
|
||||
An extendable form to be used for renaming objects in bulk.
|
||||
"""
|
||||
@@ -90,7 +89,7 @@ class CSVModelForm(forms.ModelForm):
|
||||
return super().clean()
|
||||
|
||||
|
||||
class FilterForm(BootstrapMixin, forms.Form):
|
||||
class FilterForm(forms.Form):
|
||||
"""
|
||||
Base Form class for FilterSet forms.
|
||||
"""
|
||||
@@ -100,7 +99,7 @@ class FilterForm(BootstrapMixin, forms.Form):
|
||||
)
|
||||
|
||||
|
||||
class TableConfigForm(BootstrapMixin, forms.Form):
|
||||
class TableConfigForm(forms.Form):
|
||||
"""
|
||||
Form for configuring user's table preferences.
|
||||
"""
|
||||
|
||||
@@ -3,68 +3,11 @@ import time
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .widgets import APISelect, APISelectMultiple, ClearableFileInput
|
||||
|
||||
__all__ = (
|
||||
'BootstrapMixin',
|
||||
'CheckLastUpdatedMixin',
|
||||
)
|
||||
|
||||
|
||||
class BootstrapMixin:
|
||||
"""
|
||||
Add the base Bootstrap CSS classes to form elements.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
exempt_widgets = [
|
||||
forms.FileInput,
|
||||
forms.RadioSelect,
|
||||
APISelect,
|
||||
APISelectMultiple,
|
||||
ClearableFileInput,
|
||||
]
|
||||
|
||||
for field_name, field in self.fields.items():
|
||||
css = field.widget.attrs.get('class', '')
|
||||
|
||||
if field.widget.__class__ in exempt_widgets:
|
||||
continue
|
||||
|
||||
elif isinstance(field.widget, forms.CheckboxInput):
|
||||
field.widget.attrs['class'] = f'{css} form-check-input'
|
||||
|
||||
elif isinstance(field.widget, forms.SelectMultiple) and 'size' in field.widget.attrs:
|
||||
# Use native Bootstrap class for multi-line <select> widgets
|
||||
field.widget.attrs['class'] = f'{css} form-select form-select-sm'
|
||||
|
||||
elif isinstance(field.widget, (forms.Select, forms.SelectMultiple)):
|
||||
field.widget.attrs['class'] = f'{css} netbox-static-select'
|
||||
|
||||
else:
|
||||
field.widget.attrs['class'] = f'{css} form-control'
|
||||
|
||||
if field.required and not isinstance(field.widget, forms.FileInput):
|
||||
field.widget.attrs['required'] = 'required'
|
||||
|
||||
if 'placeholder' not in field.widget.attrs and field.label is not None:
|
||||
field.widget.attrs['placeholder'] = field.label
|
||||
|
||||
def is_valid(self):
|
||||
is_valid = super().is_valid()
|
||||
|
||||
# Apply is-invalid CSS class to fields with errors
|
||||
if not is_valid:
|
||||
for field_name in self.errors:
|
||||
# Ignore e.g. __all__
|
||||
if field := self.fields.get(field_name):
|
||||
css = field.widget.attrs.get('class', '')
|
||||
field.widget.attrs['class'] = f'{css} is-invalid'
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
class CheckLastUpdatedMixin(forms.Form):
|
||||
"""
|
||||
Checks whether the object being saved has been updated since the form was initialized. If so, validation fails.
|
||||
|
||||
Reference in New Issue
Block a user