base.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. from drf_spectacular.types import OpenApiTypes
  2. from drf_spectacular.utils import extend_schema_field
  3. from rest_framework import serializers
  4. from dcim.models import FrontPort, FrontPortTemplate, PortMapping, PortTemplateMapping, RearPort, RearPortTemplate
  5. from utilities.api import get_serializer_for_model
  6. __all__ = (
  7. 'ConnectedEndpointsSerializer',
  8. 'PortSerializer',
  9. )
  10. class ConnectedEndpointsSerializer(serializers.ModelSerializer):
  11. """
  12. Legacy serializer for pre-v3.3 connections
  13. """
  14. connected_endpoints_type = serializers.SerializerMethodField(read_only=True, allow_null=True)
  15. connected_endpoints = serializers.SerializerMethodField(read_only=True)
  16. connected_endpoints_reachable = serializers.SerializerMethodField(read_only=True)
  17. @extend_schema_field(OpenApiTypes.STR)
  18. def get_connected_endpoints_type(self, obj):
  19. if endpoints := obj.connected_endpoints:
  20. return f'{endpoints[0]._meta.app_label}.{endpoints[0]._meta.model_name}'
  21. return None
  22. @extend_schema_field(serializers.ListField(allow_null=True))
  23. def get_connected_endpoints(self, obj):
  24. """
  25. Return the appropriate serializer for the type of connected object.
  26. """
  27. if endpoints := obj.connected_endpoints:
  28. serializer = get_serializer_for_model(endpoints[0])
  29. context = {'request': self.context['request']}
  30. return serializer(endpoints, nested=True, many=True, context=context).data
  31. return None
  32. @extend_schema_field(serializers.BooleanField)
  33. def get_connected_endpoints_reachable(self, obj):
  34. return obj._path and obj._path.is_complete and obj._path.is_active
  35. class PortSerializer(serializers.ModelSerializer):
  36. """
  37. Base serializer for front & rear port and port templates.
  38. """
  39. @property
  40. def _mapper(self):
  41. """
  42. Return the model and ForeignKey field name used to track port mappings for this model.
  43. """
  44. if self.Meta.model is FrontPort:
  45. return PortMapping, 'front_port'
  46. if self.Meta.model is RearPort:
  47. return PortMapping, 'rear_port'
  48. if self.Meta.model is FrontPortTemplate:
  49. return PortTemplateMapping, 'front_port'
  50. if self.Meta.model is RearPortTemplate:
  51. return PortTemplateMapping, 'rear_port'
  52. raise ValueError(f"Could not determine mapping details for {self.__class__}")
  53. def create(self, validated_data):
  54. mappings = validated_data.pop('mappings', [])
  55. instance = super().create(validated_data)
  56. # Create port mappings
  57. mapping_model, fk_name = self._mapper
  58. for attrs in mappings:
  59. mapping_model.objects.create(**{
  60. fk_name: instance,
  61. **attrs,
  62. })
  63. return instance
  64. def update(self, instance, validated_data):
  65. mappings = validated_data.pop('mappings', None)
  66. instance = super().update(instance, validated_data)
  67. if mappings is not None:
  68. # Update port mappings
  69. mapping_model, fk_name = self._mapper
  70. mapping_model.objects.filter(**{fk_name: instance}).delete()
  71. for attrs in mappings:
  72. mapping_model.objects.create(**{
  73. fk_name: instance,
  74. **attrs,
  75. })
  76. return instance