具有许多相同ID的Django ManyToManyField

Nat*_*ova 4 sql django foreign-keys django-rest-framework

我正在尝试在Django中制作一个简单的比萨订购应用程序。我有3个模型(浇头,比萨饼,订单)。在订单模型中,ManyToManyField为Pizza。如果“用户”订购每个比萨饼一个(例如,玛格丽塔和意大利辣香肠),则工作正常,但是如果在POST请求中订购2个玛格丽塔,则结果中只有一个玛格丽塔ID。我如何在一个订单中传递正宗披萨?

我的模型如下所示:

class Toppings(models.Model):
    name = models.CharField(blank=False, max_length=100, unique=True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name


class Pizza(models.Model):
    name = models.CharField(blank=False, max_length=100, unique=True)
    ingredients = models.ManyToManyField(Toppings, blank=False, null=False)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name


class PizzaOrder(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    items = models.ManyToManyField(Pizza, blank=False, null=False)
    status = models.CharField(max_length=1, choices=[
        (1, 'placed'),
        (2, 'approved'),
        (3, 'cooked'),
        (4, 'delivered'),
        (5, 'canceled')
    ], default=1)

    class Meta:
        ordering = ['created']

    def __str__(self):
        return self.created
Run Code Online (Sandbox Code Playgroud)

我用以下数据发送POST:

{
    "items": [1, 1, 1, 2, 2],
    "status": 1
}
Run Code Online (Sandbox Code Playgroud)

并在商品列表中获得了这个1和2(不是1,1,1,2,2):

{
    "id": 2,
    "items": [
        1,
        2
    ],
    "status": 1
}
Run Code Online (Sandbox Code Playgroud)

序列化器和订单视图

class OrdersSerializer(serializers.ModelSerializer):

    class Meta:
        model = PizzaOrder
        fields = ['id', 'items', 'status', 'created']


class PizzaOrdersList(ModelViewSet):
    queryset = PizzaOrder.objects.all()
    serializer_class = OrdersSerializer
Run Code Online (Sandbox Code Playgroud)

Nic*_*oen 5

贯通模型非常适合此操作。ManyToManyFields在后台创建这种数据库关系,这只会创建Pizza和PizzaOrder的唯一组合,但是您可以自己轻松实现它。

只需创建一个名为的类PizzaOrderItem,其中包含ForeignKeysPizza和PizzaOrder模型:

class PizzaOrderItem(models.Model):
    pizza = ForeignKey(Pizza)
    pizza_order = ForeignKey(PizzaOrder)
Run Code Online (Sandbox Code Playgroud)

然后,您可以更改ManyToManyField以使用通过模型创建的自定义:

class PizzaOrder(models.Model):
    ...
    items = models.ManyToManyField(Pizza, through='<your_app_name>.PizzaOrderItem',
        blank=False, 
        null=False)
    ...
Run Code Online (Sandbox Code Playgroud)

您甚至可以向贯通模型添加额外的字段,例如浇头或数量,如下所示:

class PizzaOrderItem(models.Model):
        pizza = ForeignKey(Pizza)
        pizza_order = ForeignKey(PizzaOrder)

        toppings = ManyToManyField(Toppings)
        quantity = IntegerField(default=1)
Run Code Online (Sandbox Code Playgroud)

您可以通过顺序访问披萨,通过order.items实际访问模型对象order.pizza_order_item_set


序列化器

检索订单中的物品非常简单。为了正确显示new PizzaOrderItems,您需要添加一个序列化器,并设置项目PizzaOrderSerializer以使用新的序列化器:

class PizzaOrderItemSerializer(serializer.ModelSerializer):
    class Meta:
        model = PizzaOrderItem
        # If you add a field like quantity you can add them to the fields list below.
        fields = ['pizza', 'pizza_order']


class PizzaOrderSerializer(serializer.ModelSerializer):
    items = PizzaOrderItemSerializer(source='pizza_order_item_set', many=True)

    class Meta:
        model = PizzaOrder
        fields = ['id', 'items', 'status', 'created']
Run Code Online (Sandbox Code Playgroud)

创建PizzaOrderItems

有两种创建PizzaOrderItems的方法。第一个非常简单。只需创建类似于的视图PizzaOrder,然后使用PizzaOrderItemSerializer

class PizzaOrderItemViewSet(generics.ListCreateAPIView):
    queryset = PizzaOrderItem.objects.all()
    serializer_class = PizzaOrderItemSerializer
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用POST数据为订单创建商品:

{
     "pizza" : <pizza_id_here>
     "pizza_order" : <pizza_order_id_here>
}
Run Code Online (Sandbox Code Playgroud)

第二种方法是仅使用PizzaOrder视图,并将create函数覆盖到PizzaOrder序列化程序。这是因为嵌套序列化程序默认情况下是只读的,因此我们在这里所做的是重写根序列化程序的create方法,并使用嵌套序列化程序的已验证数据创建子对象。

class PizzaOrderSerializer(serializer.ModelSerializer):
    ...

    def create(self, validated_data):
        items = validated_data.pop('items')
        order = PizzaOrder.objects.create(**validated_data)
        for item_data in items:
            PizzaOrderItem.objects.create(pizza_order=order, **item_data)
        return order
Run Code Online (Sandbox Code Playgroud)

如果使用此方法,则可以使用POST数据创建订单:

{
    "items": [
                  { "pizza" : 1 },
                  { "pizza" : 1 },
                  { "pizza" : 2 },
                  { "pizza" : 2 },
             ]
    "status": 1
}
Run Code Online (Sandbox Code Playgroud)