0

我想使用中间模型和自定义 query_param 呈现动态选择的对象。

例如,我想进行这样的查询:

http://api.url/v1/recipes/19/?location=1并根据作为 query_params 发送的位置获取带有其成分的序列化配方对象:

配方对象:

{
    "id": 19,
    "ingredients": [
        {
            "id": 35,           # Product id
            "name": "Tomato",   # Product name
            "price": 2.0,       # Product price
            "quantity": 3,
            "supplier": 12,     # Supplier at location=1
        },
        {
            "id": 36,           # Product id
            "name": "Cheese",   # Product name
            "price": 5.0,       # Product price
            "quantity": 2,
            "supplier": 12,
        },
    ],
    "title": "Pizza"
}

如果我更改?location=查询参数,我想获得相同的配方但获得另一个供应商的产品。

这是我当前的架构。但任何建议都非常感谢:

class Recipe(models.Model):
    title = models.CharField(max_length=255)

class IngredientRecipe(models.Model):
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
    quantity = models.FloatField(default=0.0)
    product_name = models.CharField(max_length=255)

    class Meta:
        unique_together = ['product_name', 'recipe']

class Supplier(models.Model):
    name = models.CharField(_('Name'), max_length=255)
    location = models.ForeignKey(Location, on_delete=models.CASCADE)

class Product(models.Model):
    name = models.CharField(max_length=255)
    supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
    price = models.FloatField()

我正在尝试根据供应商的位置序列化 Recipe 对象及其相关成分:

class IngredientSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = '__all__'

class RecipeSerializer(serializers.ModelSerializer):
    ingredients = serializers.SerializerMethodField()

    class Meta:
        model = Recipe
        fields = '__all__'

    def get_ingredients(self, obj):
        location_id = self.context['request'].query_params.get('location', None)
        product_names = IngredientRecipe.objects.filter(recipe__id=obj.id).values_list('product_name')

        # Here I got the related Products by name and if specified, the supplier's location:
        if location_id:
            qs = Product.objects.filter(
                (Q(supplier__location_id=location_id) | Q(supplier_id__isnull=True)) &
                Q(is_active=True) & Q(name__in=product_names)).values_list('id', 'name', 'price')
        else:
            qs = Product.objects.filter(Q(is_active=True) & Q(name__in=product_names)).values_list('id', 'name',
                                                                                                   'price')
        
        # Here I try to remove the duplicates and keed the cheapest price:
        duplicated_qs = list(set(qs))
        duplicated_qs = sorted(duplicated_qs, key=lambda tup: tup[2])
        keep = set()
        res = []
        for sub in duplicated_qs:
            if sub[1] not in keep:
                res.append(sub)
                keep.add(sub[1])
        ingredients_ids = [item[0] for item in res]

        # Once I have a product list I want to serialize it with the quantity information
        # and here is where I got stuck, cause I don't know how to render the context passed. 
        qs = Product.objects.filter(id__in=ingredients_ids)

        serializer = IngredientSerializer(qs, many=True, context={'quantity': obj.quantity})
        return serializer.data

谢谢大家!

4

1 回答 1

2

我通常这样做,通过覆盖 get_queryset 方法:

# In your view:
class RecipeView(viewsets.ModelViewSet):
    def get_queryset(self):
        result = Recipe.objects.all()

        location = self.request.GET.get("location", None)
        if location:
            result = result.filter(location__id=location)

        return result
于 2021-06-27T20:34:03.703 回答