diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index 02ba5209d..c14e365e1 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -1,3 +1,4 @@ +from django.apps import apps from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.db import models @@ -129,6 +130,10 @@ class Circuit(NetBoxModel): def __str__(self): return self.cid + @classmethod + def get_prerequisite_models(cls): + return [apps.get_model('circuits.Provider'), CircuitType] + def get_absolute_url(self): return reverse('circuits:circuit', args=[self.pk]) diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index 91227f1cf..8d524dcb1 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -1,4 +1,6 @@ import yaml + +from django.apps import apps from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator @@ -155,6 +157,10 @@ class DeviceType(NetBoxModel): self._original_front_image = self.front_image self._original_rear_image = self.rear_image + @classmethod + def get_prerequisite_models(cls): + return [Manufacturer, ] + def get_absolute_url(self): return reverse('dcim:devicetype', args=[self.pk]) @@ -328,6 +334,10 @@ class ModuleType(NetBoxModel): def __str__(self): return self.model + @classmethod + def get_prerequisite_models(cls): + return [Manufacturer, ] + def get_absolute_url(self): return reverse('dcim:moduletype', args=[self.pk]) @@ -645,6 +655,10 @@ class Device(NetBoxModel, ConfigContextModel): return f'{self.device_type.manufacturer} {self.device_type.model} ({self.pk})' return super().__str__() + @classmethod + def get_prerequisite_models(cls): + return [apps.get_model('dcim.Site'), DeviceRole, DeviceType, ] + def get_absolute_url(self): return reverse('dcim:device', args=[self.pk]) diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index 5978d86bd..5e355ce42 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -1,3 +1,4 @@ +from django.apps import apps from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator @@ -54,6 +55,10 @@ class PowerPanel(NetBoxModel): def __str__(self): return self.name + @classmethod + def get_prerequisite_models(cls): + return [apps.get_model('dcim.Site'), ] + def get_absolute_url(self): return reverse('dcim:powerpanel', args=[self.pk]) @@ -138,6 +143,10 @@ class PowerFeed(NetBoxModel, PathEndpoint, LinkTermination): def __str__(self): return self.name + @classmethod + def get_prerequisite_models(cls): + return [PowerPanel, ] + def get_absolute_url(self): return reverse('dcim:powerfeed', args=[self.pk]) diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index e57934353..d0600e987 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -473,6 +473,10 @@ class RackReservation(NetBoxModel): def __str__(self): 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): return reverse('dcim:rackreservation', args=[self.pk]) diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index d02bd0932..70b4e6421 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -406,6 +406,10 @@ class Location(NestedGroupModel): super().validate_unique(exclude=exclude) + @classmethod + def get_prerequisite_models(cls): + return [Site, ] + def get_absolute_url(self): return reverse('dcim:location', args=[self.pk]) diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index d1538953a..ec054339c 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -124,6 +124,10 @@ class ASN(NetBoxModel): def __str__(self): return f'AS{self.asn_with_asdot}' + @classmethod + def get_prerequisite_models(cls): + return [RIR, ] + def get_absolute_url(self): return reverse('ipam:asn', args=[self.pk]) @@ -185,6 +189,10 @@ class Aggregate(GetAvailablePrefixesMixin, NetBoxModel): def __str__(self): return str(self.prefix) + @classmethod + def get_prerequisite_models(cls): + return [RIR, ] + def get_absolute_url(self): return reverse('ipam:aggregate', args=[self.pk]) diff --git a/netbox/netbox/models/__init__.py b/netbox/netbox/models/__init__.py index 2524c7c9b..675103d06 100644 --- a/netbox/netbox/models/__init__.py +++ b/netbox/netbox/models/__init__.py @@ -89,6 +89,10 @@ class NestedGroupModel(NetBoxFeatureSet, MPTTModel): def __str__(self): return self.name + @classmethod + def get_prerequisite_models(cls): + return [] + def clean(self): super().clean() @@ -126,3 +130,7 @@ class OrganizationalModel(NetBoxFeatureSet, models.Model): class Meta: abstract = True ordering = ('name',) + + @classmethod + def get_prerequisite_models(cls): + return [] diff --git a/netbox/netbox/views/generic/utils.py b/netbox/netbox/views/generic/utils.py index 57c7b5eba..c682181a1 100644 --- a/netbox/netbox/views/generic/utils.py +++ b/netbox/netbox/views/generic/utils.py @@ -1,12 +1,12 @@ def get_prerequisite_model(queryset): - requirement = None model = queryset.model if not queryset.count(): - prerequisites = model.get_prerequisite_models() - if prerequisites: - for prereq in prerequisites: - if not prereq.objects.count(): - requirement = prereq + if hasattr(model, 'get_prerequisite_models'): + prerequisites = model.get_prerequisite_models() + if prerequisites: + for prereq in prerequisites: + if not prereq.objects.count(): + return prereq - return requirement + return None diff --git a/netbox/templates/inc/missing_prerequisites.html b/netbox/templates/inc/missing_prerequisites.html index c12b157d0..04043fc9c 100644 --- a/netbox/templates/inc/missing_prerequisites.html +++ b/netbox/templates/inc/missing_prerequisites.html @@ -1,5 +1,6 @@ {% load buttons %}