Django Rest Framework - создание и обновление отношений между родителями и детьми - PullRequest
0 голосов
/ 03 января 2019

Я пытаюсь использовать сериализаторы Django Rest Framework для создания API-интерфейсов для моего интерфейса для создания / обновления и удаления статей в магазине.Эти статьи могут иметь несколько цен (в зависимости от времени).Таким образом, существует отношение «один ко многим» от статьи (один) к цене (многие).Я определил это в models.py:

class Article(models.Model):
    article_id = models.AutoField(primary_key=True, verbose_name="Article ID")
    article_name = models.CharField(max_length=250, verbose_name="Name")
    article_order = models.IntegerField(verbose_name="Order")

class Price(models.Model):
    price_id = models.AutoField(primary_key=True, verbose_name="Price ID")
    article_id = models.ForeignKey(Article, on_delete=models.CASCADE, verbose_name="Article ID", related_name="Prices")
    price_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="Price")

Мой файл serializers.py выглядит следующим образом:

from rest_framework import serializers
from .models import *

class PriceSerializer(serializers.ModelSerializer):

    class Meta:
        model = Price
        fields = ('price_price',)

class ArticleSerializer(serializers.ModelSerializer):

    Prices = PriceSerializer(many=True)

    def create(self, validated_data):
        prices_data = validated_data.pop('Prices')
        article = Article.objects.create(**validated_data)
        for price_data in prices_data:
            Price.objects.create(article_id=article, **price_data)
        return article

    def update(self, instance, validated_data):
        prices_data = validated_data.pop('Prices')
        Article.objects.filter(article_id=instance.article_id).update(**validated_data)
        for price_data in prices_data:
            Price.objects.get_or_create(article_id=instance, **price_data)
        return instance

    class Meta:
        model = Article
        fields = '__all__'

Это отлично работает, и я могу создать новое измерение цены товара с помощьюэти данные: (article_order будет использоваться позже для упорядочивания списка)

{"Prices":[{"price_price":"1"}],"article_name":"Article A","article_order":"1"}

До этого момента все работает как положено.Но когда я пытаюсь обновить цены, оператор Price.objects.get_or_create() не распознает существующие цены.Например: если первая цена равна 10 евро, затем 20 евро, а затем 10 евро, последняя цена не будет вставлена, поскольку оператор get_or_create не распознает это как новый экземпляр модели price.Это, вероятно, имеет смысл, потому что PriceSerializer не сериализует price_id.Но когда я изменяю сериализатор на:

class PriceSerializer(serializers.ModelSerializer):

    class Meta:
        model = Price
        fields = '__all__

, я больше не могу создавать экземпляры, потому что ArticleSerializer требует article_id для экземпляра Price (но его пока нет).

У кого-нибудь есть идеи, как решить эту проблему?Документация DRF включает только оператор create для этого типа вложенного сериализатора.

https://www.django -rest-framework.org / api-guide / Relations / # Writable-nested-serializers

Ответы [ 2 ]

0 голосов
/ 03 января 2019
  1. вы можете увидеть документ по остальным фреймворкам django: https://www.django -rest-framework.org / api-guide / serializers /

    def update(self, instance, validated_data): instance.email = validated_data.get('email', instance.email) instance.content = validated_data.get('content', instance.content) instance.created = validated_data.get('created', instance.created) return instance

Я надеюсь, что может помочь вам

0 голосов
/ 03 января 2019

Вы неправильно используете метод get_or_create. Он принимает kwargs, которые используются для поиска объекта, и, если он не существует, создает их, добавляя другие значения поля из defaults.

Это означает, что если вы передадите новые значения для обновления как kwargs, он не найдет такой объект, поскольку в настоящее время он имеет разные значения для этих полей. Вместо этого вы должны передать поля, которые не изменяются в kwargs, а остальные в defaults.

Это то, что вы должны сделать

def update(self, instance, validated_data):
    prices_data = validated_data.pop('Prices')
    Article.objects.filter(article_id=instance.article_id).update(**validated_data)
    for price_data in prices_data:
        Price.objects.get_or_create(article_id=instance, defaults=price_data)
    return instance

Обратите внимание, что вы можете получить ошибку MutipleObjectsReturned, поскольку статья может иметь несколько цен в зависимости от вашей архитектуры БД. Вы можете изменить article_id на OneToOneField, чтобы предотвратить это, если не предполагалось использование нескольких цен на статью.

Вы можете узнать больше о get_or_create в Django документах

В качестве дополнительного примечания, в архитектуре ООП id в Article уже подразумевается идентификатор статьи, поэтому добавление article к именам ваших полей является излишней и плохой практикой. И снова, вы можете захотеть использовать автоматически сгенерированное поле идентификатора Django вместо его переопределения, если только вы не хотите настраивать его каким-либо образом, что в настоящее время не очевидно в вашем коде

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...