用于CRUD的通用DRF串行器体系结构

时间:2020-07-03 08:55:45

标签: django-rest-framework

假设我们有一个Django Rest Framework端点:

class Comment(models.Model):
    article = models.ForeignKey(Article)
    author = models.ForeignKey(Author)
    content = models.TextField()


class CommentsListCreateView(
    mixins.ListModelMixin,
    mixins.CreateModelMixin,
    generics.GenericAPIView
):
    allowed_methods = ['GET', 'POST']
    serializer_class = CommentSerializer
    queryset = Comment.objects.all()

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

我希望端点像这样工作:

  1. GET仅以以下格式返回评论列表:
[{
  article: {id: 1, title: '100 things about'},
  author: {id: 1, name: 'Miguel'},
  content: 'Blah blah blah'
}]
  1. POST创建一个新注释,并以相同格式返回新注释的JSON:

POST {article: 1, author: 1, content: 'Blah blah blah again'}->

{
  article: {id: 1, title: '100 things about'},
  author: {id: 1, name: 'Miguel'},
  content: 'Blah blah blah again'
}

请注意,我将obj ID作为参数发布,但是服务器返回了obj详细信息。

这种方法可以吗?

现在我有两个序列化程序CommentListSerializer和CommentCreateSerializer用于列表和视图,但是对我来说看起来很丑。我敢打赌,还有许多其他想法会更好,但没有您的帮助我将无法实现=)谢谢。

1 个答案:

答案 0 :(得分:0)

这是一种非常有效的方法,尤其是如果您想让客户端不必执行额外的请求来检索相关数据和服务器,而不必通过不必要地解析有效负载来提取关系的ID。

如果我的理解正确,那么您正在寻找更好的解决方案?在这种情况下,我可以建议以下内容:

class ArticleSerializer(ModelSerializer):
    class Meta:
        model = Article
        fields = ["id", "title"]

class AuthorSerializer(ModelSerializer):
    class Meta:
        model = Author
        fields = ["id", "name"]

class CommentSerializer(ModelSerializer):
    class Meta:
        model = Comment
        fields = ["article", "author", "content"]

    def to_representation(self, instance):
        representation = self.to_representation(instance)
        representation.update({
            "article": ArticleSerializer(instance.article).data,
            "author": AuthorSerializer(instance.author).data
        })
        return representation

class CommentsListCreateView(ListCreateAPIView):
    serializer_class = CommentSerializer
    queryset = Comment.objects.all()

我没有运行此代码,因此可能会有一些错误,但这是一般的想法。我通常更喜欢使用HyperlinkedModelSerializerHyperlinkedRelatedField,这样客户端就不必自己重建URL,而是可以自动将它们解析为实例。这通常导致我继承HyperlinkedRelatedField的子类并覆盖其to_representation方法。这样,我可以传递要用于将输出序列化为参数的序列化器。大概的草稿如下:

class CustomHyperlinkRelatedField(HyperlinkRelatedField):
    self.serializer_class = None

    def __init__(self, view_name=None, **kwargs):
        self.serializer_class = kwargs.pop(
             "serializer_class", self.serializer_class
        )
        super().__init__(view_name=view_name, **kwargs)

    def to_representation(self, instance):
        if not self.serializer_class:
            return super().to_representation(instance)

        obj = get_object_or_404(queryset, pk=instance.pk)
        request = self.context["request"]
        return self.serializer_class(obj, context={"request": request}).data

class SomeSerializer(Serializer):
    relation = CustomHyperlinkedRelatedField(
        queryset=SomeModel.objects.all(), 
        serializer_class=SomeModelSerializer
    )