netbox/netbox/dcim/models/mixins.py

121 lines
3.7 KiB
Python

from django.apps import apps
from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
__all__ = (
'CachedScopeMixin',
'RenderConfigMixin',
)
class RenderConfigMixin(models.Model):
config_template = models.ForeignKey(
to='extras.ConfigTemplate',
on_delete=models.PROTECT,
related_name='%(class)ss',
blank=True,
null=True
)
class Meta:
abstract = True
def get_config_template(self):
"""
Return the appropriate ConfigTemplate (if any) for this Device.
"""
if self.config_template:
return self.config_template
if self.role and self.role.config_template:
return self.role.config_template
if self.platform and self.platform.config_template:
return self.platform.config_template
class CachedScopeMixin(models.Model):
"""
Mixin for adding a GenericForeignKey scope to a model that can point to a Region, SiteGroup, Site, or Location.
Includes cached fields for each to allow efficient filtering. Appropriate validation must be done in the clean()
method as this does not have any as validation is generally model-specific.
"""
scope_type = models.ForeignKey(
to='contenttypes.ContentType',
on_delete=models.PROTECT,
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,
blank=True,
null=True
)
_site = models.ForeignKey(
to='dcim.Site',
on_delete=models.CASCADE,
blank=True,
null=True
)
_region = models.ForeignKey(
to='dcim.Region',
on_delete=models.CASCADE,
blank=True,
null=True
)
_site_group = models.ForeignKey(
to='dcim.SiteGroup',
on_delete=models.CASCADE,
blank=True,
null=True
)
class Meta:
abstract = True
def clean(self):
if self.scope_type and not self.scope:
scope_type = self.scope_type.model_class()
raise ValidationError({
'scope': _(
"Please select a {scope_type}."
).format(scope_type=scope_type._meta.model_name)
})
super().clean()
def save(self, *args, **kwargs):
# Cache objects associated with the terminating object (for filtering)
self.cache_related_objects()
super().save(*args, **kwargs)
def cache_related_objects(self):
self._region = self._site_group = self._site = self._location = None
if self.scope_type:
scope_type = self.scope_type.model_class()
if scope_type == apps.get_model('dcim', 'region'):
self._region = self.scope
elif scope_type == apps.get_model('dcim', 'sitegroup'):
self._site_group = self.scope
elif scope_type == apps.get_model('dcim', 'site'):
self._region = self.scope.region
self._site_group = self.scope.group
self._site = self.scope
elif scope_type == apps.get_model('dcim', 'location'):
self._region = self.scope.site.region
self._site_group = self.scope.site.group
self._site = self.scope.site
self._location = self.scope
cache_related_objects.alters_data = True