+from urllib.parse import urlparse
+from django.core.urlresolvers import resolve
from rest_framework import serializers
+from cbe.serializer_fields import TypeField,GenericRelatedField
-from rest-gm2m-field import TypeField,GenericRelatedField
-from myapp.models import MyMainModel, MyLinkedModel1, MyLinkedModel2
-from myapp.serializers import MyLinkedModel1Serializer, MyLinkedModel1Serializer
+class GenericHyperlinkedSerializer(serializers.HyperlinkedModelSerializer):
+ Serializer for models with generic relations.
+ Includes a type field to allow GenericRelatedFields specified in the serializer to know how to serialize correctly.
+ Derived from hyperlinked serializer and the url field must be present on the serializer
+ def create(self, validated_data):
+ # Remove generic fields from validated data and add to separate dict
+ for field in self._fields.keys():
+ if type(self._fields[field]) == serializers.ManyRelatedField:
+ if type( self._fields[field].child_relation) == GenericRelatedField:
+ related[field] = validated_data.pop(field)
+ # Create instance of serializers Meta.model
+ instance = self.Meta.model.objects.create(**validated_data)
+ # For all related fields attach the listed objects
+ for field in related.keys():
+ for object in related[field]:
+ attr = getattr( instance, field )
-class MyMainModelSerializer(serializers.HyperlinkedModelSerializer):
- mygm2m_field = GenericRelatedField(many=True, serializer_dict={MyLinkedModel1:MyLinkedModel1Serializer(),MyLinkedModel2:MyLinkedModel2Serializer(),})
- fields = ('type', 'url', 'mygm2m_field' )
+ def update(self, instance, validated_data):
+ # Create a dict of updatable fields
+ for field in self._fields.keys():
+ # Exclude read only fields
+ if not self._fields[field].read_only:
+ if type(self._fields[field]) != serializers.ManyRelatedField:
+ fields[field] = self._fields[field]
+ # Exclude generics but add to separate dict
+ if type( self._fields[field].child_relation) != GenericRelatedField:
+ fields[field] = self._fields[field]
+ generics[field] = self._fields[field]
- def create(self, validated_data):
- related = validated_data.pop('mygm2m_field')
- mainobject = MyMainModel.objects.create(**validated_data)
+ # Set all valid attributes of the instance to the validated data
+ for field in fields.keys():
+ setattr(instance, field, validated_data.get(field, getattr(instance,field)))
- mainobject.mygm2m_field.add(object)
+ # Add any new generic relations
+ for generic_attr in generics.keys():
+ attr = getattr( instance, generic_attr )
+ attr_objects = list(attr.all())
+ for object in validated_data.get(generic_attr, getattr(instance,generic_attr)):
+ if object not in attr_objects:
+ # If the object is not in the list of existing generic relations then add it
+ # If the object is already related then remove it from the list so we end up with a list of missing generic relations
+ attr_objects.remove(object)
+ # Remove any missing generic relations if not a partial update (PUT but not PATCH)
+ for object in attr_objects: