【问题标题】:Django - How to filter children of a nested queryset?Django - 如何过滤嵌套查询集的子项?
【发布时间】:2020-12-24 08:35:36
【问题描述】:

我有一个名为 Menu 的模型,它与另一个名为 category 的模型具有多对多关系。这两个模型都有一个名为 is_active 的字段,它指示菜单或类别是否可用。 好的,然后我有一个名为 RestaurantMenus 的 api,它返回一家餐厅的所有活动菜单,并扩展了它们的类别,响应是这样的:

Menu 1
     Category 1
     Category 2
Menu 2
     Category 3
Menu 3
     Category 4
     Category 5
     Category 6

现在我尝试实现的是仅对那些处于活动状态的菜单和类别进行序列化(is_active = True)。过滤活动菜单很简单,但过滤它的子菜单是我正在努力解决的问题。

我的模特:

class Category(models.Model):
    menu = models.ForeignKey(Menu, related_name='categories', on_delete=models.CASCADE)
    name = models.CharField(max_length=200)

class Menu(models.Model):
    name = models.CharField(max_length=200)

我的序列化器:

class CategorySerializer(serializers.ModelSerializer):
    
    class Meta:
        model = Category
        fields = "__all__"

class MenuSerializer(serializers.ModelSerializer):
    categories = CategorySerializer(many=True, read_only=True)
    
    class Meta:
        model = Menu
        fields = "__all__"

附:类别模型本身与另一个模型称为项目具有多对多关系,该模型也具有 is_active 字段。我也希望这些效果相同,但我从问题中删除了它,因为我认为过程应该是相同的。所以实际上 api 响应是这样的:

Menu 1
     Category 1
          Item 1
          Item 2
          Item 3
     Category 2
          Item 4

【问题讨论】:

  • 请添加您的型号和序列化代码。

标签: django django-rest-framework


【解决方案1】:

使用SerializerMethodField 非常简单。

您可以在 get_ 内部进行过滤,序列化查询集并从该序列化查询集中返回数据。

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = "__all__"

class MenuSerializer(serializers.ModelSerializer):
    categories = serializers.SerializerMethodField('get_categories')

    def get_categories(self, menu):
        qs = Category.objects.filter(menu=menu, is_active=True)
        serializer = CategorySerializer(qs, many=True)
        return serializer.data
    
    class Meta:
        model = Menu
        fields = "__all__"

如果您有第三个嵌套类,您可以使用 get_itemsItemSerializer 将相同的简单逻辑应用于 CategorySerializer

这种方法允许您过滤 Menu 对象以检查您的 views.py 上的 is_active = True,因为这是业务逻辑。

【讨论】:

    【解决方案2】:

    在这里你可以使用 Django Prefetch 对象。这使您可以定义查询集以从中选择集合对象。

    因此,您可以像这样编写Menu 检索查询:

    from django.db.models import Prefetch
    
    menus = Menu.objects.filter(is_active=True).prefetch_related(Prefetch('categories', queryset=Category.objects.filter(is_active=True)))
    

    要将类别项也添加到结果集中,请使用以下查询:

    menus = Menu.objects.filter(is_active=True).prefetch_related(Prefetch('categories', queryset=Category.objects.filter(is_active=True)), Prefetch('categories__items', queryset=Item.objects.filter(is_active=True))))
    

    这应该可以解决您的问题。

    注意:我没有测试过代码,所以你可能需要做一些修改。

    【讨论】:

    • @VahidAl 总是乐于提供帮助:)
    【解决方案3】:

    我假设你这样定义你的模型

    Menu
        is_active
        categories: ManyToManyField (related_name='menus')
    
    Category
        is_active
        items: ManyToManyField (related_name='categories')
    
    Item
        is_active
    

    例如

    def get_categories():
        active_category_qs = Category.objects.filter(is_active=True)
        active_menu_qs = Menu.objects.filter(is_active=True, 
        categories__in=active_category_qs)
        categories = []
    
        for menu in active_menu_qs.iterator():
            for category in menu.categories.all():
                if category.id not in [
                        obj_target["id"] for obj_target in categories
                ]:
                    category_data = CategorySerializer(category).data # just get the data of category object
                    categories.append(category_data)
        return categories
    

    【讨论】:

      【解决方案4】:

      试试这个:

      class CategorySerializer(serializers.ModelSerializer):
          class Meta:
              model  = Category
              fields = "__all__"
      
      class MenuSerializer(serializers.ModelSerializer):
          category     = SerializerMethodField()
      
          def get_category(self,instance):
              qs = Category.objects.filter(menu__id=instance.id)
              data = CategorySerializer(qs,many=True).data
              return data
      
          class Meta:
              model  = Menu
              fields = "__all__"
      
      

      【讨论】:

        【解决方案5】:

        你可以这样尝试使用list_serializer_class:

        class FilterActiveListSerializer(serializers.ListSerializer):
            def to_representation(self, data):
                data = data.filter(is_active=True)
                return super(FilterActiveListSerializer, self).to_representation(data)
        
        class ItemSerializer(serializers.ModelSerializer):
            class Meta:
                model = Item
                list_serializer_class = FilterActiveListSerializer
        
        class CategorySerializer(serializers.ModelSerializer):
            items = ItemSerializer(many=True, read_only=True)
            class Meta:
                model = Category
                list_serializer_class = FilterActiveListSerializer 
        
        class MenuSerializer(serializers.ModelSerializer):
            categories = CategorySerializer(many=True, read_only=True)
            class Meta:
                model = Menu
        

        【讨论】:

          【解决方案6】:
          item_instance = Item.objects.filter(is_active = True, category__is_active=True)
          

          您将使用此获得所有具有活动类别的活动项目。

          获取所有项目并从中创建 JSON 数据。

          如果有帮助请点赞

          【讨论】:

          • 你好,你的答案不正确,因为这样你只能过滤菜单。
          猜你喜欢
          • 1970-01-01
          • 2021-09-21
          • 2016-02-01
          • 2011-06-19
          • 2019-12-04
          • 2017-04-13
          • 1970-01-01
          • 2017-07-15
          • 2020-08-22
          相关资源
          最近更新 更多