From 585ea71d1a5deed877ea42ad63faa4e940013e36 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 29 Jan 2020 11:25:51 -0500 Subject: [PATCH] Move form field generation logic to CustomField class --- netbox/extras/forms.py | 63 ++------------------------------------- netbox/extras/models.py | 66 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 60 deletions(-) diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index bb1015638..674c3ff4c 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -3,15 +3,14 @@ from collections import OrderedDict from django import forms from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ObjectDoesNotExist from taggit.forms import TagField from dcim.models import DeviceRole, Platform, Region, Site from tenancy.models import Tenant, TenantGroup from utilities.forms import ( add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect, - CommentField, ContentTypeSelect, DatePicker, DateTimePicker, FilterChoiceField, LaxURLField, JSONField, SlugField, - StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES, + CommentField, ContentTypeSelect, DateTimePicker, FilterChoiceField, JSONField, SlugField, StaticSelect2, + BOOLEAN_WITH_BLANK_CHOICES, ) from .choices import * from .models import ConfigContext, CustomField, CustomFieldValue, ImageAttachment, ObjectChange, Tag @@ -32,63 +31,7 @@ def get_custom_fields_for_model(content_type, filterable_only=False, bulk_edit=F for cf in custom_fields: field_name = 'cf_{}'.format(str(cf.name)) - initial = cf.default if not bulk_edit else None - - # Integer - if cf.type == CustomFieldTypeChoices.TYPE_INTEGER: - field = forms.IntegerField(required=cf.required, initial=initial) - - # Boolean - elif cf.type == CustomFieldTypeChoices.TYPE_BOOLEAN: - choices = ( - (None, '---------'), - (1, 'True'), - (0, 'False'), - ) - if initial is not None and initial.lower() in ['true', 'yes', '1']: - initial = 1 - elif initial is not None and initial.lower() in ['false', 'no', '0']: - initial = 0 - else: - initial = None - field = forms.NullBooleanField( - required=cf.required, initial=initial, widget=StaticSelect2(choices=choices) - ) - - # Date - elif cf.type == CustomFieldTypeChoices.TYPE_DATE: - field = forms.DateField(required=cf.required, initial=initial, widget=DatePicker()) - - # Select - elif cf.type == CustomFieldTypeChoices.TYPE_SELECT: - choices = [(cfc.pk, cfc.value) for cfc in cf.choices.all()] - if not cf.required or bulk_edit or filterable_only: - choices = [(None, '---------')] + choices - # Check for a default choice - default_choice = None - if initial: - try: - default_choice = cf.choices.get(value=initial).pk - except ObjectDoesNotExist: - pass - field = forms.TypedChoiceField( - choices=choices, coerce=int, required=cf.required, initial=default_choice, widget=StaticSelect2() - ) - - # URL - elif cf.type == CustomFieldTypeChoices.TYPE_URL: - field = LaxURLField(required=cf.required, initial=initial) - - # Text - else: - field = forms.CharField(max_length=255, required=cf.required, initial=initial) - - field.model = cf - field.label = cf.label if cf.label else cf.name.replace('_', ' ').capitalize() - if cf.description: - field.help_text = cf.description - - field_dict[field_name] = field + field_dict[field_name] = cf.to_form_field(set_initial=not bulk_edit) return field_dict diff --git a/netbox/extras/models.py b/netbox/extras/models.py index a03494bb2..be8feda7b 100644 --- a/netbox/extras/models.py +++ b/netbox/extras/models.py @@ -1,6 +1,7 @@ from collections import OrderedDict from datetime import date +from django import forms from django.contrib.auth.models import User from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType @@ -14,6 +15,7 @@ from django.utils.text import slugify from taggit.models import TagBase, GenericTaggedItemBase from utilities.fields import ColorField +from utilities.forms import DatePicker, LaxURLField, StaticSelect2, add_blank_choice from utilities.utils import deepmerge, render_jinja2 from .choices import * from .constants import * @@ -280,6 +282,70 @@ class CustomField(models.Model): return self.choices.get(pk=int(serialized_value)) return serialized_value + def to_form_field(self, set_initial=True): + """ + Return a form field suitable for setting a CustomField's value for an object. + + set_initial: Set initial date for the field. This should be false when generating a field for bulk editing. + """ + initial = self.default if set_initial else None + + # Integer + if self.type == CustomFieldTypeChoices.TYPE_INTEGER: + field = forms.IntegerField(required=self.required, initial=initial) + + # Boolean + elif self.type == CustomFieldTypeChoices.TYPE_BOOLEAN: + choices = ( + (None, '---------'), + (1, 'True'), + (0, 'False'), + ) + if initial is not None and initial.lower() in ['true', 'yes', '1']: + initial = 1 + elif initial is not None and initial.lower() in ['false', 'no', '0']: + initial = 0 + else: + initial = None + field = forms.NullBooleanField( + required=self.required, initial=initial, widget=StaticSelect2(choices=choices) + ) + + # Date + elif self.type == CustomFieldTypeChoices.TYPE_DATE: + field = forms.DateField(required=self.required, initial=initial, widget=DatePicker()) + + # Select + elif self.type == CustomFieldTypeChoices.TYPE_SELECT: + choices = [(cfc.pk, cfc.value) for cfc in self.choices.all()] + # TODO: Accommodate bulk edit/filtering + # if not self.required or bulk_edit or filterable_only: + if not self.required: + choices = add_blank_choice(choices) + # Set the initial value to the PK of the default choice, if any + if set_initial: + default_choice = self.choices.filter(value=self.default).first() + if default_choice: + initial = default_choice.pk + field = forms.TypedChoiceField( + choices=choices, coerce=int, required=self.required, initial=initial, widget=StaticSelect2() + ) + + # URL + elif self.type == CustomFieldTypeChoices.TYPE_URL: + field = LaxURLField(required=self.required, initial=initial) + + # Text + else: + field = forms.CharField(max_length=255, required=self.required, initial=initial) + + field.model = self + field.label = self.label if self.label else self.name.replace('_', ' ').capitalize() + if self.description: + field.help_text = self.description + + return field + class CustomFieldValue(models.Model): field = models.ForeignKey(