1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Closes #4837: Use dynamic form widget for relationships to MPTT objects

This commit is contained in:
Jeremy Stretch
2020-07-09 09:50:01 -04:00
parent b535608519
commit 15525392a2
10 changed files with 28 additions and 37 deletions

View File

@ -25,6 +25,7 @@ When running a report or custom script, the task is now queued for background pr
* [#4806](https://github.com/netbox-community/netbox/issues/4806) - Add a `url` field to all API serializers
* [#4807](https://github.com/netbox-community/netbox/issues/4807) - Add bulk edit ability for device bay templates
* [#4817](https://github.com/netbox-community/netbox/issues/4817) - Standardize device/VM component `name` field to 64 characters
* [#4837](https://github.com/netbox-community/netbox/issues/4837) - Use dynamic form widget for relationships to MPTT objects (e.g. regions)
### Configuration Changes
@ -52,6 +53,7 @@ When running a report or custom script, the task is now queued for background pr
* extras.Report: The `failed` field has been removed. The `completed` (boolean) and `status` (string) fields have been introduced to convey the status of a report's most recent execution. Additionally, the `result` field now conveys the nested representation of a JobResult.
* extras.Script: Added `module` and `result` fields. The `result` field now conveys the nested representation of a JobResult.
* A `url` field is now included on all object representations, identifying the unique REST API URL for each object.
* A `_depth` field has been added to all objects which feature a self-recursive hierarchy (namely regions, rack groups, and tenant groups).
### Other Changes

View File

@ -47,10 +47,11 @@ __all__ = [
class NestedRegionSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
site_count = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(source='level', read_only=True)
class Meta:
model = models.Region
fields = ['id', 'url', 'name', 'slug', 'site_count']
fields = ['id', 'url', 'name', 'slug', 'site_count', '_depth']
class NestedSiteSerializer(WritableNestedSerializer):
@ -68,10 +69,11 @@ class NestedSiteSerializer(WritableNestedSerializer):
class NestedRackGroupSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:rackgroup-detail')
rack_count = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(source='level', read_only=True)
class Meta:
model = models.RackGroup
fields = ['id', 'url', 'name', 'slug', 'rack_count']
fields = ['id', 'url', 'name', 'slug', 'rack_count', '_depth']
class NestedRackRoleSerializer(WritableNestedSerializer):

View File

@ -63,10 +63,11 @@ class RegionSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:region-detail')
parent = NestedRegionSerializer(required=False, allow_null=True)
site_count = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(source='level', read_only=True)
class Meta:
model = Region
fields = ['id', 'url', 'name', 'slug', 'parent', 'description', 'site_count']
fields = ['id', 'url', 'name', 'slug', 'parent', 'description', 'site_count', '_depth']
class SiteSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
@ -101,10 +102,11 @@ class RackGroupSerializer(ValidatedModelSerializer):
site = NestedSiteSerializer()
parent = NestedRackGroupSerializer(required=False, allow_null=True)
rack_count = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(source='level', read_only=True)
class Meta:
model = RackGroup
fields = ['id', 'url', 'name', 'slug', 'site', 'parent', 'description', 'rack_count']
fields = ['id', 'url', 'name', 'slug', 'site', 'parent', 'description', 'rack_count', '_depth']
class RackRoleSerializer(ValidatedModelSerializer):

View File

@ -6,7 +6,6 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.forms.array import SimpleArrayField
from django.core.exceptions import ObjectDoesNotExist
from django.utils.safestring import mark_safe
from mptt.forms import TreeNodeChoiceField
from netaddr import EUI
from netaddr.core import AddrFormatError
from timezone_field import TimeZoneFormField
@ -179,10 +178,9 @@ class MACAddressField(forms.Field):
#
class RegionForm(BootstrapMixin, forms.ModelForm):
parent = TreeNodeChoiceField(
parent = DynamicModelChoiceField(
queryset=Region.objects.all(),
required=False,
widget=StaticSelect2()
required=False
)
slug = SlugField()
@ -219,10 +217,9 @@ class RegionFilterForm(BootstrapMixin, forms.Form):
#
class SiteForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
region = TreeNodeChoiceField(
region = DynamicModelChoiceField(
queryset=Region.objects.all(),
required=False,
widget=StaticSelect2()
required=False
)
slug = SlugField()
comments = CommentField()
@ -305,10 +302,9 @@ class SiteBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
initial='',
widget=StaticSelect2()
)
region = TreeNodeChoiceField(
region = DynamicModelChoiceField(
queryset=Region.objects.all(),
required=False,
widget=StaticSelect2()
required=False
)
tenant = DynamicModelChoiceField(
queryset=Tenant.objects.all(),

View File

@ -2,14 +2,13 @@ from django import forms
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.utils.safestring import mark_safe
from mptt.forms import TreeNodeMultipleChoiceField
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,
ContentTypeSelect, CSVModelForm, DateTimePicker, DynamicModelMultipleChoiceField, JSONField, SlugField,
StaticSelect2, StaticSelect2Multiple, BOOLEAN_WITH_BLANK_CHOICES,
StaticSelect2, BOOLEAN_WITH_BLANK_CHOICES,
)
from virtualization.models import Cluster, ClusterGroup
from .choices import *
@ -211,10 +210,9 @@ class TagBulkEditForm(BootstrapMixin, BulkEditForm):
#
class ConfigContextForm(BootstrapMixin, forms.ModelForm):
regions = TreeNodeMultipleChoiceField(
regions = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
required=False,
widget=StaticSelect2Multiple()
required=False
)
sites = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(),

View File

@ -3,7 +3,6 @@ import json
import logging
import os
import pkgutil
import time
import traceback
from collections import OrderedDict
@ -12,11 +11,8 @@ from django import forms
from django.conf import settings
from django.core.validators import RegexValidator
from django.db import transaction
from django.utils import timezone
from django.utils.decorators import classproperty
from django_rq import job
from mptt.forms import TreeNodeChoiceField, TreeNodeMultipleChoiceField
from mptt.models import MPTTModel
from extras.api.serializers import ScriptOutputSerializer
from extras.choices import JobResultStatusChoices, LogLevelChoices
@ -182,10 +178,6 @@ class ObjectVar(ScriptVariable):
# Queryset for field choices
self.field_attrs['queryset'] = queryset
# Update form field for MPTT (nested) objects
if issubclass(queryset.model, MPTTModel):
self.form_field = TreeNodeChoiceField
class MultiObjectVar(ScriptVariable):
"""
@ -199,10 +191,6 @@ class MultiObjectVar(ScriptVariable):
# Queryset for field choices
self.field_attrs['queryset'] = queryset
# Update form field for MPTT (nested) objects
if issubclass(queryset.model, MPTTModel):
self.form_field = TreeNodeMultipleChoiceField
class FileVar(ScriptVariable):
"""

View File

@ -222,6 +222,10 @@ $(document).ready(function() {
results = results.reduce((results,record,idx) => {
record.text = record[element.getAttribute('display-field')] || record.name;
if (record._depth) {
// Annotate hierarchical depth for MPTT objects
record.text = '--'.repeat(record._depth) + ' ' + record.text;
}
record.id = record[element.getAttribute('value-field')] || record.id;
if(element.getAttribute('disabled-indicator') && record[element.getAttribute('disabled-indicator')]) {
// The disabled-indicator equated to true, so we disable this option

View File

@ -16,10 +16,11 @@ __all__ = [
class NestedTenantGroupSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
tenant_count = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(source='level', read_only=True)
class Meta:
model = TenantGroup
fields = ['id', 'url', 'name', 'slug', 'tenant_count']
fields = ['id', 'url', 'name', 'slug', 'tenant_count', '_depth']
class NestedTenantSerializer(WritableNestedSerializer):

View File

@ -15,10 +15,11 @@ class TenantGroupSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenantgroup-detail')
parent = NestedTenantGroupSerializer(required=False, allow_null=True)
tenant_count = serializers.IntegerField(read_only=True)
_depth = serializers.IntegerField(source='level', read_only=True)
class Meta:
model = TenantGroup
fields = ['id', 'url', 'name', 'slug', 'parent', 'description', 'tenant_count']
fields = ['id', 'url', 'name', 'slug', 'parent', 'description', 'tenant_count', '_depth']
class TenantSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):

View File

@ -18,10 +18,7 @@ from .models import Tenant, TenantGroup
class TenantGroupForm(BootstrapMixin, forms.ModelForm):
parent = DynamicModelChoiceField(
queryset=TenantGroup.objects.all(),
required=False,
widget=APISelect(
api_url="/api/tenancy/tenant-groups/"
)
required=False
)
slug = SlugField()