在DRF中过滤嵌套视图,如何实现?

Filtering nested views in DRF, how to implement?

有一个产品序列化程序:

class ProductSerializer(serializers.ModelSerializer):
    category = serializers.SlugRelatedField(slug_field="name", read_only=True)

class Meta:
    model = Product
    fields=(
        'id',
        'name',
        'category', 
        'subcategory',
        'manufacturer',
        'get_absolute_url',
        'description',
        'price',
        'get_image',
        'get_thumbnail'
    )

以及生成与此类别关联的产品的嵌套表示的类别序列化程序:

class CategorySerializer(serializers.ModelSerializer):
    products = ProductSerializer(many=True)

class Meta:
    model = Category
    fields = (
        'id',
        'name',
        'get_absolute_url',
        'products'
    )

示例数据:

 [
    {
        "id": 4,
        "name": "Notebook",
        "get_absolute_url": "/notebooks/",
        "products": [
            {
                "id": 3,
                "name": "HP for work",
                "category": "Notebook",
                "subcategory": "Working",
                "manufacturer": "HP",
                "get_absolute_url": "/notebooks/hp_2000/",
                "description": "desc",
                "price": "25000.00",
                "get_image": "http://127.0.0.1:8000/media/uploads/slidebar-image1.jpg",
                "get_thumbnail": "http://127.0.0.1:8000/media/uploads/uploads/slidebar-image1.jpg"
            },
            {
                "id": 1,
                "name": "Acer for game",
                "category": "Notebook",
                "subcategory": "Gamers",
                "manufacturer": "Acer",
                "get_absolute_url": "/notebooks/acer_for_game/",
                "description": "Desc",
                "price": "50000.00",
                "get_image": "http://127.0.0.1:8000/media/uploads/AcerAspire_6m9A45R.jpg",
                "get_thumbnail": "http://127.0.0.1:8000/media/uploads/uploads/AcerAspire_6m9A45R.jpg"
            }
        ]
    }
]
 

我还可以使用查询参数在 URL 中显示过滤后的产品,例如,按子类别:

url = http://127.0.0.1:8000/api/products/query-product/?sub=Gamers:

[
{
    "id": 1,
    "name": "Acer for game",
    "category": "Notebook",
    "subcategory": "Gamers",
    "manufacturer": "Acer",
    "get_absolute_url": "/notebooks/acer_for_game/",
    "description": "Desc",
    "price": "50000.00",
    "get_image": "http://127.0.0.1:8000/media/uploads/AcerAspire_6m9A45R.jpg",
    "get_thumbnail": "http://127.0.0.1:8000/media/uploads/uploads/AcerAspire_6m9A45R.jpg"
}

]

这是过滤产品的地方:

class ProductQueryList(generics.ListAPIView):
   serializer_class = ProductSerializer

   def get_queryset(self):
       queryset = Product.objects.all()
       subcategory_name = self.request.query_params.get('sub')
       if subcategory_name is not None:
           queryset = queryset.filter(subcategory = subcategory_name)
       return queryset

我目前无法过滤类别。在这里,我要么传递一个包含特定类别的 queryset,要么将与该类别关联的筛选产品传递给相同的 queryset:

class CategoryDetail(generics.ListAPIView):
    serializer_class = CategorySerializer
def get_queryset(self):
    category = self.kwargs['category_slug']
    # queryset = Category.objects.get(slug=category)
    # subcategory_name = self.request.query_params.get('sub')
    # # if subcategory_name is not None:
    #     queryset = queryset.products.filter(subcategory = subcategory_name)
    return Category.objects.filter(slug=category)

有什么方法可以将 queryset.products 之类的内容传递给嵌套产品列表吗?那些以某种方式将类别传递给 queryset,并覆盖嵌套在其中的产品。

你可以在这里使用一个Prefetch对象来实现嵌套过滤,像这样:

class CategoryDetail(generics.ListAPIView):
    serializer_class = CategorySerializer
    
    def get_queryset(self):
        category = self.kwargs['category_slug']
        queryset = Category.objects.get(slug=category)
        
        subcategory_name = self.request.query_params.get('sub')
        if subcategory_name is not None:
            prefetch_filtered_products = Prefetch(
              'products', 
              Product.objects.filter(subcategory=subcategory_name)
            )
            return queryset.prefetch_related(prefetch_filtered_products)
        return queryset.prefetch_related('products')

这还可以避免您对数据库进行额外查询。