1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Add termination FKs on Circuit model

This commit is contained in:
Jeremy Stretch
2021-03-18 13:54:05 -04:00
parent 574a43fff7
commit 872e936924
9 changed files with 96 additions and 54 deletions

View File

@ -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

View File

@ -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'),
),
]

View File

@ -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
),
]

View File

@ -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):

View File

@ -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]),
)

View File

@ -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)

View File

@ -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')

View File

@ -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

View File

@ -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',