Django: complex order by and filter by from many relations query with nested models

Viewed 311

What I want to achieve: I want list of

[
  {
    "location":"Loc 1",
    "session":[
      {
        "start":"2021-01-01",
        "counts":600,
        "details":[
          {
            "id":13,
            "length_max":21,
            "length_min":15,
            "length_avg":16,
            "length_std":19,
            "is_active":false,
            "type":"dog"
          }
        ]
      }
    ]
  },
  {
    "location":"Loc3",
    "session":[
      {
        "start":"2021-01-01",
        "counts":500,
        "details":[
          {
            "id":15,
            "length_max":19,
            "length_min":16,
            "length_avg":16,
            "length_std":19,
            "is_active":false,
            "type":"dog"
          }
        ]
      }
    ]
  }
]

My Viewset is

class SessionFilter(FilterSet):
    type_filter = filters.CharFilter(method="filter_by_type")
    

    def filter_by_type(self,queryset,name,value):
        queryset = queryset.filter(session__details__type=value).distinct()
        return queryset

class SessionModelViewSet(ModelViewSet):
    queryset = Session.objects.all()
    serializer_class = SessionSerializers
    filter_backends = (DjangoFilterBackend,)
    filter_class = SessionFilter

I am trying to filter based on type, but am not able to fetch what I need. The out I am getting is

[
  {
    "location":"Loc 1",
    "session":[
      {
        "start":"2021-01-01",
        "counts":600,
        "details":[
          {
            "id":13,
            "length_max":21,
            "length_min":15,
            "length_avg":16,
            "length_std":19,
            "is_active":false,
            "type":"dog"
          }
        ]
      },
      {
        "start":"2021-01-01",
        "counts":600,
        "details":[
          {
            "id":7,
            "length_max":39,
            "length_min":25,
            "length_avg":25,
            "length_std":27,
            "is_active":true,
            "type":"cat"
          },
          {
            "id":19,
            "length_max":39,
            "length_min":25,
            "length_avg":25,
            "length_std":27,
            "is_active":false,
            "type":"cat"
          }
        ]
      }
    ]
  },
  {
    "location":"Loc3",
    "session":[
      {
        "start":"2021-01-01",
        "counts":500,
        "details":[
          {
            "id":15,
            "length_max":19,
            "length_min":16,
            "length_avg":16,
            "length_std":19,
            "is_active":false,
            "type":"dog"
          }
        ]
      },
      {
        "start":"2021-01-01",
        "counts":500,
        "details":[
          {
            "id":9,
            "length_max":39,
            "length_min":25,
            "length_avg":25,
            "length_std":27,
            "is_active":true,
            "type":"cat"
          },
          {
            "id":21,
            "length_max":39,
            "length_min":25,
            "length_avg":25,
            "length_std":27,
            "is_active":false,
            "type":"cat"
          }
        ]
      }
    ]
  }
]

How can I customise the filter or change the nested query to fetch the desired output and how can I order by id. Till the second nested level, I am able to fetch data (maybe it is not complicated) but in the 3rd level, I am facing this issue.

Model.py

class Place(models.Model):
    id = models.IntegerField(primary_key=True)
    location = models.CharField(max_length=100)

    class Meta:
        db_table = 'place'
        managed=False
        
        
class Session(models.Model):
    id = models.IntegerField(primary_key=True)
    place = models.ForeignKey(Place,related_name='session',on_delete=models.CASCADE, null=True)
    start = models.DateField(auto_now=True)
    counts = models.IntegerField()

    class Meta:
        db_table = 'session'
        managed=False


class Animal(models.Model):
    id = models.IntegerField(primary_key=True)
    sess = models.ForeignKey(Session,related_name='details',on_delete=models.CASCADE, null=True)
    type = models.CharField(max_length=100)
    is_active = models.BooleanField()
    length_max = models.IntegerField()
    length_min = models.IntegerField()
    length_avg = models.IntegerField()
    length_std = models.IntegerField()

    class Meta:
        db_table = 'animal'
        managed=False
        

serializer.py

class AnimalSerializers(FlexFieldsModelSerializer):
    class Meta:
        model = Animal
        fields = ["id","length_max","length_min","length_avg","length_std","is_active","type"]


class SessionSerializers(FlexFieldsModelSerializer):
    class Meta:
        model = Session
        fields = ["start","counts","details"]
        expandable_fields = {
          'details': (AnimalSerializers, {'many': True})
        }


class PlaceSerializers(FlexFieldsModelSerializer):
  class Meta:
      model = Place
      fields = ["location","session"]
      expandable_fields = {
        'session': (SessionSerializers, {'many': True})
      }

After prefetch I am getting the json format as

