【问题标题】:Many to Many field POST requests on API Django Rest FrameworkAPI Django Rest Framework 上的多对多字段 POST 请求
【发布时间】:2021-07-09 17:28:18
【问题描述】:

所以我有 3 个感兴趣的模型:

models.py
class Author(models.Model): #Author of books
name = models.CharField(max_length = 100)

    @property    # This is code to get the books linked to an author 
    def books(self):
        book = Book.objects.filter(authors = self.id)
        return ', '.join(map(str,book)) #This makes the book list in this format "A, B, C"

    def __str__(self):
        return self.name.title()


class Genre(models.Model): #Genre of books
name = models.CharField(max_length = 50, unique = True)

    @property
    def books(self):
        book = Book.objects.filter(genre = self.id)
        return ', '.join(map(str,book))

    def __str__(self):
        return self.name.title()

class Book(models.Model):
    name = models.CharField(max_length = 150,)   #Name of books
    authors = models.ManyToManyField(Author,)     #Many to many because multiple books can have multiple authors
     genre = models.ManyToManyField(Genre)

    def __str__(self):
        return self.name.title()

这些是模型,流派和作者都与书籍有多对多的关系

serializers.py
class AuthorListSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Author
        fields = ('name',)

class GenreListSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Genre
        fields = ('name',)

class BookListSerializer(serializers.ModelSerializer):
    authors = AuthorListSerializer(many = True,) #To represent the relationship as a string instead of id
    genre = serializers.SlugRelatedField(many = True, queryset = models.Genre.objects.all(),slug_field = 'name')

    class Meta:
        model = models.Book
        fields = ('name','authors','rating', 'genre')

class BookDetailSerializer(serializers.ModelSerializer):
    publisher = serializers.SlugRelatedField(queryset = models.Publisher.objects.all(), slug_field= 'name') #To display publisher name instead of id

    authors = serializers.StringRelatedField(many = True,) #To represent the relationship as a string instead of id
    genre = serializers.StringRelatedField(many = True,)

    class Meta:
        model = models.Book
        fields = ('name', 'authors', 'rating','genre',
                  'publisher', 'total_qty', 'avail_qty',
                  'pub_date','isbn','price',)

所以在我的 Django-Rest-Framework Browsable api 中,Normal ForeignKey 字段显示了它们的选项以创建一个新的对象实例。 例如,“BookDetailSerializer”中的发布者将所有发布者显示为 POST 或 PUT 或 PATCH 请求的选项。 但是对于包括流派和作者在内的多对多字段,它不仅是空白的,而且我似乎无法输入任何内容。 我尝试使用 DRF-writable-nested 第三方包,但没有任何改变:

    class BookListSerializer(WritableNestedModelSerializer):
        authors = AuthorListSerializer(many = True,) #To represent the relationship as a string instead of id
        genre = serializers.SlugRelatedField(many = True,
                                             queryset = models.Genre.objects.all(),slug_field = 'name')

我的问题是如何通过 DRF 可浏览 api 使用我的作者和流派列表发出 POST 请求? 提前致谢!!

This image shows the options available for making a POST request involving publishers

This image shows that you can't add any input for POST request involving both Genre and Authors as many-to many relations.

更新:所以我加了一个create方法还是不行,如下图:

class BookListSerializer(serializers.ModelSerializer):
authors = AuthorListSerializer(many = True,) #To represent the relationship as a string instead of id
genre = serializers.SlugRelatedField(many = True,
                                             queryset = models.Genre.objects.all(),slug_field = 'name')


class Meta:
    model = models.Book
    fields = ('name','authors','rating', 'genre')


def create(self,validated_data):
    authors_data = models.Author.objects.all()
    book = Book.objects.create(authors = authors_data,**validated_data,)
    return book

我能做什么?

【问题讨论】:

    标签: django django-models django-rest-framework django-serializer


    【解决方案1】:

    经过4天的努力,我找到了答案 在处理多对多关系时,您希望代码如下所示。

     class BookListSerializer(serializers.ModelSerializer):
    
    class Meta:
        model = models.Book
        fields = ('name','authors','rating', 'genre')
        depth = 1
    

    添加深度允许嵌套关系完全序列化。 因为关系是嵌套的,并且是多对多关系,所以您必须在 views.py 中创建自己的创建函数,如下所示:

     class BookViewSet(GetSerializerClassMixin, viewsets.ModelViewSet):
    
    queryset = models.Book.objects.all()
    serializer_class = serializers.BookDetailSerializer
    serializer_action_classes = {'list': serializers.BookListSerializer}
    permission_classes = [IsAdminOrReadOnly, permissions.IsAuthenticated,] 
    
    def create(self,request, *args, **kwargs):
        data = request.data
    
        new_book = models.Book.objects.create(name = data["name"], publisher = models.Publisher.objects.get(id = data["publisher"]),
                                            pub_date = data["pub_date"],
                                            price = data["price"],
                                            isbn = data['isbn'],)
        new_book.save()
        
        for author in data['authors']:
            author_obj = models.Author.objects.get(name = author['name'])
            new_book.authors.add(author_obj)
    
        for gen in data['genre']:
            gen_obj = models.Genre.objects.get(name = gen['name'])
            new_book.genre.add(gen_obj)
    
        serializer = serializers.BookListSerializer(new_book)
        
        return Response(serializer.data)
    

    对于多对多关系,您必须在保存对象后创建它们并手动将它们添加到对象中。 那里存在“Publishers”的外键关系 对于这种关系,您必须手动指向它在数据库中的位置,因此下面的代码。

     name = data["name"], publisher = models.Publisher.objects.get(id = data["publisher"]),
    

    对于问题中的 Detail 书序列化器,它与 BookListSerializer 相同

    这就是我能够在多对多关系中处理 POST 请求的方式

    【讨论】:

      猜你喜欢
      • 2019-11-20
      • 1970-01-01
      • 2021-08-01
      • 2020-11-18
      • 2014-11-14
      • 1970-01-01
      • 1970-01-01
      • 2023-01-08
      • 1970-01-01
      相关资源
      最近更新 更多