Björn Kristinsson
Björn Kristinsson

Reputation: 624

Filtering a reverse relationship by user, and returning only one object in Django Rest Framework

Given these models:

class Product(models.Model):
    name = CharField(max_length=255)

class Order(models.Model):
    product = models.ForeignKey(Product)
    user = models.ForeignKey(User)
    quantity = models.PositiveIntegerField()

A user can only have a single Order object per product. I would like an API call to show a list of products, with the user's order where available. Is there a way to do that?

The default serialisation lists ALL orders under order_set. I did get a bit ahead with this to filter by user:

class FilteredOrderSerializer(serialisers.ListSerializer):
    def to_representation(self, data):
        data = data.filter(user=self.context['request'].user)
        return super(FilteredOrderSerializer, self).to_representation(data)


class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        list_serializer_class = FilteredOrderSerializer


class ProductSerializer(serializers.ModelSerializer):
    order_set = OrderSerializer(many=True, read_only=True)

    class Meta:
        model = Product

So now it only shows the authenticated user's order, but the resulting JSON looks something like

[
    {
        "name": "prod1",
        "order_set": [
            {
                "quantity": 4
            }
        ],
    }
]

i.e. the field is still called order_set and it's a list, while I would like it to be called order and be either an object or null. So I'm not sure where this filtering should take place, nor how to define the field.

Edit: I'm using a simple viewset

class ProductViewSet(view sets.ReadOnlyModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

Upvotes: 2

Views: 1445

Answers (1)

Bipul Jain
Bipul Jain

Reputation: 4643

You need to add related_name field in your orders so you can

product.orders.all()

Then this should do.

class Order(models.Model):
    product = models.ForeignKey(Product, related_name='orders')
    user = models.ForeignKey(User)
    quantity = models.PositiveIntegerField()


class ProductSerializer(serializers.ModelSerializer):
    order = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = ('name', 'order')

    def get_order(self, object):
        try:
            order = object.orders.get(user=self.context['request'].user)
            return SceneDetailSerializer(order).data
        except Order.DoesNotExist:
            return None

Update: You can try out serializer method field. Not sure if self contains context and user references in it. You need to remove listfield from your order serializer. Let me know if it works.

Upvotes: 2

Related Questions