{
        "location": "Loc 2",
        "session": [
            {
                "start": "2021-01-01",
                "counts": 300,
                "details": [
                    {
                        "id": 14,
                        "length_max": 22,
                        "length_min": 16,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": true,
                        "type": "dog"
                    }
                ]
            },
            {
                "start": "2021-01-01",
                "counts": 300,
                "details": []
            }
        ]
    }

is there a way to eliminate and fetch the only one which is required?

After using with filter method, the obtained json format is

.prefetch_related(
    Prefetch('session', queryset=Session.objects.filter(details__type=value)),
    Prefetch('session__details', queryset=Details.objects.all().order_by('id'))),
)


[
    {
        "location": "Loc 3",
        "session": [
            {
                "start": "2021-01-01",
                "counts": 500,
                "details": [
                    {
                        "id": 15,
                        "length_max": 19,
                        "length_min": 16,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            }
        ]
    },
    {
        "location": "Loc 4",
        "session": [
            {
                "start": "2021-01-02",
                "counts": 800,
                "details": [
                    {
                        "id": 1,
                        "length_max": 24,
                        "length_min": 18,
                        "length_avg": 25,
                        "length_std": 27,
                        "is_active": false,
                        "type": "cat"
                    },
                    {
                        "id": 4,
                        "length_max": 24,
                        "length_min": 18,
                        "length_avg": 25,
                        "length_std": 27,
                        "is_active": false,
                        "type": "cat"
                    },
                    {
                        "id": 16,
                        "length_max": 24,
                        "length_min": 18,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            },
            {
                "start": "2021-01-02",
                "counts": 800,
                "details": [
                    {
                        "id": 10,
                        "length_max": 29,
                        "length_min": 16,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    },
                    {
                        "id": 22,
                        "length_max": 29,
                        "length_min": 16,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            }
        ]
    },

Output after updating the code

 [
    {
        "location": "Loc 1",
        "session": [
            {
                "start": "2021-01-01",
                "counts": 600,
                "details": [
                    {
                        "id": 13,
                        "length_max": 21,
                        "length_min": 15,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            }
        ]
    },
    {
        "location": "Loc 2",
        "session": [
            {
                "start": "2021-01-01",
                "counts": 300,
                "details": [
                    {
                        "id": 14,
                        "length_max": 22,
                        "length_min": 16,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": true,
                        "type": "dog"
                    }
                ]
            }
        ]
    },
    {
        "location": "Loc 3",
        "session": [
            {
                "start": "2021-01-01",
                "counts": 500,
                "details": [
                    {
                        "id": 15,
                        "length_max": 19,
                        "length_min": 16,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            }
        ]
    },
    {
        "location": "Loc 4",
        "session": [
            {
                "start": "2021-01-02",
                "counts": 800,
                "details": [
                    {
                        "id": 16,
                        "length_max": 24,
                        "length_min": 18,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            },
            {
                "start": "2021-01-02",
                "counts": 800,
                "details": [
                    {
                        "id": 10,
                        "length_max": 29,
                        "length_min": 16,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    },
                    {
                        "id": 22,
                        "length_max": 29,
                        "length_min": 16,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            }
        ]
    },
    {
        "location": "Loc 5",
        "session": [
            {
                "start": "2021-01-02",
                "counts": 400,
                "details": [
                    {
                        "id": 17,
                        "length_max": 28,
                        "length_min": 19,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": true,
                        "type": "dog"
                    }
                ]
            },
            {
                "start": "2021-01-02",
                "counts": 400,
                "details": [
                    {
                        "id": 11,
                        "length_max": 38,
                        "length_min": 28,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    },
                    {
                        "id": 23,
                        "length_max": 38,
                        "length_min": 28,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": true,
                        "type": "dog"
                    }
                ]
            }
        ]
    },
    {
        "location": "Loc 6",
        "session": [
            {
                "start": "2021-01-02",
                "counts": 450,
                "details": [
                    {
                        "id": 18,
                        "length_max": 35,
                        "length_min": 26,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            },
            {
                "start": "2021-01-02",
                "counts": 450,
                "details": [
                    {
                        "id": 12,
                        "length_max": 15,
                        "length_min": 13,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": true,
                        "type": "dog"
                    },
                    {
                        "id": 24,
                        "length_max": 15,
                        "length_min": 13,
                        "length_avg": 16,
                        "length_std": 19,
                        "is_active": false,
                        "type": "dog"
                    }
                ]
            }
        ]
    }
]

1 Answers

Try with this:

.prefetch_related(
    Prefetch('session', queryset=Session.objects.filter(details__type=value)),
    Prefetch('session__details', queryset=Details.objects.filter(type=value).order_by('id'))),
)

This will remove all sessions which don't have details that have the value you are looking for, and will sort all the details within those sessions by their ids.

Related