0

I'm trying to filter nested objects in API response by the ownership of currently authorized user.

Here is my setup: Django 1.8, Django REST framework 3.

MODELS

class Container(models.Model):
  container_title = models.CharField(max_length = 50)

class Item(models.Model):
  item_title = models.CharField(max_length = 50, blank = True, null = True, default = " ")
  item_container = models.ForeignKey(Container, on_delete = models.CASCADE, related_name = 'all_items_in_container')

class Notification(models.Model):
  owner = models.ForeignKey('auth.User', null = True, on_delete = models.CASCADE)
  item_to_track = models.ForeignKey(Container, default = False, blank = True, null = True, on_delete = models.CASCADE, related_name = 'notifications_for_container')

SERIALIZERS

class ItemsSerializer(serializers.HyperlinkedModelSerializer):
  class Meta:
    model = Item
    fields = ('pk', 'item_title')

class NotificationSerializer(serializers.HyperlinkedModelSerializer):
  owner = serializers.IntegerField(source='owner.pk')
  class Meta:
    model = Notification
    fields = ('pk', 'owner')

class ContainerSerializer(serializers.ModelSerializer):
  all_items_in_container = ItemsSerializer(many=True)
  #notifications_for_container = NotificationSerializer(many = True)  # this line returns all existing notifications per Container

  notifications_for_container = serializers.SerializerMethodField('get_current_user_notifications')

  def get_current_user_notifications(self, obj):
    user = self.context['request'].user
    user_notifications = Notification.objects.filter(item_to_track=obj, owner=user)
    serializer = NotificationSerializer(user_notifications)
    return serializer.data

  class Meta:
    model = Container
    fields = ('notifications_for_container', 'pk', 'container_title', 'all_items_in_container')

DRF VIEW

class ContainerViewSet(viewsets.ReadOnlyModelViewSet):

    queryset = Container.objects.all()

    def list(self, request, *args, **kwargs):
      queryset = self.queryset
      serializer = ContainerSerializer(data = [], many = True,  context={'request': request})

      if serializer.is_valid():
        new_serializer = ContainerSerializer(queryset, many = True, context={'request': request})
        data = new_serializer.data[:]

      return Response({'data':data})


    def retrieve(self, request, pk, *args, **kwargs):
      queryset = self.get_queryset()

      try:
        holder = Container.objects.get(pk=pk)
      except Container.DoesNotExist:
        holder = False

      if holder:
        serializer = ContainerSerializer(holder, context={'request': request})
        data = serializer.data

        return Response({'data':data})

Different users can create Notification objects for the same Container.

If i uncomment #notifications_for_container = NotificationSerializer(many = True), I'll get:

"data": [
    {
      "notifications_for_container": [
        {
          "pk": 11,
          "owner": 2,
        },
        {
          "pk": 20,
          "owner": 46,
        }
      ],
      "pk": 6,
      "container_title": "TEST CONTAINER",
      "all_items_in_holder": [
        {
          "pk": 12,
          "item_title": "xbox1 from amazon ",
        },
      ]
     }
    ]

no matter under which user I'm currently logged in.

What I'm trying to achieve here, is to return only owner's Notification in notifications_for_container section of API response.

Eg if I'm logged in as user with pk=2, I should get only

"data": [
    {
      "notifications_for_container": [
        {
          "pk": 11,
          "owner": 2,
        }
      ]
...
...

I've been trying to implement the solution from How can I apply a filter to a nested resource in Django REST framework?, but I'm getting KeyError at /api/containers/ 'request'

I've been trying to solve this problem for 2 days already and maybe I don't see something obvious, so any help would be appreciated. Thanks in advance!

0

2 Answers 2

1

first:

serializer = ContainerSerializer(many=True, context={'request': request})

or

serializer = ContainerSerializer(many=True, context=self.get_serializer_context())

second:

def get_current_user_notifications(self, obj):
    user = self.context['request'].user
    # user_notifications  is a queryset
    user_notifications = Notification.objects.filter(item_to_track=obj, owner=user) 
    # so you need add many=True
    serializer = NotificationSerializer(user_notifications, many=True)
    return serializer.data
Sign up to request clarification or add additional context in comments.

Comments

0

The problem with your code seems to be that the context attribute is not being populated with the current request. Take a look at Including extra context. Probably you should be doing something like:

     serializer = ContainerSerializer(many=True, context={'request': request})

7 Comments

It is hard to tell, what file has the statement serializer = NotificationSerializerForItemsHolder(user_notifications)? Is it possible to post your full traceback?
Pardon, my mistake, here is the full error text: AttributeError at /api/containers/ Got AttributeError when attempting to get a value for field 'owner' on serializer 'NotificationSerializer'. The serializer field might be named incorrectly and not match any attribute or key on the 'QuerySet' instance. Original exception text was: 'QuerySet' object has no attribute 'owner'.
Perhaps you should try to change source='owner.pk' to just source='pk'
Nope, it didn't help. it definitely fails on line serializer = NotificationSerializerForItemsHolder(user_notifications) but I cannot understand why. i get this in the traceback: myproj/api_app/views.py" in list 107. data = new_serializer.data myproj/api_app/serializers.py" in get_current_user_notifications 101. return serializer.data
Try to update the question with the full traceback. Reproducing the problem based on the information you posted is a lot of work and I lack the time (and lets be honest, the will) to do this properly but I can take a look at the full trace if you update your question with it.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.