|
|
@@ -7,13 +7,14 @@ from django.utils.translation import gettext as _
|
|
|
from dcim.models import DeviceRole, DeviceType, Platform, Region, Site, SiteGroup
|
|
|
from tenancy.models import Tenant, TenantGroup
|
|
|
from utilities.forms import (
|
|
|
- add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect,
|
|
|
- CommentField, ContentTypeMultipleChoiceField, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField,
|
|
|
- JSONField, SlugField, StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES,
|
|
|
+ add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorField,
|
|
|
+ CommentField, ContentTypeChoiceField, ContentTypeMultipleChoiceField, CSVContentTypeField, CSVModelForm,
|
|
|
+ CSVMultipleContentTypeField, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField, StaticSelect2,
|
|
|
+ StaticSelect2Multiple, BOOLEAN_WITH_BLANK_CHOICES,
|
|
|
)
|
|
|
from virtualization.models import Cluster, ClusterGroup
|
|
|
from .choices import *
|
|
|
-from .models import ConfigContext, CustomField, ImageAttachment, JournalEntry, ObjectChange, Tag
|
|
|
+from .models import *
|
|
|
from .utils import FeatureQuery
|
|
|
|
|
|
|
|
|
@@ -21,58 +22,431 @@ from .utils import FeatureQuery
|
|
|
# Custom fields
|
|
|
#
|
|
|
|
|
|
-class CustomFieldForm(forms.Form):
|
|
|
- """
|
|
|
- Extend Form to include custom field support.
|
|
|
- """
|
|
|
- model = None
|
|
|
+class CustomFieldForm(BootstrapMixin, forms.ModelForm):
|
|
|
+ content_types = ContentTypeMultipleChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_fields')
|
|
|
+ )
|
|
|
|
|
|
- def __init__(self, *args, **kwargs):
|
|
|
- if self.model is None:
|
|
|
- raise NotImplementedError("CustomFieldForm must specify a model class.")
|
|
|
- self.custom_fields = []
|
|
|
+ class Meta:
|
|
|
+ model = CustomField
|
|
|
+ fields = '__all__'
|
|
|
+ fieldsets = (
|
|
|
+ ('Custom Field', ('name', 'label', 'type', 'weight', 'required', 'description')),
|
|
|
+ ('Assigned Models', ('content_types',)),
|
|
|
+ ('Behavior', ('filter_logic',)),
|
|
|
+ ('Values', ('default', 'choices')),
|
|
|
+ ('Validation', ('validation_minimum', 'validation_maximum', 'validation_regex')),
|
|
|
+ )
|
|
|
|
|
|
- super().__init__(*args, **kwargs)
|
|
|
|
|
|
- # Append relevant custom fields to the form instance
|
|
|
- obj_type = ContentType.objects.get_for_model(self.model)
|
|
|
- for cf in CustomField.objects.filter(content_types=obj_type):
|
|
|
- field_name = 'cf_{}'.format(cf.name)
|
|
|
- self.fields[field_name] = cf.to_form_field()
|
|
|
+class CustomFieldCSVForm(CSVModelForm):
|
|
|
+ content_types = CSVMultipleContentTypeField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_fields'),
|
|
|
+ help_text="One or more assigned object types"
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = CustomField
|
|
|
+ fields = (
|
|
|
+ 'name', 'label', 'type', 'content_types', 'required', 'description', 'weight', 'filter_logic', 'default',
|
|
|
+ 'weight',
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class CustomFieldBulkEditForm(BootstrapMixin, BulkEditForm):
|
|
|
+ pk = forms.ModelMultipleChoiceField(
|
|
|
+ queryset=CustomField.objects.all(),
|
|
|
+ widget=forms.MultipleHiddenInput
|
|
|
+ )
|
|
|
+ description = forms.CharField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ required = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=BulkEditNullBooleanSelect()
|
|
|
+ )
|
|
|
+ weight = forms.IntegerField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ nullable_fields = []
|
|
|
+
|
|
|
+
|
|
|
+class CustomFieldFilterForm(BootstrapMixin, forms.Form):
|
|
|
+ field_groups = [
|
|
|
+ ['type', 'content_types'],
|
|
|
+ ['weight', 'required'],
|
|
|
+ ]
|
|
|
+ content_types = ContentTypeMultipleChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_fields')
|
|
|
+ )
|
|
|
+ type = forms.MultipleChoiceField(
|
|
|
+ choices=CustomFieldTypeChoices,
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2Multiple()
|
|
|
+ )
|
|
|
+ weight = forms.IntegerField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ required = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2(
|
|
|
+ choices=BOOLEAN_WITH_BLANK_CHOICES
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+#
|
|
|
+# Custom links
|
|
|
+#
|
|
|
+
|
|
|
+class CustomLinkForm(BootstrapMixin, forms.ModelForm):
|
|
|
+ content_type = ContentTypeChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_links')
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = CustomLink
|
|
|
+ fields = '__all__'
|
|
|
+ fieldsets = (
|
|
|
+ ('Custom Link', ('name', 'content_type', 'weight', 'group_name', 'button_class', 'new_window')),
|
|
|
+ ('Templates', ('link_text', 'link_url')),
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class CustomLinkCSVForm(CSVModelForm):
|
|
|
+ content_type = CSVContentTypeField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_links'),
|
|
|
+ help_text="Assigned object type"
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = CustomLink
|
|
|
+ fields = (
|
|
|
+ 'name', 'content_type', 'weight', 'group_name', 'button_class', 'new_window', 'link_text', 'link_url',
|
|
|
+ )
|
|
|
|
|
|
- # Annotate the field in the list of CustomField form fields
|
|
|
- self.custom_fields.append(field_name)
|
|
|
|
|
|
+class CustomLinkBulkEditForm(BootstrapMixin, BulkEditForm):
|
|
|
+ pk = forms.ModelMultipleChoiceField(
|
|
|
+ queryset=CustomLink.objects.all(),
|
|
|
+ widget=forms.MultipleHiddenInput
|
|
|
+ )
|
|
|
+ content_type = ContentTypeChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_fields'),
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ new_window = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=BulkEditNullBooleanSelect()
|
|
|
+ )
|
|
|
+ weight = forms.IntegerField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ button_class = forms.ChoiceField(
|
|
|
+ choices=CustomLinkButtonClassChoices,
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2()
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ nullable_fields = []
|
|
|
+
|
|
|
+
|
|
|
+class CustomLinkFilterForm(BootstrapMixin, forms.Form):
|
|
|
+ field_groups = [
|
|
|
+ ['content_type'],
|
|
|
+ ['weight', 'new_window'],
|
|
|
+ ]
|
|
|
+ content_type = ContentTypeChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_fields')
|
|
|
+ )
|
|
|
+ weight = forms.IntegerField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ new_window = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2(
|
|
|
+ choices=BOOLEAN_WITH_BLANK_CHOICES
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+#
|
|
|
+# Export templates
|
|
|
+#
|
|
|
|
|
|
-class CustomFieldModelForm(forms.ModelForm):
|
|
|
+class ExportTemplateForm(BootstrapMixin, forms.ModelForm):
|
|
|
+ content_type = ContentTypeChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_links')
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = ExportTemplate
|
|
|
+ fields = '__all__'
|
|
|
+ fieldsets = (
|
|
|
+ ('Custom Link', ('name', 'content_type', 'description')),
|
|
|
+ ('Template', ('template_code',)),
|
|
|
+ ('Rendering', ('mime_type', 'file_extension', 'as_attachment')),
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class ExportTemplateCSVForm(CSVModelForm):
|
|
|
+ content_type = CSVContentTypeField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('export_templates'),
|
|
|
+ help_text="Assigned object type"
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = ExportTemplate
|
|
|
+ fields = (
|
|
|
+ 'name', 'content_type', 'description', 'mime_type', 'file_extension', 'as_attachment', 'template_code',
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class ExportTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
|
|
|
+ pk = forms.ModelMultipleChoiceField(
|
|
|
+ queryset=ExportTemplate.objects.all(),
|
|
|
+ widget=forms.MultipleHiddenInput
|
|
|
+ )
|
|
|
+ content_type = ContentTypeChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_fields'),
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ description = forms.CharField(
|
|
|
+ max_length=200,
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ mime_type = forms.CharField(
|
|
|
+ max_length=50,
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ file_extension = forms.CharField(
|
|
|
+ max_length=15,
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ as_attachment = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=BulkEditNullBooleanSelect()
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ nullable_fields = ['description', 'mime_type', 'file_extension']
|
|
|
+
|
|
|
+
|
|
|
+class ExportTemplateFilterForm(BootstrapMixin, forms.Form):
|
|
|
+ field_groups = [
|
|
|
+ ['content_type', 'mime_type'],
|
|
|
+ ['file_extension', 'as_attachment'],
|
|
|
+ ]
|
|
|
+ content_type = ContentTypeChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_fields')
|
|
|
+ )
|
|
|
+ mime_type = forms.CharField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ file_extension = forms.CharField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ as_attachment = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2(
|
|
|
+ choices=BOOLEAN_WITH_BLANK_CHOICES
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+#
|
|
|
+# Webhooks
|
|
|
+#
|
|
|
+
|
|
|
+class WebhookForm(BootstrapMixin, forms.ModelForm):
|
|
|
+ content_types = ContentTypeMultipleChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('webhooks')
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = Webhook
|
|
|
+ fields = '__all__'
|
|
|
+ fieldsets = (
|
|
|
+ ('Webhook', ('name', 'enabled')),
|
|
|
+ ('Assigned Models', ('content_types',)),
|
|
|
+ ('Events', ('type_create', 'type_update', 'type_delete')),
|
|
|
+ ('HTTP Request', (
|
|
|
+ 'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template', 'secret',
|
|
|
+ )),
|
|
|
+ ('SSL', ('ssl_verification', 'ca_file_path')),
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class WebhookCSVForm(CSVModelForm):
|
|
|
+ content_types = CSVMultipleContentTypeField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('webhooks'),
|
|
|
+ help_text="One or more assigned object types"
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ model = Webhook
|
|
|
+ fields = (
|
|
|
+ 'name', 'enabled', 'content_types', 'type_create', 'type_update', 'type_delete', 'payload_url',
|
|
|
+ 'http_method', 'http_content_type', 'additional_headers', 'body_template', 'secret', 'ssl_verification',
|
|
|
+ 'ca_file_path'
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class WebhookBulkEditForm(BootstrapMixin, BulkEditForm):
|
|
|
+ pk = forms.ModelMultipleChoiceField(
|
|
|
+ queryset=Webhook.objects.all(),
|
|
|
+ widget=forms.MultipleHiddenInput
|
|
|
+ )
|
|
|
+ enabled = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=BulkEditNullBooleanSelect()
|
|
|
+ )
|
|
|
+ type_create = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=BulkEditNullBooleanSelect()
|
|
|
+ )
|
|
|
+ type_update = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=BulkEditNullBooleanSelect()
|
|
|
+ )
|
|
|
+ type_delete = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=BulkEditNullBooleanSelect()
|
|
|
+ )
|
|
|
+ http_method = forms.ChoiceField(
|
|
|
+ choices=WebhookHttpMethodChoices,
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ payload_url = forms.CharField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ ssl_verification = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=BulkEditNullBooleanSelect()
|
|
|
+ )
|
|
|
+ secret = forms.CharField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+ ca_file_path = forms.CharField(
|
|
|
+ required=False
|
|
|
+ )
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ nullable_fields = ['secret', 'ca_file_path']
|
|
|
+
|
|
|
+
|
|
|
+class WebhookFilterForm(BootstrapMixin, forms.Form):
|
|
|
+ field_groups = [
|
|
|
+ ['content_types', 'http_method'],
|
|
|
+ ['enabled', 'type_create', 'type_update', 'type_delete'],
|
|
|
+ ]
|
|
|
+ content_types = ContentTypeMultipleChoiceField(
|
|
|
+ queryset=ContentType.objects.all(),
|
|
|
+ limit_choices_to=FeatureQuery('custom_fields')
|
|
|
+ )
|
|
|
+ http_method = forms.MultipleChoiceField(
|
|
|
+ choices=WebhookHttpMethodChoices,
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2Multiple()
|
|
|
+ )
|
|
|
+ enabled = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2(
|
|
|
+ choices=BOOLEAN_WITH_BLANK_CHOICES
|
|
|
+ )
|
|
|
+ )
|
|
|
+ type_create = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2(
|
|
|
+ choices=BOOLEAN_WITH_BLANK_CHOICES
|
|
|
+ )
|
|
|
+ )
|
|
|
+ type_update = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2(
|
|
|
+ choices=BOOLEAN_WITH_BLANK_CHOICES
|
|
|
+ )
|
|
|
+ )
|
|
|
+ type_delete = forms.NullBooleanField(
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2(
|
|
|
+ choices=BOOLEAN_WITH_BLANK_CHOICES
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+#
|
|
|
+# Custom field models
|
|
|
+#
|
|
|
+
|
|
|
+class CustomFieldsMixin:
|
|
|
"""
|
|
|
- Extend ModelForm to include custom field support.
|
|
|
+ Extend a Form to include custom field support.
|
|
|
"""
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
-
|
|
|
- self.obj_type = ContentType.objects.get_for_model(self._meta.model)
|
|
|
self.custom_fields = []
|
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
self._append_customfield_fields()
|
|
|
|
|
|
+ def _get_content_type(self):
|
|
|
+ """
|
|
|
+ Return the ContentType of the form's model.
|
|
|
+ """
|
|
|
+ if not hasattr(self, 'model'):
|
|
|
+ raise NotImplementedError(f"{self.__class__.__name__} must specify a model class.")
|
|
|
+ return ContentType.objects.get_for_model(self.model)
|
|
|
+
|
|
|
+ def _get_form_field(self, customfield):
|
|
|
+ return customfield.to_form_field()
|
|
|
+
|
|
|
def _append_customfield_fields(self):
|
|
|
"""
|
|
|
- Append form fields for all CustomFields assigned to this model.
|
|
|
+ Append form fields for all CustomFields assigned to this object type.
|
|
|
"""
|
|
|
+ content_type = self._get_content_type()
|
|
|
+
|
|
|
# Append form fields; assign initial values if modifying and existing object
|
|
|
- for cf in CustomField.objects.filter(content_types=self.obj_type):
|
|
|
- field_name = 'cf_{}'.format(cf.name)
|
|
|
- if self.instance.pk:
|
|
|
- self.fields[field_name] = cf.to_form_field(set_initial=False)
|
|
|
- self.fields[field_name].initial = self.instance.custom_field_data.get(cf.name)
|
|
|
- else:
|
|
|
- self.fields[field_name] = cf.to_form_field()
|
|
|
+ for customfield in CustomField.objects.filter(content_types=content_type):
|
|
|
+ field_name = f'cf_{customfield.name}'
|
|
|
+ self.fields[field_name] = self._get_form_field(customfield)
|
|
|
|
|
|
# Annotate the field in the list of CustomField form fields
|
|
|
self.custom_fields.append(field_name)
|
|
|
|
|
|
+
|
|
|
+class CustomFieldModelForm(CustomFieldsMixin, forms.ModelForm):
|
|
|
+ """
|
|
|
+ Extend ModelForm to include custom field support.
|
|
|
+ """
|
|
|
+ def _get_content_type(self):
|
|
|
+ return ContentType.objects.get_for_model(self._meta.model)
|
|
|
+
|
|
|
+ def _get_form_field(self, customfield):
|
|
|
+ if self.instance.pk:
|
|
|
+ form_field = customfield.to_form_field(set_initial=False)
|
|
|
+ form_field.initial = self.instance.custom_field_data.get(customfield.name, None)
|
|
|
+ return form_field
|
|
|
+
|
|
|
+ return customfield.to_form_field()
|
|
|
+
|
|
|
def clean(self):
|
|
|
|
|
|
# Save custom field data on instance
|
|
|
@@ -84,18 +458,11 @@ class CustomFieldModelForm(forms.ModelForm):
|
|
|
|
|
|
class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelForm):
|
|
|
|
|
|
- def _append_customfield_fields(self):
|
|
|
+ def _get_form_field(self, customfield):
|
|
|
+ return customfield.to_form_field(for_csv_import=True)
|
|
|
|
|
|
- # Append form fields
|
|
|
- for cf in CustomField.objects.filter(content_types=self.obj_type):
|
|
|
- field_name = 'cf_{}'.format(cf.name)
|
|
|
- self.fields[field_name] = cf.to_form_field(for_csv_import=True)
|
|
|
|
|
|
- # Annotate the field in the list of CustomField form fields
|
|
|
- self.custom_fields.append(field_name)
|
|
|
-
|
|
|
-
|
|
|
-class CustomFieldBulkEditForm(BulkEditForm):
|
|
|
+class CustomFieldModelBulkEditForm(BulkEditForm):
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
super().__init__(*args, **kwargs)
|
|
|
@@ -114,7 +481,7 @@ class CustomFieldBulkEditForm(BulkEditForm):
|
|
|
self.custom_fields.append(cf.name)
|
|
|
|
|
|
|
|
|
-class CustomFieldFilterForm(forms.Form):
|
|
|
+class CustomFieldModelFilterForm(forms.Form):
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
|
@@ -153,7 +520,7 @@ class TagCSVForm(CSVModelForm):
|
|
|
|
|
|
class Meta:
|
|
|
model = Tag
|
|
|
- fields = Tag.csv_headers
|
|
|
+ fields = ('name', 'slug', 'color', 'description')
|
|
|
help_texts = {
|
|
|
'color': mark_safe('RGB color in hexadecimal (e.g. <code>00ff00</code>)'),
|
|
|
}
|
|
|
@@ -193,10 +560,8 @@ class TagBulkEditForm(BootstrapMixin, BulkEditForm):
|
|
|
queryset=Tag.objects.all(),
|
|
|
widget=forms.MultipleHiddenInput
|
|
|
)
|
|
|
- color = forms.CharField(
|
|
|
- max_length=6,
|
|
|
- required=False,
|
|
|
- widget=ColorSelect()
|
|
|
+ color = ColorField(
|
|
|
+ required=False
|
|
|
)
|
|
|
description = forms.CharField(
|
|
|
max_length=200,
|
|
|
@@ -294,13 +659,15 @@ class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
|
|
|
|
|
|
class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
|
|
field_order = [
|
|
|
- 'q', 'region_id', 'site_group_id', 'site_id', 'role_id', 'platform_id', 'cluster_group_id', 'cluster_id',
|
|
|
+ 'region_id', 'site_group_id', 'site_id', 'role_id', 'platform_id', 'cluster_group_id', 'cluster_id',
|
|
|
'tenant_group_id', 'tenant_id',
|
|
|
]
|
|
|
- q = forms.CharField(
|
|
|
- required=False,
|
|
|
- label=_('Search')
|
|
|
- )
|
|
|
+ field_groups = [
|
|
|
+ ['region_id', 'site_group_id', 'site_id'],
|
|
|
+ ['device_type_id', 'role_id', 'platform_id'],
|
|
|
+ ['cluster_group_id', 'cluster_id'],
|
|
|
+ ['tenant_group_id', 'tenant_id', 'tag']
|
|
|
+ ]
|
|
|
region_id = DynamicModelMultipleChoiceField(
|
|
|
queryset=Region.objects.all(),
|
|
|
required=False,
|
|
|
@@ -393,6 +760,12 @@ class ImageAttachmentForm(BootstrapMixin, forms.ModelForm):
|
|
|
class JournalEntryForm(BootstrapMixin, forms.ModelForm):
|
|
|
comments = CommentField()
|
|
|
|
|
|
+ kind = forms.ChoiceField(
|
|
|
+ choices=add_blank_choice(JournalEntryKindChoices),
|
|
|
+ required=False,
|
|
|
+ widget=StaticSelect2()
|
|
|
+ )
|
|
|
+
|
|
|
class Meta:
|
|
|
model = JournalEntry
|
|
|
fields = ['assigned_object_type', 'assigned_object_id', 'kind', 'comments']
|
|
|
@@ -422,10 +795,10 @@ class JournalEntryBulkEditForm(BootstrapMixin, BulkEditForm):
|
|
|
|
|
|
class JournalEntryFilterForm(BootstrapMixin, forms.Form):
|
|
|
model = JournalEntry
|
|
|
- q = forms.CharField(
|
|
|
- required=False,
|
|
|
- label=_('Search')
|
|
|
- )
|
|
|
+ field_groups = [
|
|
|
+ ['created_before', 'created_after', 'created_by_id'],
|
|
|
+ ['assigned_object_type_id', 'kind']
|
|
|
+ ]
|
|
|
created_after = forms.DateTimeField(
|
|
|
required=False,
|
|
|
label=_('After'),
|
|
|
@@ -465,10 +838,10 @@ class JournalEntryFilterForm(BootstrapMixin, forms.Form):
|
|
|
|
|
|
class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
|
|
|
model = ObjectChange
|
|
|
- q = forms.CharField(
|
|
|
- required=False,
|
|
|
- label=_('Search')
|
|
|
- )
|
|
|
+ field_groups = [
|
|
|
+ ['time_before', 'time_after', 'action'],
|
|
|
+ ['user_id', 'changed_object_type_id'],
|
|
|
+ ]
|
|
|
time_after = forms.DateTimeField(
|
|
|
required=False,
|
|
|
label=_('After'),
|