Use strings to specify prerequisite models

This commit is contained in:
jeremystretch 2022-11-16 17:22:09 -05:00
parent f411c4f439
commit ebf555e1fb
12 changed files with 61 additions and 86 deletions

View File

@ -104,6 +104,10 @@ class Circuit(PrimaryModel):
clone_fields = ( clone_fields = (
'provider', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate', 'description', 'provider', 'type', 'status', 'tenant', 'install_date', 'termination_date', 'commit_rate', 'description',
) )
prerequisite_models = (
'circuits.CircuitType',
'circuits.Provider',
)
class Meta: class Meta:
ordering = ['provider', 'cid'] ordering = ['provider', 'cid']
@ -117,10 +121,6 @@ class Circuit(PrimaryModel):
def __str__(self): def __str__(self):
return self.cid return self.cid
@classmethod
def get_prerequisite_models(cls):
return [apps.get_model('circuits.Provider'), CircuitType]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('circuits:circuit', args=[self.pk]) return reverse('circuits:circuit', args=[self.pk])

View File

@ -124,6 +124,9 @@ class DeviceType(PrimaryModel, WeightMixin):
clone_fields = ( clone_fields = (
'manufacturer', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit' 'manufacturer', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow', 'weight', 'weight_unit'
) )
prerequisite_models = (
'dcim.Manufacturer',
)
class Meta: class Meta:
ordering = ['manufacturer', 'model'] ordering = ['manufacturer', 'model']
@ -151,10 +154,6 @@ class DeviceType(PrimaryModel, WeightMixin):
self._original_front_image = self.front_image self._original_front_image = self.front_image
self._original_rear_image = self.rear_image self._original_rear_image = self.rear_image
@classmethod
def get_prerequisite_models(cls):
return [Manufacturer, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:devicetype', args=[self.pk]) return reverse('dcim:devicetype', args=[self.pk])
@ -325,6 +324,9 @@ class ModuleType(PrimaryModel, WeightMixin):
) )
clone_fields = ('manufacturer', 'weight', 'weight_unit',) clone_fields = ('manufacturer', 'weight', 'weight_unit',)
prerequisite_models = (
'dcim.Manufacturer',
)
class Meta: class Meta:
ordering = ('manufacturer', 'model') ordering = ('manufacturer', 'model')
@ -338,10 +340,6 @@ class ModuleType(PrimaryModel, WeightMixin):
def __str__(self): def __str__(self):
return self.model return self.model
@classmethod
def get_prerequisite_models(cls):
return [Manufacturer, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:moduletype', args=[self.pk]) return reverse('dcim:moduletype', args=[self.pk])
@ -599,6 +597,11 @@ class Device(PrimaryModel, ConfigContextModel):
'device_type', 'device_role', 'tenant', 'platform', 'site', 'location', 'rack', 'face', 'status', 'airflow', 'device_type', 'device_role', 'tenant', 'platform', 'site', 'location', 'rack', 'face', 'status', 'airflow',
'cluster', 'virtual_chassis', 'cluster', 'virtual_chassis',
) )
prerequisite_models = (
'dcim.Site',
'dcim.DeviceRole',
'dcim.DeviceType',
)
class Meta: class Meta:
ordering = ('_name', 'pk') # Name may be null ordering = ('_name', 'pk') # Name may be null
@ -638,10 +641,6 @@ class Device(PrimaryModel, ConfigContextModel):
return f'{self.device_type.manufacturer} {self.device_type.model} ({self.pk})' return f'{self.device_type.manufacturer} {self.device_type.model} ({self.pk})'
return super().__str__() return super().__str__()
@classmethod
def get_prerequisite_models(cls):
return [apps.get_model('dcim.Site'), DeviceRole, DeviceType, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:device', args=[self.pk]) return reverse('dcim:device', args=[self.pk])

View File

@ -1,4 +1,3 @@
from django.apps import apps
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
@ -48,6 +47,10 @@ class PowerPanel(PrimaryModel):
to='extras.ImageAttachment' to='extras.ImageAttachment'
) )
prerequisite_models = (
'dcim.Site',
)
class Meta: class Meta:
ordering = ['site', 'name'] ordering = ['site', 'name']
constraints = ( constraints = (
@ -60,10 +63,6 @@ class PowerPanel(PrimaryModel):
def __str__(self): def __str__(self):
return self.name return self.name
@classmethod
def get_prerequisite_models(cls):
return [apps.get_model('dcim.Site'), ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:powerpanel', args=[self.pk]) return reverse('dcim:powerpanel', args=[self.pk])
@ -137,6 +136,9 @@ class PowerFeed(PrimaryModel, PathEndpoint, CabledObjectModel):
'power_panel', 'rack', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage', 'power_panel', 'rack', 'status', 'type', 'mark_connected', 'supply', 'phase', 'voltage', 'amperage',
'max_utilization', 'max_utilization',
) )
prerequisite_models = (
'dcim.PowerPanel',
)
class Meta: class Meta:
ordering = ['power_panel', 'name'] ordering = ['power_panel', 'name']
@ -150,10 +152,6 @@ class PowerFeed(PrimaryModel, PathEndpoint, CabledObjectModel):
def __str__(self): def __str__(self):
return self.name return self.name
@classmethod
def get_prerequisite_models(cls):
return [PowerPanel, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:powerfeed', args=[self.pk]) return reverse('dcim:powerfeed', args=[self.pk])

View File

@ -1,7 +1,6 @@
import decimal import decimal
from functools import cached_property from functools import cached_property
from django.apps import apps
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
@ -177,6 +176,9 @@ class Rack(PrimaryModel, WeightMixin):
'site', 'location', 'tenant', 'status', 'role', 'type', 'width', 'u_height', 'desc_units', 'outer_width', 'site', 'location', 'tenant', 'status', 'role', 'type', 'width', 'u_height', 'desc_units', 'outer_width',
'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'weight_unit', 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'weight_unit',
) )
prerequisite_models = (
'dcim.Site',
)
class Meta: class Meta:
ordering = ('site', 'location', '_name', 'pk') # (site, location, name) may be non-unique ordering = ('site', 'location', '_name', 'pk') # (site, location, name) may be non-unique
@ -197,10 +199,6 @@ class Rack(PrimaryModel, WeightMixin):
return f'{self.name} ({self.facility_id})' return f'{self.name} ({self.facility_id})'
return self.name return self.name
@classmethod
def get_prerequisite_models(cls):
return [apps.get_model('dcim.Site'), ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:rack', args=[self.pk]) return reverse('dcim:rack', args=[self.pk])
@ -488,16 +486,16 @@ class RackReservation(PrimaryModel):
max_length=200 max_length=200
) )
prerequisite_models = (
'dcim.Rack',
)
class Meta: class Meta:
ordering = ['created', 'pk'] ordering = ['created', 'pk']
def __str__(self): def __str__(self):
return "Reservation for rack {}".format(self.rack) return "Reservation for rack {}".format(self.rack)
@classmethod
def get_prerequisite_models(cls):
return [apps.get_model('dcim.Site'), Rack, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:rackreservation', args=[self.pk]) return reverse('dcim:rackreservation', args=[self.pk])

View File

@ -286,6 +286,9 @@ class Location(NestedGroupModel):
) )
clone_fields = ('site', 'parent', 'status', 'tenant', 'description') clone_fields = ('site', 'parent', 'status', 'tenant', 'description')
prerequisite_models = (
'dcim.Site',
)
class Meta: class Meta:
ordering = ['site', 'name'] ordering = ['site', 'name']
@ -312,10 +315,6 @@ class Location(NestedGroupModel):
), ),
) )
@classmethod
def get_prerequisite_models(cls):
return [Site, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('dcim:location', args=[self.pk]) return reverse('dcim:location', args=[self.pk])

View File

@ -10,7 +10,6 @@ from django.utils.translation import gettext as _
from dcim.fields import ASNField from dcim.fields import ASNField
from dcim.models import Device from dcim.models import Device
from netbox.models import OrganizationalModel, PrimaryModel
from ipam.choices import * from ipam.choices import *
from ipam.constants import * from ipam.constants import *
from ipam.fields import IPNetworkField, IPAddressField from ipam.fields import IPNetworkField, IPAddressField
@ -18,9 +17,9 @@ from ipam.managers import IPAddressManager
from ipam.querysets import PrefixQuerySet from ipam.querysets import PrefixQuerySet
from ipam.validators import DNSValidator from ipam.validators import DNSValidator
from netbox.config import get_config from netbox.config import get_config
from netbox.models import OrganizationalModel, PrimaryModel
from virtualization.models import VirtualMachine from virtualization.models import VirtualMachine
__all__ = ( __all__ = (
'Aggregate', 'Aggregate',
'ASN', 'ASN',
@ -101,6 +100,10 @@ class ASN(PrimaryModel):
null=True null=True
) )
prerequisite_models = (
'ipam.RIR',
)
class Meta: class Meta:
ordering = ['asn'] ordering = ['asn']
verbose_name = 'ASN' verbose_name = 'ASN'
@ -109,10 +112,6 @@ class ASN(PrimaryModel):
def __str__(self): def __str__(self):
return f'AS{self.asn_with_asdot}' return f'AS{self.asn_with_asdot}'
@classmethod
def get_prerequisite_models(cls):
return [RIR, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('ipam:asn', args=[self.pk]) return reverse('ipam:asn', args=[self.pk])
@ -163,6 +162,9 @@ class Aggregate(GetAvailablePrefixesMixin, PrimaryModel):
clone_fields = ( clone_fields = (
'rir', 'tenant', 'date_added', 'description', 'rir', 'tenant', 'date_added', 'description',
) )
prerequisite_models = (
'ipam.RIR',
)
class Meta: class Meta:
ordering = ('prefix', 'pk') # prefix may be non-unique ordering = ('prefix', 'pk') # prefix may be non-unique
@ -170,10 +172,6 @@ class Aggregate(GetAvailablePrefixesMixin, PrimaryModel):
def __str__(self): def __str__(self):
return str(self.prefix) return str(self.prefix)
@classmethod
def get_prerequisite_models(cls):
return [RIR, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('ipam:aggregate', args=[self.pk]) return reverse('ipam:aggregate', args=[self.pk])

View File

@ -1,4 +1,3 @@
from django.apps import apps
from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -95,6 +94,9 @@ class L2VPNTermination(NetBoxModel):
) )
clone_fields = ('l2vpn',) clone_fields = ('l2vpn',)
prerequisite_models = (
'ipam.L2VPN',
)
class Meta: class Meta:
ordering = ('l2vpn',) ordering = ('l2vpn',)
@ -111,10 +113,6 @@ class L2VPNTermination(NetBoxModel):
return f'{self.assigned_object} <> {self.l2vpn}' return f'{self.assigned_object} <> {self.l2vpn}'
return super().__str__() return super().__str__()
@classmethod
def get_prerequisite_models(cls):
return [apps.get_model('ipam.L2VPN'), ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('ipam:l2vpntermination', args=[self.pk]) return reverse('ipam:l2vpntermination', args=[self.pk])

View File

@ -3,9 +3,9 @@ from django.core.validators import ValidationError
from django.db import models from django.db import models
from mptt.models import MPTTModel, TreeForeignKey from mptt.models import MPTTModel, TreeForeignKey
from netbox.models.features import *
from utilities.mptt import TreeManager from utilities.mptt import TreeManager
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
from netbox.models.features import *
__all__ = ( __all__ = (
'ChangeLoggedModel', 'ChangeLoggedModel',
@ -33,14 +33,6 @@ class NetBoxFeatureSet(
def docs_url(self): def docs_url(self):
return f'{settings.STATIC_URL}docs/models/{self._meta.app_label}/{self._meta.model_name}/' return f'{settings.STATIC_URL}docs/models/{self._meta.app_label}/{self._meta.model_name}/'
@classmethod
def get_prerequisite_models(cls):
"""
Return a list of model types that are required to create this model or empty list if none. This is used for
showing prerequisite warnings in the UI on the list and detail views.
"""
return []
# #
# Base model classes # Base model classes

View File

@ -1,12 +1,13 @@
from django.apps import apps
def get_prerequisite_model(queryset): def get_prerequisite_model(queryset):
model = queryset.model """
Return any prerequisite model that must be created prior to creating
an instance of the current model.
"""
if not queryset.exists(): if not queryset.exists():
if hasattr(model, 'get_prerequisite_models'): for prereq in getattr(queryset.model, 'prerequisite_models', []):
prerequisites = model.get_prerequisite_models() model = apps.get_model(prereq)
if prerequisites: if not model.objects.exists():
for prereq in prerequisites: return model
if not prereq.objects.exists():
return prereq
return None

View File

@ -94,6 +94,9 @@ class Cluster(PrimaryModel):
clone_fields = ( clone_fields = (
'type', 'group', 'status', 'tenant', 'site', 'type', 'group', 'status', 'tenant', 'site',
) )
prerequisite_models = (
'virtualization.ClusterType',
)
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
@ -111,10 +114,6 @@ class Cluster(PrimaryModel):
def __str__(self): def __str__(self):
return self.name return self.name
@classmethod
def get_prerequisite_models(cls):
return [ClusterType, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('virtualization:cluster', args=[self.pk]) return reverse('virtualization:cluster', args=[self.pk])

View File

@ -15,7 +15,6 @@ from utilities.fields import NaturalOrderingField
from utilities.ordering import naturalize_interface from utilities.ordering import naturalize_interface
from utilities.query_functions import CollateAsChar from utilities.query_functions import CollateAsChar
from virtualization.choices import * from virtualization.choices import *
from .clusters import Cluster
__all__ = ( __all__ = (
'VirtualMachine', 'VirtualMachine',
@ -131,6 +130,9 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
clone_fields = ( clone_fields = (
'site', 'cluster', 'device', 'tenant', 'platform', 'status', 'role', 'vcpus', 'memory', 'disk', 'site', 'cluster', 'device', 'tenant', 'platform', 'status', 'role', 'vcpus', 'memory', 'disk',
) )
prerequisite_models = (
'virtualization.Cluster',
)
class Meta: class Meta:
ordering = ('_name', 'pk') # Name may be non-unique ordering = ('_name', 'pk') # Name may be non-unique
@ -150,10 +152,6 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
def __str__(self): def __str__(self):
return self.name return self.name
@classmethod
def get_prerequisite_models(cls):
return [Cluster, ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('virtualization:virtualmachine', args=[self.pk]) return reverse('virtualization:virtualmachine', args=[self.pk])

View File

@ -1,4 +1,3 @@
from django.apps import apps
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
@ -193,10 +192,6 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
def __str__(self): def __str__(self):
return f'#{self.pk}' return f'#{self.pk}'
@classmethod
def get_prerequisite_models(cls):
return [apps.get_model('dcim.Interface'), ]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('wireless:wirelesslink', args=[self.pk]) return reverse('wireless:wirelesslink', args=[self.pk])