首页 > 解决方案 > 用于 CRUD 的通用 DRF 串行器架构

问题描述

假设我们有一个 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 用于列表和视图,但对我来说看起来很难看。我敢打赌,还有很多其他更好的想法,但如果没有你的帮助,我就无法实现 =) 谢谢。

标签: django-rest-framework

解决方案


这是一种完全有效的方法,特别是如果您希望客户端无需执行额外请求来检索相关数据,并且服务器无需解析有效负载以提取关系的 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
    )

推荐阅读