mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-29 11:56:25 -06:00
7699 refactor mixins
This commit is contained in:
parent
0c7710ddb6
commit
5c022f0ae2
@ -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',
|
||||
)
|
||||
|
@ -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)')
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -1,4 +0,0 @@
|
||||
# models values for ContentTypes which may be Cluster scope types
|
||||
CLUSTER_SCOPE_TYPES = (
|
||||
'region', 'sitegroup', 'site', 'location',
|
||||
)
|
@ -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'),
|
||||
|
@ -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(),
|
||||
|
@ -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 = (
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user