Django:使用嵌套模型从许多关系查询中进行复杂排序和过滤

Posted

技术标签:

【中文标题】Django:使用嵌套模型从许多关系查询中进行复杂排序和过滤【英文标题】:Django: complex order by and filter by from many relations query with nested models 【发布时间】:2021-09-28 01:54:02 【问题描述】:

我想要达到的目标: 我要列表

[
  
    "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 排序。直到第二个嵌套级别,我才能获取数据(也许并不复杂),但在第三级,我遇到了这个问题。

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)
      

预取后我得到的 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"
                    
                ]
            
        ]
    
]

【问题讨论】:

你能分享你的序列化器和模型吗? 无论如何,您都可以尝试使用过滤后的查询集添加预取,如下所示:.prefetch_related(Prefetch('session__details', queryset=Details.objects.filter(type=value).order_by('id'))) @bdbd,感谢您的快速回复。它帮助了我。但是,现在数据与上一阶段一起显示,下一阶段数据为空。 @bdbd,如果订单是在较低的阶段 id 上制作的,订单会不会起作用?因为那部分没有用。请给我建议,如果有任何其他选择。 【参考方案1】:

试试这个:

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

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

【讨论】:

再次感谢,上面的数据对前 3 个数据集运行良好,从第 4 个数据集中,过滤和未过滤的数据都来了。排序也没有按预期工作。 我更新了我的答案以在session__details 中过滤Details。可以再试一次吗? 您还想按会话还是按细节订购? 好像已经下单了。还是您希望它下降? 我只尝试按升序排列,前 4 个数字,即 13、14、15、16 个 id 是有序的,之后不是。过滤器按预期工作,而不是按顺序工作。

以上是关于Django:使用嵌套模型从许多关系查询中进行复杂排序和过滤的主要内容,如果未能解决你的问题,请参考以下文章

Django Serializer 嵌套创建:如何避免对关系的 N+1 查询

在 django 中获取具有多对多关系的复杂查询集

具有三层深度嵌套模型的查询集过滤器(多个一对多关系)

在 Django 中进行一个复杂的查询,我所有的后续帖子

Django ORM 嵌套模型

Django-使用 ManyToManyField 查询嵌套模型中的重复查询