7699 refactor mixins

This commit is contained in:
Arthur Hanson 2024-10-30 11:53:51 -07:00
parent 0c7710ddb6
commit 5c022f0ae2
9 changed files with 94 additions and 83 deletions

View File

@ -123,3 +123,8 @@ COMPATIBLE_TERMINATION_TYPES = {
'powerport': ['poweroutlet', 'powerfeed'],
'rearport': ['consoleport', 'consoleserverport', 'interface', 'frontport', 'rearport', 'circuittermination'],
}
# models values for ContentTypes which may be Cluster scope types
LOCATION_SCOPE_TYPES = (
'region', 'sitegroup', 'site', 'location',
)

View File

@ -2,15 +2,36 @@ from django import forms
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext_lazy as _
from dcim.constants import LOCATION_SCOPE_TYPES
from dcim.models import Site
from utilities.forms import get_field_value
from utilities.forms.fields import (
ContentTypeChoiceField, CSVContentTypeField, DynamicModelChoiceField,
)
from utilities.templatetags.builtins.filters import bettertitle
from utilities.forms.widgets import HTMXSelect
__all__ = (
'ScopedBulkEditForm',
'ScopedForm',
)
class ScopedForm(forms.Form):
scope_type = ContentTypeChoiceField(
queryset=ContentType.objects.filter(model__in=LOCATION_SCOPE_TYPES),
widget=HTMXSelect(),
required=False,
label=_('Scope type')
)
scope = DynamicModelChoiceField(
label=_('Scope'),
queryset=Site.objects.none(), # Initial queryset
required=False,
disabled=True,
selector=True
)
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance')
@ -43,3 +64,40 @@ class ScopedForm(forms.Form):
if self.instance and scope_type_id != self.instance.scope_type_id:
self.initial['scope'] = None
class ScopedBulkEditForm(forms.Form):
scope_type = ContentTypeChoiceField(
queryset=ContentType.objects.filter(model__in=LOCATION_SCOPE_TYPES),
widget=HTMXSelect(method='post', attrs={'hx-select': '#form_fields'}),
required=False,
label=_('Scope type')
)
scope = DynamicModelChoiceField(
label=_('Scope'),
queryset=Site.objects.none(), # Initial queryset
required=False,
disabled=True,
selector=True
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if scope_type_id := get_field_value(self, 'scope_type'):
try:
scope_type = ContentType.objects.get(pk=scope_type_id)
model = scope_type.model_class()
self.fields['scope'].queryset = model.objects.all()
self.fields['scope'].widget.attrs['selector'] = model._meta.label_lower
self.fields['scope'].disabled = False
self.fields['scope'].label = _(bettertitle(model._meta.verbose_name))
except ObjectDoesNotExist:
pass
class ScopedImportForm(forms.Form):
scope_type = CSVContentTypeField(
queryset=ContentType.objects.filter(model__in=LOCATION_SCOPE_TYPES),
required=False,
label=_('Scope type (app & model)')
)

View File

@ -1,5 +1,7 @@
from django.apps import apps
from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models
from dcim.constants import LOCATION_SCOPE_TYPES
__all__ = (
'CachedScopeMixin',
@ -33,8 +35,25 @@ class RenderConfigMixin(models.Model):
class CachedScopeMixin(models.Model):
"""
Cached associations for scope to enable efficient filtering - must define scope and scope_type on model
Cached associations for scope to enable efficient filtering
"""
scope_type = models.ForeignKey(
to='contenttypes.ContentType',
on_delete=models.PROTECT,
limit_choices_to=models.Q(model__in=LOCATION_SCOPE_TYPES),
related_name='+',
blank=True,
null=True
)
scope_id = models.PositiveBigIntegerField(
blank=True,
null=True
)
scope = GenericForeignKey(
ct_field='scope_type',
fk_field='scope_id'
)
_location = models.ForeignKey(
to='dcim.Location',
on_delete=models.CASCADE,

View File

@ -1,3 +1,4 @@
from dcim.constants import LOCATION_SCOPE_TYPES
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
@ -5,7 +6,6 @@ from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountF
from netbox.api.serializers import NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer
from virtualization.choices import *
from virtualization.constants import CLUSTER_SCOPE_TYPES
from virtualization.models import Cluster, ClusterGroup, ClusterType
from utilities.api import get_serializer_for_model
@ -51,7 +51,7 @@ class ClusterSerializer(NetBoxModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
scope_type = ContentTypeField(
queryset=ContentType.objects.filter(
model__in=CLUSTER_SCOPE_TYPES
model__in=LOCATION_SCOPE_TYPES
),
allow_null=True,
required=False,

View File

@ -1,4 +0,0 @@
# models values for ContentTypes which may be Cluster scope types
CLUSTER_SCOPE_TYPES = (
'region', 'sitegroup', 'site', 'location',
)

View File

@ -1,20 +1,19 @@
from django import forms
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _
from dcim.choices import InterfaceModeChoices
from dcim.constants import INTERFACE_MTU_MAX, INTERFACE_MTU_MIN
from dcim.forms.mixins import ScopedBulkEditForm
from dcim.models import Device, DeviceRole, Platform, Site
from extras.models import ConfigTemplate
from ipam.models import VLAN, VLANGroup, VRF
from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import BulkRenameForm, add_blank_choice, get_field_value
from utilities.forms.fields import CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
from utilities.forms import BulkRenameForm, add_blank_choice
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect, HTMXSelect
from utilities.forms.widgets import BulkEditNullBooleanSelect
from virtualization.choices import *
from virtualization.constants import CLUSTER_SCOPE_TYPES
from virtualization.models import *
__all__ = (
@ -57,7 +56,7 @@ class ClusterGroupBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('description',)
class ClusterBulkEditForm(NetBoxModelBulkEditForm):
class ClusterBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
type = DynamicModelChoiceField(
label=_('Type'),
queryset=ClusterType.objects.all(),
@ -79,19 +78,6 @@ class ClusterBulkEditForm(NetBoxModelBulkEditForm):
queryset=Tenant.objects.all(),
required=False
)
scope_type = ContentTypeChoiceField(
queryset=ContentType.objects.filter(model__in=CLUSTER_SCOPE_TYPES),
widget=HTMXSelect(method='post', attrs={'hx-select': '#form_fields'}),
required=False,
label=_('Scope type')
)
scope = DynamicModelChoiceField(
label=_('Scope'),
queryset=Site.objects.none(), # Initial queryset
required=False,
disabled=True,
selector=True
)
description = forms.CharField(
label=_('Description'),
max_length=200,
@ -109,21 +95,6 @@ class ClusterBulkEditForm(NetBoxModelBulkEditForm):
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if scope_type_id := get_field_value(self, 'scope_type'):
try:
scope_type = ContentType.objects.get(pk=scope_type_id)
model = scope_type.model_class()
self.fields['scope'].queryset = model.objects.all()
self.fields['scope'].widget.attrs['selector'] = model._meta.label_lower
self.fields['scope'].disabled = False
self.fields['scope'].label = _(bettertitle(model._meta.verbose_name))
except ObjectDoesNotExist:
pass
class VirtualMachineBulkEditForm(NetBoxModelBulkEditForm):
status = forms.ChoiceField(
label=_('Status'),

View File

@ -1,15 +1,14 @@
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _
from dcim.choices import InterfaceModeChoices
from dcim.forms.mixins import ScopedImportForm
from dcim.models import Device, DeviceRole, Platform, Site
from extras.models import ConfigTemplate
from ipam.models import VRF
from netbox.forms import NetBoxModelImportForm
from tenancy.models import Tenant
from utilities.forms.fields import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
from virtualization.choices import *
from virtualization.constants import CLUSTER_SCOPE_TYPES
from virtualization.models import *
__all__ = (
@ -38,7 +37,7 @@ class ClusterGroupImportForm(NetBoxModelImportForm):
fields = ('name', 'slug', 'description', 'tags')
class ClusterImportForm(NetBoxModelImportForm):
class ClusterImportForm(ScopedImportForm, NetBoxModelImportForm):
type = CSVModelChoiceField(
label=_('Type'),
queryset=ClusterType.objects.all(),
@ -57,11 +56,6 @@ class ClusterImportForm(NetBoxModelImportForm):
choices=ClusterStatusChoices,
help_text=_('Operational status')
)
scope_type = CSVContentTypeField(
queryset=ContentType.objects.filter(model__in=CLUSTER_SCOPE_TYPES),
required=False,
label=_('Scope type (app & model)')
)
site = CSVModelChoiceField(
label=_('Site'),
queryset=Site.objects.all(),

View File

@ -14,10 +14,8 @@ from utilities.forms import ConfirmationForm
from utilities.forms.fields import (
CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, SlugField,
)
from utilities.forms.fields import ContentTypeChoiceField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import HTMXSelect
from virtualization.constants import CLUSTER_SCOPE_TYPES
from virtualization.models import *
__all__ = (
@ -70,19 +68,6 @@ class ClusterForm(TenancyForm, ScopedForm, NetBoxModelForm):
queryset=ClusterGroup.objects.all(),
required=False
)
scope_type = ContentTypeChoiceField(
queryset=ContentType.objects.filter(model__in=CLUSTER_SCOPE_TYPES),
widget=HTMXSelect(),
required=False,
label=_('Scope type')
)
scope = DynamicModelChoiceField(
label=_('Scope'),
queryset=Site.objects.none(), # Initial queryset
required=False,
disabled=True,
selector=True
)
comments = CommentField()
fieldsets = (

View File

@ -1,5 +1,5 @@
from django.apps import apps
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
@ -9,7 +9,6 @@ from dcim.models.mixins import CachedScopeMixin
from netbox.models import OrganizationalModel, PrimaryModel
from netbox.models.features import ContactsMixin
from virtualization.choices import *
from virtualization.constants import CLUSTER_SCOPE_TYPES
__all__ = (
'Cluster',
@ -79,22 +78,6 @@ class Cluster(ContactsMixin, CachedScopeMixin, PrimaryModel):
blank=True,
null=True
)
scope_type = models.ForeignKey(
to='contenttypes.ContentType',
on_delete=models.PROTECT,
limit_choices_to=models.Q(model__in=CLUSTER_SCOPE_TYPES),
related_name='+',
blank=True,
null=True
)
scope_id = models.PositiveBigIntegerField(
blank=True,
null=True
)
scope = GenericForeignKey(
ct_field='scope_type',
fk_field='scope_id'
)
# Generic relations
vlan_groups = GenericRelation(