I wanted to build a django-rest-framework api for interacting with django-activity-stream. Activity stream uses Generic Foreign Keys heavily which aren’t naturally supported. We can however reuse existing serializers and nest the data conditionally.
Here is a ModelSerializer for activity steam’s Action model.
from rest_framework import serializers
from actstream.models import Action
from myapp.models import ThingA, ThingB
from myapp.serializers import ThingASerializer, ThingBSerializer
class GenericRelatedField(serializers.Field):
def to_representation(self, value):
if isinstance(value, ThingA):
return ThingASerializer(value).data
if isinstance(value, ThingB):
return ThingBSerializer(value).data
# Not found - return string.
return str(value)
class ActionSerializer(serializers.ModelSerializer):
actor = GenericRelatedField(read_only=True)
target = GenericRelatedField(read_only=True)
action_object = GenericRelatedField(read_only=True)
class Meta:
model = Action
GenericRelatedField will check if the value is an instance of a known Model and assign it the appropriate serializer.
Next we can use a viewset for displaying Actions. Since activity stream uses querysets it’s pretty simple to integrate with a ModelViewSet. In my case I’m checking for a get parameter to determine whether we want all actions, actions of people the logged in user follows, or actions of the user. I added some filters on action and target content type too.
from rest_framework import viewsets
from actstream.models import user_stream, Action
from .serializers import ActionSerializer
class ActivityViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = ActionSerializer
def get_queryset(self):
following = self.request.GET.get('following')
if following and following != 'false' and following != '0':
if following == 'myself':
qs = user_stream(self.request.user, with_user_activity=True)
return qs.filter(actor_object_id=self.request.user.id)
else: # Everyone else but me
return user_stream(self.request.user)
return Action.objects.all()
filter_fields = (
'actor_content_type', 'actor_content_type__model',
'target_content_type', 'target_content_type__model',
)