首页 > 解决方案 > Django:使用嵌套模型从许多关系查询中进行复杂排序和过滤

问题描述

我想要实现的目标: 我想要列表

[
  {
    "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"
          }
        ]
      }
    ]
  }
]

我的视图集是

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

我正在尝试根据类型进行过滤,但无法获取我需要的内容。我得到的结果是

[
  {
    "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"
          }
        ]
      }
    ]
  }
]

如何自定义过滤器或更改嵌套查询以获取所需的输出以及如何按 id 排序。直到第二个嵌套级别,我才能获取数据(也许并不复杂),但在第三级,我面临这个问题。

模型.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
        

序列化程序.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})
      }

预取后,我得到的 json 格式为

{
        "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": []
            }
        ]
    }

有没有办法消除和获取唯一需要的?

使用with过滤方法后,得到的json格式为

.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"
                    }
                ]
            }
        ]
    },

更新代码后的输出

 [
    {
        "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"
                    }
                ]
            }
        ]
    }
]

标签: djangodjango-rest-frameworkdjango-viewsdjango-filter

解决方案


试试这个:

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

这将删除所有没有具有您要查找的值的详细信息的会话,并将按其 ID 对这些会话中的所有详细信息进行排序。


推荐阅读