djangodjango-rest-frameworkdjango-contenttypes

django-rest-framework serializer for ContentType object


I am building an activity model, somewhat similar to this package. It has an actor, verb and the target.

class Activity(models.Model):
    actor_type = models.ForeignKey(ContentType, related_name='actor_type_activities')
    actor_id = models.PositiveIntegerField()
    actor = GenericForeignKey('actor_type', 'actor_id')
    verb = models.CharField(max_length=10)
    target_type = models.ForeignKey(ContentType, related_name='target_type_activities')
    target_id = models.PositiveIntegerField()
    target = GenericForeignKey('target_type', 'target_id')
    pub_date = models.DateTimeField(default=timezone.now)

Now whenever a new object of whichever models (Tender, Job and News) is created, a new Activity object is created, with the target being the objects of any of these three models.

eg. user (actor) published (verb) title (target)

class Tender(models.Model):
    title = models.CharField(max_length=256)
    description = models.TextField()

class Job(models.Model):
    title = models.CharField(max_length=256)
    qualification = models.CharField(max_length=256)

class News(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    title = models.CharField(max_length=150)

To get this data I am making an API which will get me the required json data. I am using django-rest-framework for this and very new with it.

class ActorSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'username', 'email')

class ActivitySerializer(serializers.HyperlinkedModelSerializer):
    actor = ActorSerializer()
    class Meta:
        model = Activity
        fields = ('url', 'actor', 'verb', 'pub_date')

In the above serializers, I knew that actor will be the User. And so I used the User model for the ActorSerializer class. But as for the target, it can be any of these three models (News/Job/Tender).

How can I make a serializer (eg. TargetSerialier class) for the ContentType object so that I can use the target in the ActivitySerializer class field?


Solution

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