Home > Back-end >  How to filter ManyToManyField in serializer
How to filter ManyToManyField in serializer

Time:09-29

In my code ServiceListSerializer(many=True, source='service_id') is giving all the services list, instead of services according to its category

I have no idea how to retrieve and filter from manytomany field

Heres my code:

Models.py

class Services(models.Model):
    service_id = models.AutoField(primary_key=True)
    parent_id = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True,related_name='sub_service')
    service_name = models.CharField(max_length=100)
    service_icon = models.CharField(max_length=500, null=True, blank=True)
    service_image = models.CharField(max_length=500, null=True, blank=True)
    service_description = models.CharField(max_length=5000, null=True, blank=True)
    category_id = models.ForeignKey(Category,on_delete=models.CASCADE)
    active_status = models.BooleanField(default=True)
    type = models.SmallIntegerField(blank=True, null=True)


class Variant(models.Model):
    variant_id = models.AutoField(primary_key=True)
    service_id = models.ManyToManyField(Services)
    category_id = models.ManyToManyField(Category)
    variant_name = models.CharField(max_length=100)
    variant_icon = models.CharField(max_length=1000, null=True, blank=True)
    variant_image = models.CharField(max_length=1000, null=True, blank=True)
    variant_description = models.CharField(max_length=5000, null=True, blank=True)
    active_status = models.BooleanField(default=True)

View.py

class ServicesList(viewsets.ViewSet):

    def list(self, request):
        variant_id = request.data.get('variant_id')
        queryset = Category.objects.all()
        querysetSerializer = CategoryServiceListSerializer(queryset, many=True, context={'variant_id': variant_id})
        return Response({"status": 200, "message": "Success", "data": querysetSerializer.data})

Serializer.py

class CategoryServiceListSerializer(serializers.ModelSerializer):
    variant = serializers.SerializerMethodField()

    def get_variant(self, instance):
        variant_id = self.context.get('variant_id')
        if variant_id:
            variants = instance.variant_set.filter(pk=variant_id)
        else:
            variants = instance.variant_set.all()
        return VariantServiceSerializer(variants, many=True).data

    class Meta:
        fields = ("category_id", "category_name", "category_icon", "category_description", "variant")
        model = Category


class ServiceListSerializer(serializers.ModelSerializer):

    class Meta:
        model = Services
        fields = "__all__"

class VariantServiceSerializer(serializers.ModelSerializer):
    services = ServiceListSerializer(many=True, source='service_id')
    # services = serializers.SerializerMethodField()

    class Meta:
        model = Variant
        fields = "__all__"

    # def get_services(self, obj):
    #     print(obj.service_id)
    #     return obj

Output:

{
            "category_id": 1,                        # <
            "category_name": "category1",
            "variant": [
                {
                    "variant_id": 2,
                    "variant_name": "variant2",
                    "services": [
                        {
                            "service_id": 2,
                            "service_name": "service2",
                            "category_id": 1                
                        },
                        {
                            "service_id": 1,
                            "service_name": "service1",
                            "category_id": 1
                        },
                        {
                            "service_id": 1,
                            "service_name": "service1",
                            "category_id": 2                     #Category Not match with parent
                        }
                    ]
                }
            ]
        },
        {
            "category_id": 2,                        # <
            "category_name": "categoy2",
            "variant": [
                {
                    "variant_id": 2,
                    "variant_name": "variant2",
                    "services": [
                        {
                            "service_id": 2,
                            "service_name": "service2",
                            "category_id": 1                     #Category Not match with parent
                        },
                        {
                            "service_id": 1,
                            "service_name": "service1",
                            "category_id": 1                     #Category Not match with parent
                        },
                        {
                            "service_id": 1,
                            "service_name": "service1",
                            "category_id": 2
                        }
                    ]
                }
            ]
        },
        {
            "category_id": 3,                        # <
            "category_name": "category3",
            "variant": [
                {
                    "variant_id": 2,
                    "variant_name": "variant2",
                    "services": [
                        {
                            "service_id": 2,
                            "service_name": "service2",
                            "category_id": 1                     #Category Not match with parent
                        },
                        {
                            "service_id": 1,
                            "service_name": "service1",
                            "category_id": 1                     #Category Not match with parent
                        },
                        {
                            "service_id": 1,
                            "service_name": "service1",
                            "category_id": 2                     #Category Not match with parent
                        }
                    ]
                }
            ]
        }

Expected Output:

{
            "category_id": 1,
            "category_name": "category1",
            "variant": [
                {
                    "variant_id": 2,
                    "variant_name": "variant2",
                    "services": [
                        {
                            "service_id": 2,
                            "service_name": "service2",
                            "category_id": 1
                        },
                        {
                            "service_id": 1,
                            "service_name": "service1",
                            "category_id": 1
                        }
                    ]
                }
            ]
        },
        {
            "category_id": 2,
            "category_name": "categoy2",
            "variant": [
                {
                    "variant_id": 2,
                    "variant_name": "variant2",
                    "services": [
                        {
                            "service_id": 1,
                            "service_name": "service1",
                            "category_id": 2
                        }
                    ]
                }
            ]
        },
        {
            "category_id": 3,
            "category_name": "category3",
            "variant": [
                {
                    "variant_id": 2,
                    "variant_name": "variant2",
                    "services": []
                }
            ]
        }

CodePudding user response:

Try to Prefetch the related services of a variant and filter only with the matching category based on the category instance like this:

class CategoryServiceListSerializer(serializers.ModelSerializer):
    variant = serializers.SerializerMethodField()

    def get_variant(self, instance):
        variant_id = self.context.get('variant_id')
        if variant_id:
            variants = instance.variant_set.filter(pk=variant_id)
        else:
            variants = instance.variant_set.all()

        variants = variants.prefetch_related(
            Prefetch(
                'services'
                queryset=Services.objects.filter(category_id=instance.category_id)
            )
        )

        return VariantServiceSerializer(variants, many=True).data
  • Related