【问题标题】:Post request Manytomany field DRF with postman使用邮递员发布请求 Manytomany 字段 DRF
【发布时间】:2021-07-24 10:02:09
【问题描述】:

目标:从 Postman 使用“POST”在数据库中创建一个新条目。

我正在尝试从 Postman 发送数据,并且我正在使用嵌套序列化。我已经更改了我在下面分享了 sn-p 的创建方法。另外,我试过这个solution,但没有用。有人可以指出我犯的错误吗?

当我尝试作为表单数据发布时,错误是{"magazine":["This field is required."]}。 当我尝试将其作为原始数据发布时,错误为Direct assignment to the forward side of a many-to-many set is prohibited. Use magazine.set() instead.

这是我的模型:

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    author = models.ForeignKey('authors.Author', on_delete=models.CASCADE)
    magazine = models.ManyToManyField('articles.Magazine')

    def __str__(self):
        return self.title


class Magazine(models.Model):
    name = models.CharField(max_length=30)
    title = models.CharField(max_length=100)

    def __str__(self):
        return self.name

这是我的序列化器:

class MagazineSerializer(serializers.ModelSerializer):
    class Meta:
        model = Magazine
        fields = '__all__'

class ArticleSerializer(serializers.ModelSerializer):
    author = AuthorSerializer(read_only=True, many=False)
    magazine = MagazineSerializer(many=True)
    class Meta:
        model = Article
        fields = [
            'title',
            'content',
            'author',
            'magazine',
        ]

    def create(self, validated_data):
        allmags = []
        magazine = validated_data.pop('magazine')
        for i in magazine:
            if Magazine.objects.get(id=magazine["id"]).exists():
                mags = Magazine.objects.get(id=magazine["id"])
                allmags.append(mags)
            else:
                return Response({"Error":  "No such magazine exists"}, status=status.HTTP_400_BAD_REQUEST)
            
        validated_data['author'] = self.context['request'].user
        validated_data['magazine'] = allmags
        return Article.objects.create(**validated_data)

这是我的看法:

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer

class MagazineViewSet(viewsets.ModelViewSet):
    queryset = Magazine.objects.all()
    serializer_class = MagazineSerializer

    serializer_action_class = {
        'get_articles': MagazineSerializer,
    }

    @action(detail=True, url_path='articles', url_name='articles')
    def get_articles(self, request, pk=None):
        articles = Article.objects.filter(magazine=self.kwargs.get('pk'))
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data, status=200)

这就是我尝试发送原始数据的方式:

{
    "title": "New Post form Postman",
    "content": "Postman content new",
    "magazine": [
        {
            "id": 1,
            "name": "The Times",
            "title": "All News"
        }
    ]
}

This is how I posted as form-data:

【问题讨论】:

    标签: django django-models django-rest-framework django-views postman


    【解决方案1】:

    首先,您能否澄清一下您是否还希望从嵌套序列化程序中创建新的Magazine 对象,或者您是否希望用户只能为现有杂志创建Article(这似乎是您的第二次打电话给.exists())?

    第一种情况:

    如果您打算在第一种情况下,也希望在同一个 POST 请求中创建新杂志,我建议使用 drf-writeable-nested 包。

    第二种情况:

    为此,您也应该使用drf-writeable-nested 包。或者,您可以改用这个 hack

    from rest_framework.exceptions import ValidationError
    from rest_framework import serializers
    from .models import Article, Magazine
    
    class ArticleSerializer(serializers.ModelSerializer):
        author = AuthorSerializer(read_only=True, many=False)
        magazines = MagazineSerializer(many=True, read_only=True)
        # accept list of PKs
        magazines_ids = serializers.PrimaryKeyRelatedField(
            many=True, write_only=True, queryset=Magazine.objects.all()
        )
        class Meta:
            model = Article
            fields = [
                'title',
                'content',
                'author',
                'magazines',
                'magazines_ids',
            ]
    
        def create(self, validated_data):
            magazines = validated_data.pop("magazines_ids", None)
            validated_data["author"] = self.context["request"].user
            article = Article.objects.create(**validated_data)
            if magazines:
                article.magazine.set(magazines)
    
            return article 
    
    
    

    现在您的 POST 请求 JSON 正文应如下所示:

    {
        "title": "New Post form Postman",
        "content": "Postman content new",
        "magazines_ids": [1]
    }
    

    magazines 参数获取主键列表。


    奖金

    另外,出于我的好奇,您确定要使用 ManytoManyField 吗?我会假设 Article 只能属于 single Magazine 您应该使用 ForeignKey,例如:

    magazine = models.ForeignKey("articles.Magazine", related_name="articles")
    

    然后在你的“行动”中,你可以做出这样的改变:

    @action(detail=True, url_path='articles', url_name='articles')
    def get_articles(self, request, pk=None):
        articles = self.get_object().articles
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data, status=200)
    

    【讨论】:

    • 是的,我正在尝试实现您所说的第二种情况,我确实尝试过这样做,但我不断收到错误"Invalid data. Expected a dictionary, but got int.". 我也尝试传入整个对象,但是它不起作用。
    • 我也尝试了你的解决方案,我得到了AttributeError: 'Article' object has no attribute 'magazine_set'
    • 其实我做的正好相反,我在尝试创建文章并将其映射到多个杂志,即:一篇文章可以在许多杂志上发表,因此是ManytoMany字段。
    • 我只是大大改进了我的答案,也让它变得简短。它现在应该可以工作了。
    • 很抱歉没有传递正确的参数,它工作正常,但现在我可以看到数据没有显示嵌套序列化。我没有看到任何杂志的详细信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-12
    • 1970-01-01
    • 2018-11-07
    • 2020-11-15
    • 2019-10-01
    • 2019-11-03
    • 2021-03-24
    相关资源
    最近更新 更多