Browse Source

Implemented API endpoints for ImageAttachments

Jeremy Stretch 8 years ago
parent
commit
2c1fa628a2

+ 54 - 3
netbox/extras/api/serializers.py

@@ -1,9 +1,14 @@
 from rest_framework import serializers
 
-from dcim.api.serializers import NestedSiteSerializer
-from extras.models import ACTION_CHOICES, Graph, GRAPH_TYPE_CHOICES, ExportTemplate, TopologyMap, UserAction
+from django.core.exceptions import ObjectDoesNotExist
+
+from dcim.api.serializers import NestedDeviceSerializer, NestedRackSerializer, NestedSiteSerializer
+from dcim.models import Device, Rack, Site
+from extras.models import (
+    ACTION_CHOICES, ExportTemplate, Graph, GRAPH_TYPE_CHOICES, ImageAttachment, TopologyMap, UserAction,
+)
 from users.api.serializers import NestedUserSerializer
-from utilities.api import ChoiceFieldSerializer
+from utilities.api import ChoiceFieldSerializer, ContentTypeFieldSerializer
 
 
 #
@@ -72,6 +77,52 @@ class WritableTopologyMapSerializer(serializers.ModelSerializer):
 
 
 #
+# Image attachments
+#
+
+class ImageAttachmentSerializer(serializers.ModelSerializer):
+    parent = serializers.SerializerMethodField()
+
+    class Meta:
+        model = ImageAttachment
+        fields = ['id', 'parent', 'name', 'image', 'image_height', 'image_width', 'created']
+
+    def get_parent(self, obj):
+
+        # Static mapping of models to their nested serializers
+        if isinstance(obj.parent, Device):
+            serializer = NestedDeviceSerializer
+        elif isinstance(obj.parent, Rack):
+            serializer = NestedRackSerializer
+        elif isinstance(obj.parent, Site):
+            serializer = NestedSiteSerializer
+        else:
+            raise Exception("Unexpected type of parent object for ImageAttachment")
+
+        return serializer(obj.parent, context={'request': self.context['request']}).data
+
+
+class WritableImageAttachmentSerializer(serializers.ModelSerializer):
+    content_type = ContentTypeFieldSerializer()
+
+    class Meta:
+        model = ImageAttachment
+        fields = ['id', 'content_type', 'object_id', 'name', 'image']
+
+    def validate(self, data):
+
+        # Validate that the parent object exists
+        try:
+            data['content_type'].get_object_for_this_type(id=data['object_id'])
+        except ObjectDoesNotExist:
+            raise serializers.ValidationError(
+                "Invalid parent object: {} ID {}".format(data['content_type'], data['object_id'])
+            )
+
+        return data
+
+
+#
 # User actions
 #
 

+ 3 - 0
netbox/extras/api/urls.py

@@ -23,6 +23,9 @@ router.register(r'export-templates', views.ExportTemplateViewSet)
 # Topology maps
 router.register(r'topology-maps', views.TopologyMapViewSet)
 
+# Image attachments
+router.register(r'image-attachments', views.ImageAttachmentViewSet)
+
 # Recent activity
 router.register(r'recent-activity', views.RecentActivityViewSet)
 

+ 7 - 1
netbox/extras/api/views.py

@@ -6,7 +6,7 @@ from django.http import HttpResponse
 from django.shortcuts import get_object_or_404
 
 from extras import filters
-from extras.models import ExportTemplate, Graph, TopologyMap, UserAction
+from extras.models import ExportTemplate, Graph, ImageAttachment, TopologyMap, UserAction
 from utilities.api import WritableSerializerMixin
 from . import serializers
 
@@ -81,6 +81,12 @@ class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
         return response
 
 
+class ImageAttachmentViewSet(WritableSerializerMixin, ModelViewSet):
+    queryset = ImageAttachment.objects.all()
+    serializer_class = serializers.ImageAttachmentSerializer
+    write_serializer_class = serializers.WritableImageAttachmentSerializer
+
+
 class RecentActivityViewSet(ReadOnlyModelViewSet):
     """
     List all UserActions to provide a log of recent activity.

+ 1 - 1
netbox/extras/migrations/0005_add_imageattachment.py

@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# Generated by Django 1.10.6 on 2017-03-30 21:09
+# Generated by Django 1.10.6 on 2017-04-03 15:55
 from __future__ import unicode_literals
 
 from django.db import migrations, models

+ 1 - 1
netbox/extras/models.py

@@ -384,7 +384,7 @@ class ImageAttachment(models.Model):
     """
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField()
-    obj = GenericForeignKey('content_type', 'object_id')
+    parent = GenericForeignKey('content_type', 'object_id')
     image = models.ImageField(upload_to=image_upload, height_field='image_height', width_field='image_width')
     image_height = models.PositiveSmallIntegerField()
     image_width = models.PositiveSmallIntegerField()

+ 17 - 1
netbox/utilities/api.py

@@ -1,9 +1,10 @@
 from django.conf import settings
+from django.contrib.contenttypes.models import ContentType
 
 from rest_framework import authentication, exceptions
 from rest_framework.exceptions import APIException
 from rest_framework.permissions import DjangoModelPermissions, SAFE_METHODS
-from rest_framework.serializers import Field
+from rest_framework.serializers import Field, ValidationError
 
 from users.models import Token
 
@@ -79,6 +80,21 @@ class ChoiceFieldSerializer(Field):
         return self._choices.get(data)
 
 
+class ContentTypeFieldSerializer(Field):
+    """
+    Represent a ContentType as '<app_label>.<model>'
+    """
+    def to_representation(self, obj):
+        return "{}.{}".format(obj.app_label, obj.model)
+
+    def to_internal_value(self, data):
+        app_label, model = data.split('.')
+        try:
+            return ContentType.objects.get_by_natural_key(app_label=app_label, model=model)
+        except ContentType.DoesNotExist:
+            raise ValidationError("Invalid content type")
+
+
 class WritableSerializerMixin(object):
     """
     Allow for the use of an alternate, writable serializer class for write operations (e.g. POST, PUT).