Django ManyToManyField с множеством одинаковых идентификаторов - PullRequest
4 голосов
/ 24 октября 2019

Я пытаюсь сделать простое приложение для заказа пиццы в Джанго. У меня есть 3 модели (топпинги, пиццы, заказы). В модели заказов есть ManyToManyField для пиццы. Работает нормально, если «пользователь» заказывает одну пиццу (например, Маргариту и Пепперони), но если заказать 2 Маргариту в POST-запросе, я получу только один идентификатор Маргариты в своем результате. Как я могу передать n-пиццу в одном заказе?

Мои модели выглядят так:

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

Я отправляю POST с этими данными:

{
    "items": [1, 1, 1, 2, 2],
    "status": 1
}

и получил только 1 и 2 в списке товаров (не 1, 1,1,2,2):

{
    "id": 2,
    "items": [
        1,
        2
    ],
    "status": 1
}

Сериализатор и просмотр для заказа

class OrdersSerializer(serializers.ModelSerializer):

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


class PizzaOrdersList(ModelViewSet):
    queryset = PizzaOrder.objects.all()
    serializer_class = OrdersSerializer

1 Ответ

5 голосов
/ 24 октября 2019

Сквозная модель идеально подходит для этого. ManyToManyFields создает этот вид базы данных в фоновом режиме, который создает только уникальные комбинации Pizza и PizzaOrder, но вы можете легко реализовать это самостоятельно.

Просто создайте класс с именем PizzaOrderItem, который содержит ForeignKeys для моделей Pizza и PizzaOrder:

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

Затем вы можете изменить ManyToManyField, чтобы использовать свой пользовательский, созданный с помощью модели:

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

Вы можете даже добавить дополнительные поля в своймодель, как начинки или количество, например:

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

        toppings = ManyToManyField(Toppings)
        quantity = IntegerField(default=1)

Пиццу можно получить в заказе через order.items, а фактические через объекты модели - через order.pizza_order_item_set.


Сериализаторы

Получить товары для заказа довольно просто. Чтобы правильно отобразить ваш новый 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']

Создание PizzaOrderItems

Существует два способа создания PizzaOrderItems. Первый довольно прост. Просто создайте представление, похожее на PizzaOrder, и используйте PizzaOrderItemSerializer:

class PizzaOrderItemViewSet(generics.ListCreateAPIView):
    queryset = PizzaOrderItem.objects.all()
    serializer_class = PizzaOrderItemSerializer

Затем вы можете создавать позиции для заказа, используя данные POST:

{
     "pizza" : <pizza_id_here>
     "pizza_order" : <pizza_order_id_here>
}

Второй способ - использовать только представление PizzaOrder и переопределить функции создания для сериализатора 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

ЕслиВы используете этот метод, вы можете создать заказ с данными POST это:

{
    "items": [
                  { "pizza" : 1 },
                  { "pizza" : 1 },
                  { "pizza" : 2 },
                  { "pizza" : 2 },
             ]
    "status": 1
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...