Сериализатор DRF для вложенного JSON с возможностью записи

Во-первых, я хотел бы упомянуть, что я уже видел много сообщений SO об этой проблеме, но все еще не мог понять проблему, возникающую здесь. Я хотел бы отправить объект JSON в свое приложение Django, которое выглядит так:

{
    "id": "1363362773409783808",
    "text": "@MsLaydeeLala Damn haha. Reminds me of my dog. If someone comes into my room while I\u2019m sleeping he gets up and growls at them if he doesn\u2019t recognize them right away.",
    "created_at": "2021-02-21T05:39:49.000Z",
    "author": {
        "id": "112233445566778899",
        "username": "Elrafa559",
        "name": "EL RAFA"
    },
    "keywords": [
        {
            "name": "dog"
        }
    ]
}

и сохраните его в моей базе данных, где находятся мои модели:

class Keyword(models.Model):
    """
        Represents keywords which we are looking for, must be unique, 20 chars max.
    """
    name = models.CharField(max_length=20, unique=True)

    def __str__(self):
        return self.name


class TwitterAccount(models.Model):
    id = models.CharField(max_length=20, primary_key=True)
    username = models.CharField(max_length=80)
    name = models.CharField(max_length=80)


class Tweet(models.Model):
    id = models.CharField(max_length=20, primary_key=True)
    text = models.CharField(max_length=380)  # 280 for tweet, 100 for links
    created_at = models.DateTimeField()

    author = models.ForeignKey(TwitterAccount, on_delete=models.CASCADE)

    keywords = models.ManyToManyField(Keyword)

    def __str__(self):
        return str(self.author.username) + "/" + str(self.id)


class TweetKeyword(models.Model):
    # TODO is CASCADE the right choice?
    tweet = models.ForeignKey(Tweet, on_delete=models.CASCADE)
    keyword = models.ForeignKey(Keyword, on_delete=models.CASCADE)

для этого я написал этот сериализатор:

class TweetSerializer(serializers.ModelSerializer):
    author = TwitterAccountSerializer()
    keywords = KeywordSerializer(many=True)

    class Meta:
        model = Tweet
        fields = ["id", "text", "created_at", "author", "keywords"]
        # depth = 1

    def create(self, validated_data):
        print(validated_data)
        try:
            author = validated_data.pop('author')
            author, did_create = TwitterAccount.objects.update_or_create(**author)
            print('644444')
            tweet = Tweet.objects.update_or_create(author=author, **validated_data)

            keywords_data = validated_data.pop('keywords')
            for keyword_data in keywords_data:
                Keyword.objects.update_or_create(**keyword_data)

            return tweet

        except Exception as e:
            print("Exception occured in method .create in TweetSerializer")
            print(e.args)
            return None

ошибка, которую я получаю в методе создания TweetSerializer:

("Field 'id' expected a number but got [OrderedDict([('name', 'dog')])].",)
('`create()` did not return an object instance.',)

кто-нибудь может объяснить, почему поле keywords обрабатывается так, как если бы оно было id?


person Danialz    schedule 21.02.2021    source источник


Ответы (1)


Один из способов сделать то, что вы хотите, это:

  1. извлекать данные ключевых слов из проверенных данных
  2. создать объект твита без ключевых слов
  3. в цикле for создайте каждое ключевое слово и свяжите его с объектом твита

Вот код (я удалил авторскую часть):

def create(self, validated_data):
    keywords_data = validated_data.pop("keywords")
    tweet = Tweet.objects.update_or_create(**validated_data)[0]
    for keyword_data in keywords_data:
        keyword = Keyword.objects.update_or_create(**keyword_data)[0]
        tweet.keywords.add(keyword)
    return tweet

Вы можете прочитать документацию DRF об этом Там нет точного примера, но есть и другие и много объяснений.

person Roman Mkrtchian    schedule 21.02.2021