Home > Back-end >  Django - filtering related objects
Django - filtering related objects

Time:10-13

I have a puzzle.

These are my models:

class StatusGroup(models.Model):

    name = models.TextField()

    def __str__(self):
        return self.name


class StatusDetail(models.Model):
    action = models.CharField(choices=[("CORRECT", "CORRECT"),
                                       ("INCORRECT", "INCORRECT")],
                              max_length=64)
    status_group = models.ForeignKey(to=StatusGroup,
                                      on_delete=models.CASCADE,
                                      related_name="status_details")

    def __str__(self):
        return f"Detail: {self.action}"

serializers:

class StatusDetailSerializer(serializers.ModelSerializer):

    class Meta:
        model= models.StatusDetail
        fields = "__all__"

class StatusGroupSerializer(serializers.ModelSerializer):
    status_details = StatusDetailSerializer(many=True)

    class Meta:
        model = models.StatusGroup
        fields = [
            "pk",
            "status_details",
            "name"
        ]

And a view:

class Status(viewsets.ModelViewSet):

    queryset = models.StatusGroup.objects.all()
    serializer_class = serializers.StatusGroupSerializer
    authentication_classes = []
    permission_classes = [permissions.AllowAny]

    filter_backends = (DjangoFilterBackend,)
    filterset_fields = ['status_details__action']


When I hit localhost:8000/api/status?status_details__action=INCORRECT

I get:

[
    {
        "pk": 2,
        "status_details": [
            {
                "id": 3,
                "action": "CORRECT",
                "status_group": 2
            },
            {
                "id": 4,
                "action": "INCORRECT",
                "status_group": 2
            }
        ],
        "name": "Mixed"
    }
]

Whereas I would like to have:

[
    {
        "pk": 2,
        "status_details": [
            {
                "id": 4,
                "action": "INCORRECT",
                "status_group": 2
            }
        ],
        "name": "Mixed"
    }
]

How do I force Django to filter the related objects? I can get the result I want in SQL console, but Django adds, all the related objects that belong to the StatusGroup. I have a misconception, but I don't know what that is.

CodePudding user response:

try this way (using query set):

from django.db.models import Prefetch

queryset = StatusGroup.objects.prefetch_related(
Prefetch('status_details', queryset=StatusDetail.objects.filter(action='what_you_want'), to_attr='action'))

and in your serializer class:

class StatusGroupSerializer(serializers.ModelSerializer):
status_details = StatusDetailSerializer(source='action', many=True, read_only=True)

CodePudding user response:

First you can create a FilterSet class like this:

from django_filters import rest_framework as filters

class StatusGroupFilter(filters.FilterSet):
    action = filters.CharFilter(
        field_name="status_details__action",
        lookup_expr="iexact"
    )

    class Meta:
        model = StatusGroup
        fields = ["action"]

Then in your views:

class Status(viewsets.ModelViewSet):

    queryset = models.StatusGroup.objects.all()
    serializer_class = serializers.StatusGroupSerializer
    authentication_classes = []
    permission_classes = [permissions.AllowAny]

    filter_backends = (DjangoFilterBackend,)
    # Set the filterset class here
    filterset_class = StatusGroupFilter

Then in the URL you can call localhost:8000/api/status?action=INCORRECT

  • Related