add load/save values, fix filterset

This commit is contained in:
dreng
2023-05-06 22:57:12 +02:00
parent 7e2ffc575f
commit 9cc1607811
5 changed files with 88 additions and 27 deletions
+37 -1
View File
@@ -14,7 +14,7 @@ from netbox_topology_views.api.serializers import (
RoleImageSerializer, RoleImageSerializer,
TopologyDummySerializer, TopologyDummySerializer,
) )
from netbox_topology_views.models import RoleImage, IndividualOptions from netbox_topology_views.models import RoleImage, IndividualOptions, CoordinateGroup, Coordinate
from netbox_topology_views.views import get_topology_data from netbox_topology_views.views import get_topology_data
from netbox_topology_views.utils import get_image_from_url, export_data_to_xml, get_query_settings from netbox_topology_views.utils import get_image_from_url, export_data_to_xml, get_query_settings
from netbox_topology_views.filters import DeviceFilterSet from netbox_topology_views.filters import DeviceFilterSet
@@ -50,6 +50,8 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
if not actual_device: if not actual_device:
return Response({"status": "invalid node_id in body"}, status=400) return Response({"status": "invalid node_id in body"}, status=400)
# Storing coordinates in custom field is deprecated now.
# We preserve this for backwards compatibility.
try: try:
actual_device.custom_field_data["coordinates"] = "%s;%s" % ( actual_device.custom_field_data["coordinates"] = "%s;%s" % (
x_coord, x_coord,
@@ -61,6 +63,40 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
{"status": "coords custom field could not be saved"}, status=500 {"status": "coords custom field could not be saved"}, status=500
) )
# Default group named "default" must always exist in order to make sure
# that coordinate values can be stored even if no coordinate group has been
# selected. The default group will be added automatically if it does not exist.
try:
group = CoordinateGroup.objects.get(name="default")
except CoordinateGroup.DoesNotExist:
try:
group = CoordinateGroup(
name="default",
description="Automatically generated default group. If you delete "
"this group, all default coordinates are gone for good but "
"the group itself will be re-created."
)
group.save()
except:
return Response(
{"status": "Error while creating default group."}, status=500
)
try:
# Hen-and-egg-problem. Thanks, Django! By default, Django updates records that
# already exist and inserts otherwise. This does not work with our
# unique_together key if no pk is given. But: No record, no pk.
if not Coordinate.objects.filter(group=group, device=actual_device):
# Unique group/device pair does not exist. Prepare new data set
coords = Coordinate(group=group, device=actual_device, x=x_coord, y=y_coord)
else:
# Unique group/device pair already exists. Update data
coords = Coordinate(pk=Coordinate.objects.get(group=group, device=actual_device).pk, group=group, device=actual_device, x=x_coord, y=y_coord)
coords.save()
except:
return Response(
{"status": "Coordinates could not be saved."}, status=500
)
return Response({"status": "saved coords"}) return Response({"status": "saved coords"})
class ExportTopoToXML(PermissionRequiredMixin, ViewSet): class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
+15 -5
View File
@@ -1,7 +1,7 @@
import django_filters import django_filters
from dcim.choices import DeviceStatusChoices from dcim.choices import DeviceStatusChoices
from dcim.models import Device, DeviceRole, Location, Rack, Region, Site from dcim.models import Device, DeviceRole, Location, Rack, Region, Site
from .models import Coordinate from .models import Coordinate, CoordinateGroup
from django.db.models import Q from django.db.models import Q
from netbox.filtersets import NetBoxModelFilterSet from netbox.filtersets import NetBoxModelFilterSet
from tenancy.filtersets import TenancyFilterSet from tenancy.filtersets import TenancyFilterSet
@@ -55,14 +55,24 @@ class DeviceFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
qs_filter = Q(name__icontains=value) qs_filter = Q(name__icontains=value)
return queryset.filter(qs_filter) return queryset.filter(qs_filter)
class CoordinateFilterSet(NetBoxModelFilterSet): class CoordinatesFilterSet(NetBoxModelFilterSet):
group = django_filters.ModelMultipleChoiceFilter(
queryset = CoordinateGroup.objects.all(),
)
device = django_filters.ModelMultipleChoiceFilter(
queryset = Device.objects.all(),
)
class Meta: class Meta:
model = Coordinate model = Coordinate
fields = ('id', 'group', 'device', 'x', 'y') fields = ['id', 'group', 'device', 'x', 'y']
def search(self, queryset, name, value): def search(self, queryset, name, value):
"""Perform the filtered search.""" """Perform the filtered search."""
if not value.strip(): if not value.strip():
return queryset return queryset
qs_filter = Q(group__icontains=value | Q(device__icontains=value)) return queryset.filter(
return queryset.filter(qs_filter) Q(group__name__icontains=value) |
Q(device__name__icontains=value)
)
+6 -12
View File
@@ -162,27 +162,21 @@ class CoordinatesForm(NetBoxModelForm):
class CoordinatesFilterForm(NetBoxModelFilterSetForm): class CoordinatesFilterForm(NetBoxModelFilterSetForm):
model = Coordinate model = Coordinate
fieldsets = (
(None, ('q', 'filter_id')),
('Coordinates', ('group', 'device', 'x', 'y'))
)
group = forms.ModelMultipleChoiceField( group = forms.ModelMultipleChoiceField(
queryset=CoordinateGroup.objects.all(), queryset=CoordinateGroup.objects.all(),
required=False required=False
) )
device = DynamicModelChoiceField( device = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(), queryset=Device.objects.all(),
required=False required=False
) )
id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(),
required=False,
label=_("Device"),
query_params={
"location_id": "$location_id",
"region_id": "$region_id",
"site_id": "$site_id",
"role_id": "$device_role_id",
},
)
x = forms.IntegerField( x = forms.IntegerField(
required=False required=False
) )
+1 -6
View File
@@ -121,7 +121,7 @@ class CoordinateGroup(NetBoxModel):
class Coordinate(NetBoxModel): class Coordinate(NetBoxModel):
""" """
Coordinates are being used to place devices in a topology view onto a certian Coordinates are being used to place devices in a topology view onto a certain
position. Devices belong to one or more coordinate groups. They have to position. Devices belong to one or more coordinate groups. They have to
be unique together. be unique together.
""" """
@@ -147,11 +147,6 @@ class Coordinate(NetBoxModel):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('plugins:netbox_topology_views:coordinate', args=[self.pk]) return reverse('plugins:netbox_topology_views:coordinate', args=[self.pk])
def set_xy_from_text_coords(self, coords: str):
xy = coords.split(';')
self.x = xy[0]
self.y = xy[1]
class IndividualOptions(NetBoxModel): class IndividualOptions(NetBoxModel):
CHOICES = ( CHOICES = (
('interface', 'interface'), ('interface', 'interface'),
+29 -3
View File
@@ -32,7 +32,7 @@ from wireless.models import WirelessLink
from netbox.views.generic import ObjectView, ObjectListView, ObjectEditView, ObjectDeleteView, ObjectChangeLogView from netbox.views.generic import ObjectView, ObjectListView, ObjectEditView, ObjectDeleteView, ObjectChangeLogView
from netbox_topology_views.filters import DeviceFilterSet, CoordinateFilterSet from netbox_topology_views.filters import DeviceFilterSet, CoordinatesFilterSet
from netbox_topology_views.forms import DeviceFilterForm, IndividualOptionsForm, CoordinateGroupsForm, CoordinatesForm, CoordinatesFilterForm from netbox_topology_views.forms import DeviceFilterForm, IndividualOptionsForm, CoordinateGroupsForm, CoordinatesForm, CoordinatesFilterForm
from netbox_topology_views.models import RoleImage, CoordinateGroup, Coordinate, IndividualOptions from netbox_topology_views.models import RoleImage, CoordinateGroup, Coordinate, IndividualOptions
from netbox_topology_views.tables import CoordinateGroupListTable, CoordinateListTable from netbox_topology_views.tables import CoordinateGroupListTable, CoordinateListTable
@@ -156,8 +156,31 @@ def create_node(
node["href"] = device.get_absolute_url() node["href"] = device.get_absolute_url()
node["image"] = get_image_for_entity(device) node["image"] = get_image_for_entity(device)
# Default group named "default" must always exist in order to make sure
# that coordinate values can be stored even if no coordinate group has been
# selected. The default group will be added automatically if it does not exist.
try:
group = CoordinateGroup.objects.get(name="default")
except CoordinateGroup.DoesNotExist:
try:
group = CoordinateGroup(
name="default",
description="Automatically generated default group. If you delete "
"this group, all default coordinates are gone for good but "
"the group itself will be re-created."
)
group.save()
except:
pass
node["physics"] = True node["physics"] = True
if "coordinates" in device.custom_field_data: if Coordinate.objects.filter(group=group, device=device.pk).values('x') and Coordinate.objects.filter(group=group, device=device.pk).values('y'):
node["x"] = Coordinate.objects.get(group=group, device=device.pk).x
node["y"] = Coordinate.objects.get(group=group, device=device.pk).y
node["physics"] = False
elif "coordinates" in device.custom_field_data:
# We prefer the new Coordinate model but leave the deprecated method
# for now as fallback for compatibility reasons
if device.custom_field_data["coordinates"] is not None: if device.custom_field_data["coordinates"] is not None:
if ";" in device.custom_field_data["coordinates"]: if ";" in device.custom_field_data["coordinates"]:
cords = device.custom_field_data["coordinates"].split(";") cords = device.custom_field_data["coordinates"].split(";")
@@ -166,6 +189,9 @@ def create_node(
node["physics"] = False node["physics"] = False
elif save_coords: elif save_coords:
node["physics"] = False node["physics"] = False
elif save_coords:
node["physics"] = False
return node return node
@@ -772,7 +798,7 @@ class CoordinateListView(PermissionRequiredMixin, ObjectListView):
queryset = Coordinate.objects.all() queryset = Coordinate.objects.all()
table = CoordinateListTable table = CoordinateListTable
template_name = 'netbox_topology_views/coordinate_list.html' template_name = 'netbox_topology_views/coordinate_list.html'
filterset = CoordinateFilterSet filterset = CoordinatesFilterSet
filterset_form = CoordinatesFilterForm filterset_form = CoordinatesFilterForm
class CoordinateEditView(PermissionRequiredMixin, ObjectEditView): class CoordinateEditView(PermissionRequiredMixin, ObjectEditView):