Kaynağa Gözat

Add termination FKs on Circuit model

Jeremy Stretch 4 yıl önce
ebeveyn
işleme
872e936924

+ 1 - 2
netbox/circuits/api/views.py

@@ -48,8 +48,7 @@ class CircuitTypeViewSet(CustomFieldModelViewSet):
 
 class CircuitViewSet(CustomFieldModelViewSet):
     queryset = Circuit.objects.prefetch_related(
-        Prefetch('terminations', queryset=CircuitTermination.objects.prefetch_related('site')),
-        'type', 'tenant', 'provider',
+        'type', 'tenant', 'provider', 'termination_a', 'termination_z'
     ).prefetch_related('tags')
     serializer_class = serializers.CircuitSerializer
     filterset_class = filters.CircuitFilterSet

+ 15 - 0
netbox/circuits/migrations/0027_cloud.py

@@ -12,6 +12,7 @@ class Migration(migrations.Migration):
     ]
 
     operations = [
+        # Create the new Cloud model
         migrations.CreateModel(
             name='Cloud',
             fields=[
@@ -37,6 +38,8 @@ class Migration(migrations.Migration):
             name='cloud',
             unique_together={('provider', 'name')},
         ),
+
+        # Add cloud FK to CircuitTermination
         migrations.AddField(
             model_name='circuittermination',
             name='cloud',
@@ -47,4 +50,16 @@ class Migration(migrations.Migration):
             name='site',
             field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='circuit_terminations', to='dcim.site'),
         ),
+
+        # Add FKs to CircuitTermination on Circuit
+        migrations.AddField(
+            model_name='circuit',
+            name='termination_a',
+            field=models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='circuits.circuittermination'),
+        ),
+        migrations.AddField(
+            model_name='circuit',
+            name='termination_z',
+            field=models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='circuits.circuittermination'),
+        ),
     ]

+ 37 - 0
netbox/circuits/migrations/0028_cache_circuit_terminations.py

@@ -0,0 +1,37 @@
+import sys
+
+from django.db import migrations
+
+
+def cache_circuit_terminations(apps, schema_editor):
+    Circuit = apps.get_model('circuits', 'Circuit')
+    CircuitTermination = apps.get_model('circuits', 'CircuitTermination')
+
+    if 'test' not in sys.argv:
+        print(f"\n    Caching circuit terminations...", flush=True)
+
+    a_terminations = {
+        ct.circuit_id: ct.pk for ct in CircuitTermination.objects.filter(term_side='A')
+    }
+    z_terminations = {
+        ct.circuit_id: ct.pk for ct in CircuitTermination.objects.filter(term_side='Z')
+    }
+    for circuit in Circuit.objects.all():
+        Circuit.objects.filter(pk=circuit.pk).update(
+            termination_a_id=a_terminations.get(circuit.pk),
+            termination_z_id=z_terminations.get(circuit.pk),
+        )
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('circuits', '0027_cloud'),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            code=cache_circuit_terminations,
+            reverse_code=migrations.RunPython.noop
+        ),
+    ]

+ 22 - 16
netbox/circuits/models.py

@@ -9,7 +9,6 @@ from extras.utils import extras_features
 from netbox.models import BigIDModel, ChangeLoggedModel, OrganizationalModel, PrimaryModel
 from utilities.querysets import RestrictedQuerySet
 from .choices import *
-from .querysets import CircuitQuerySet
 
 
 __all__ = (
@@ -236,7 +235,25 @@ class Circuit(PrimaryModel):
         blank=True
     )
 
