content: 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 _
  
  from dcim.constants import VIRTUAL_IFACE_TYPES, WIRELESS_IFACE_TYPES
  
  __all__ = (
      'CachedScopeMixin',
      'InterfaceValidationMixin',
      '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 or self.scope_id):
              scope_type = self.scope_type.model_class()
              raise ValidationError(
                  _("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
  
  
  class InterfaceValidationMixin:
  
      def clean(self):
          super().clean()
  
          # An interface cannot be bridged to itself
          if self.pk and self.bridge_id == self.pk:
              raise ValidationError({'bridge': _("An interface cannot be bridged to itself.")})
  
          # Only physical interfaces may have a PoE mode/type assigned
          if self.poe_mode and self.type in VIRTUAL_IFACE_TYPES:
              raise ValidationError({
                  'poe_mode': _("Virtual interfaces cannot have a PoE mode.")
              })
          if self.poe_type and self.type in VIRTUAL_IFACE_TYPES:
              raise ValidationError({
                  'poe_type': _("Virtual interfaces cannot have a PoE type.")
              })
  
          # An interface with a PoE type set must also specify a mode
          if self.poe_type and not self.poe_mode:
              raise ValidationError({
                  'poe_type': _("Must specify PoE mode when designating a PoE type.")
              })
  
          # RF role may be set only for wireless interfaces
          if self.rf_role and self.type not in WIRELESS_IFACE_TYPES:
              raise ValidationError({'rf_role': _("Wireless role may be set only on wireless interfaces.")})
  