django-rest-framework serializer for ContentType object
Okay so answering my own question here. I had some help with zymud's answer. So, apparently in the documentation, there is a way to serialize the Generic relation.
So, all I had to do was create a custom field and associate that field in the serializer itself:
class ActivityObjectRelatedField(serializers.RelatedField): def to_representation(self, value): if isinstance(value, User): return 'User: ' + value.username elif isinstance(value, News): return 'News: ' + value.title elif isinstance(value, Job): return 'Job: ' + value.title elif isinstance(value, Tender): return 'Tender: ' + value.title raise Exception('Unexpected type of tagged object')class ActivitySerializer(serializers.HyperlinkedModelSerializer): actor = ActivityObjectRelatedField(read_only=True) target = ActivityObjectRelatedField(read_only=True) class Meta: model = Activity fields = ('url', 'actor', 'verb', 'target', 'pub_date')
You can implement custom field for generic key. Example:
from django.core.urlresolvers import resolvefrom rest_framework.fields import Fieldclass GenericRelatedField(Field): """ A custom field that expect object URL as input and transforms it to django model instance. """ read_only = False _default_view_name = '%(model_name)s-detail' lookup_field = 'pk' def __init__(self, related_models=(), **kwargs): super(GenericRelatedField, self).__init__(**kwargs) # related models - list of models that should be acceptable by # field. Note that all this models should have corresponding # endpoint. self.related_models = related_models def _get_url_basename(self, obj): """ Get object URL basename """ format_kwargs = { 'app_label': obj._meta.app_label, 'model_name': obj._meta.object_name.lower() } return self._default_view_name % format_kwargs def _get_request(self): try: return self.context['request'] except KeyError: raise AttributeError('GenericRelatedField have to be initialized with `request` in context') def to_representation(self, obj): """ Serializes any object to its URL representation """ kwargs = {self.lookup_field: getattr(obj, self.lookup_field)} request = self._get_request() return request.build_absolute_uri(reverse(self._get_url_basename(obj), kwargs=kwargs)) def clear_url(self, url): """ Removes domain and protocol from url """ if url.startswith('http'): return '/' + url.split('/', 3)[-1] return url def get_model_from_resolve_match(self, match): queryset = match.func.cls.queryset if queryset is not None: return queryset.model else: return match.func.cls.model def instance_from_url(self, url): url = self.clear_url(url) match = resolve(url) model = self.get_model_from_resolve_match(match) return model.objects.get(**match.kwargs) def to_internal_value(self, data): """ Restores model instance from its URL """ if not data: return None request = self._get_request() user = request.user try: obj = self.instance_from_url(data) model = obj.__class__ except (Resolver404, AttributeError, MultipleObjectsReturned, ObjectDoesNotExist): raise serializers.ValidationError("Can`t restore object from url: %s" % data) if model not in self.related_models: raise serializers.ValidationError('%s object does not support such relationship' % str(obj)) return obj
Example of usage:
class ActivitySerializer(serializers.HyperlinkedModelSerializer): target = GenericRelatedField(related_models=(News, Job, Tender)) ...
There is a third party lib as per documentation that did the heavy lifting already:
https://www.django-rest-framework.org/api-guide/relations/#rest-framework-generic-relations
It is pretty neat actually, my serializer class ended up few readable lines:
class ActivityTypeSerializer(serializers.ModelSerializer): target = GenericRelatedField({ User: UserSerializer(), Device: DeviceSerializer(), }) class Meta: model = Activity fields = ('target', 'target_id', 'verb', 'target_ct',)