Mohit Harshan
Mohit Harshan

Reputation: 1986

Polymorphic models serializer

I'm using a Polymorphic model for setting up notifications:

My models:

class Notification(PolymorphicModel):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    created_by = models.ForeignKey(ElsUser, on_delete=models.CASCADE, default=None, related_name="creatednotifications")
    created_on = models.DateTimeField(default=timezone.now)
    created_for = models.ForeignKey(ElsUser, on_delete=models.CASCADE, default=None, related_name="receivednotifications")
    read = models.DateTimeField(default=None, null=True, blank=True)
    message = models.CharField(default=None, blank=True, null=True, max_length=800)

    @property
    def total(self):
        return self.objects.filter(created_for=self.request.user).count()

    @property
    def unread(self):
        return self.objects.filter(created_for=self.request.user,read=None).count()

    @property
    def read(self):
        return self.objects.filter(created_for=self.request.user).exclude(read=None).count()    

class WorkflowNotification(Notification):
    # permission_transition = models.ForeignKey(WorkflowStatePermissionTransition, on_delete=models.CASCADE)
    action = models.ForeignKey(UserAction, on_delete=models.CASCADE)

Currently i have just one model WorkFlowNotification inheriting from the Polymorphic model,but many would be there in the future.

Im trying to get the count(total) of notifications for the logged in user in the API ..total is given as property field to help in the same

my serializer:

class NotificationSerializer(serializers.ModelSerializer):
    total = serializers.ReadOnlyField()
    read = serializers.ReadOnlyField()
    unread = serializers.ReadOnlyField()

    class Meta:
        model = Notification
        fields = ['id', 'total','read', 'unread']

In the view:

   class NotificationsMeta(generics.ListAPIView):
    serializer_class = NotificationSerializer
    queryset = Notification.objects.all()

When i try to run the server it shows:

  Got AttributeError when attempting to get a value for field `total` on serializer `NotificationSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `WorkflowNotification` instance.
Original exception text was: Manager isn't accessible via WorkflowNotification instances.

Upvotes: 1

Views: 3773

Answers (2)

mehamasum
mehamasum

Reputation: 5742

Since you need the 'meta data' only, what is the use of making a model serializer? Or any serializer, for that matter? Serializers will give you serialized instances of the objects of your model. So if you have multiple objects, you will get multiple serialized objects in response.

Just make your view a normal APIView. Since there is no necessity of serializing anything.

class NotificationsMeta(APIView):
    def get(self, request, format=None):
        qs = Notification.objects.filter(created_for=self.request.user)
        response = {
            'total': qs.count(),
            'read': qs.filter(read=None).count(),
            'unread': qs.exclude(read=None).count()
        }
        return Response(response)

Now remove those property functions from your model.

I didn't test your queries, just copied them from your model. You will need to check if they are working properly. Hope this helps.

Upvotes: 3

Shakil
Shakil

Reputation: 4630

I am not sure about how calling a model property who is responsible for querying in model can give appropriate data from serializer. Unfortunately i do have knowledge gap about that. I am thinking about an alternative solution. I hope following should work.

class NotificationSerializer(serializers.ModelSerializer):
    total = serializers.serializers.SerializerMethodField()

    read = serializers.ReadOnlyField()
    unread = serializers.ReadOnlyField()

    class Meta:
        model = Notification
        fields = ['read', 'unread']


    def get_total(self, obj):
        user =  self.context['request'].user 
        return Notification.objects.filter(created_for=user).count()

If this work then you can able to do similar kind of thing for read and unread too.

In order to get notification for current_user we need to overwrite get_queryset from view.

   class NotificationsMeta(generics.ListAPIView):
    serializer_class = NotificationSerializer
    def get_queryset(self):
       return Notification.objects.filter(created_for=self.request.user)

Upvotes: -1

Related Questions