-    objects = CircuitQuerySet.as_manager()
+    # Cache associated CircuitTerminations
+    termination_a = models.ForeignKey(
+        to='circuits.CircuitTermination',
+        on_delete=models.SET_NULL,
+        related_name='+',
+        editable=False,
+        blank=True,
+        null=True
+    )
+    termination_z = models.ForeignKey(
+        to='circuits.CircuitTermination',
+        on_delete=models.SET_NULL,
+        related_name='+',
+        editable=False,
+        blank=True,
+        null=True
+    )
+
+    objects = RestrictedQuerySet.as_manager()
 
     csv_headers = [
         'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
@@ -271,20 +288,6 @@ class Circuit(PrimaryModel):
     def get_status_class(self):
         return CircuitStatusChoices.CSS_CLASSES.get(self.status)
 
-    def _get_termination(self, side):
-        for ct in self.terminations.all():
-            if ct.term_side == side:
-                return ct
-        return None
-
-    @property
-    def termination_a(self):
-        return self._get_termination('A')
-
-    @property
-    def termination_z(self):
-        return self._get_termination('Z')
-
 
 @extras_features('webhooks')
 class CircuitTermination(ChangeLoggedModel, PathEndpoint, CableTermination):
@@ -345,6 +348,9 @@ class CircuitTermination(ChangeLoggedModel, PathEndpoint, CableTermination):
         unique_together = ['circuit', 'term_side']
 
     def __str__(self):
+        if self.site:
+            return str(self.site)
+        return str(self.cloud)
         return f"Side {self.get_term_side_display()}"
 
     def clean(self):

+ 0 - 17
netbox/circuits/querysets.py

@@ -1,17 +0,0 @@
-from django.db.models import OuterRef, Subquery
-
-from utilities.querysets import RestrictedQuerySet
-
-
-class CircuitQuerySet(RestrictedQuerySet):
-
-    def annotate_sites(self):
-        """
-        Annotate the A and Z termination site names for ordering.
-        """
-        from circuits.models import CircuitTermination
-        _terminations = CircuitTermination.objects.filter(circuit=OuterRef('pk'))
-        return self.annotate(
-            a_side=Subquery(_terminations.filter(term_side='A').values('site__name')[:1]),
-            z_side=Subquery(_terminations.filter(term_side='Z').values('site__name')[:1]),
-        )

+ 8 - 8
netbox/circuits/signals.py

@@ -1,17 +1,17 @@
-from django.db.models.signals import post_delete, post_save
+from django.db.models.signals import post_save
 from django.dispatch import receiver
 from django.utils import timezone
 
 from .models import Circuit, CircuitTermination
 
 
-@receiver((post_save, post_delete), sender=CircuitTermination)
+@receiver(post_save, sender=CircuitTermination)
 def update_circuit(instance, **kwargs):
     """
-    When a CircuitTermination has been modified, update the last_updated time of its parent Circuit.
+    When a CircuitTermination has been modified, update its parent Circuit.
     """
-    circuits = Circuit.objects.filter(pk=instance.circuit_id)
-    time = timezone.now()
-    for circuit in circuits:
-        circuit.last_updated = time
-        circuit.save()
+    fields = {
+        'last_updated': timezone.now(),
+        f'termination_{instance.term_side.lower()}': instance.pk,
+    }
+    Circuit.objects.filter(pk=instance.circuit_id).update(**fields)

+ 9 - 7
netbox/circuits/tables.py

@@ -83,11 +83,11 @@ class CircuitTable(BaseTable):
     )
     status = ChoiceFieldColumn()
     tenant = TenantColumn()
-    a_side = tables.Column(
-        verbose_name='A Side'
+    termination_a = tables.Column(
+        verbose_name='Side A'
     )
-    z_side = tables.Column(
-        verbose_name='Z Side'
+    termination_z = tables.Column(
+        verbose_name='Side Z'
     )
     tags = TagColumn(
         url_name='circuits:circuit_list'
@@ -96,7 +96,9 @@ class CircuitTable(BaseTable):
     class Meta(BaseTable.Meta):
         model = Circuit
         fields = (
-            'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'install_date', 'commit_rate',
-            'description', 'tags',
+            'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'install_date',
+            'commit_rate', 'description', 'tags',
+        )
+        default_columns = (
+            'pk', 'cid', 'provider', 'type', 'status', 'tenant', 'termination_a', 'termination_z', 'description',
         )
-        default_columns = ('pk', 'cid', 'provider', 'type', 'status', 'tenant', 'a_side', 'z_side', 'description')

+ 3 - 3
netbox/circuits/views.py

@@ -33,7 +33,7 @@ class ProviderView(generic.ObjectView):
             provider=instance
         ).prefetch_related(
             'type', 'tenant', 'terminations__site'
-        ).annotate_sites()
+        )
 
         circuits_table = tables.CircuitTable(circuits)
         circuits_table.columns.hide('provider')
@@ -172,8 +172,8 @@ class CircuitTypeBulkDeleteView(generic.BulkDeleteView):
 
 class CircuitListView(generic.ObjectListView):
     queryset = Circuit.objects.prefetch_related(
-        'provider', 'type', 'tenant', 'terminations'
-    ).annotate_sites()
+        'provider', 'type', 'tenant', 'termination_a', 'termination_z'
+    )
     filterset = filters.CircuitFilterSet
     filterset_form = forms.CircuitFilterForm
     table = tables.CircuitTable

+ 1 - 1
netbox/netbox/constants.py

@@ -40,7 +40,7 @@ SEARCH_TYPES = OrderedDict((
     ('circuit', {
         'queryset': Circuit.objects.prefetch_related(
             'type', 'provider', 'tenant', 'terminations__site'
-        ).annotate_sites(),
+        ),
         'filterset': CircuitFilterSet,
         'table': CircuitTable,
         'url': 'circuits:circuit_list',