django-rest-framework serializer for ContentType object django-rest-framework serializer for ContentType object django django

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',)