在Django Rest Framework

时间:2016-08-17 16:07:44

标签: django django-rest-framework

在我的模型中,我有以下类:

class Topic(models.Model):
    name = models.CharField(max_length=25, unique=True)

class Content(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    topic = models.ForeignKey(Topic, blank=True, null=True)

我的序列化程序是这样的:

class TopicSerializer(serializers.ModelSerializer):
     class Meta:
         model = Topic
         fields = ('name')

class ContentSerializer(serializers.ModelSerializer):
     topic = TopicSerializer(read_only=True)
     class Meta:
          model = Content
          fields = ('title', 'body', 'topic')

好的,所以在我的网址文件中,我有以下模式:

urlpatterns = [
    ...
    url(r'^api/topic_detail/(?P<name>[a-zA-Z0-9-]+)/content_list/$', views.topic_content_list, name='topic_content_list'),
    ...
]

因此,当用户说出/api/topic_detail/sports/content_list/时,我们会获得包含体育主题的所有内容的列表。现在我想要的是如果我们 POST 以下数据到上面的URL,那么创建一个Content对象,主题字段自动与sports相关。

我正在尝试按照以下方式在视图中执行此操作:

@api_view(['GET', 'POST'])
def topic_content_list(request, name):
    try:
        topic = Topic.objects.get(name=name)
    except:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        contents = Content.objects.filter(topic=topic)
        serializer = ContentSerializer(contents, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        request.data["topic"] = topic
        serializer = ContentSerializer(data=request.data)
        print request.data
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

现在让我说我去了网址/api/topic_detail/sports/content_list/并发布了这个:

{
    "title": "My blog post",
    "body" : ".....",
}

创建内容对象并正确设置标题和正文字段。但是,topic字段设置为null。我怎样才能解决这个问题?任何帮助表示赞赏。

此外,请不要建议使用通用视图集,因为我对自动发生的很多事情感到不舒服。

修改

好吧,所以我解决了我的愚蠢错误:

class ContentSerializer(serializers.ModelSerializer):
     topic = TopicSerializer(read_only=False)
     class Meta:
          model = Content
          fields = ('title', 'body', 'topic')

也就是说,我将read_only参数设置为False。但是,现在该帖子会创建一个新错误:

{
    "topic": {
        "non_field_errors": [
            "Invalid data. Expected a dictionary, but got Topic."
        ]
    }
}

我很确定这是指我发送的data.website不是JSON,而只是一个Django模型实例。我如何JSONify单个实例?

2 个答案:

答案 0 :(得分:7)

这是来自您的序列化程序。

     topic = TopicSerializer(read_only=True)

这意味着您的主题是只读的,因此当您尝试保存序列化程序时,主题不会被保存。删除它,问题将得到解决。

编辑:

现在按照第二个错误,这是因为它正在期待一个dict而你正在传递模型实例,所以你有两个选择。要么手工制作字典。

topic_dict = { "name": topic.name }

并将其作为'topic'传递给request.data,然后保存或给出topic_id,因为存在外键关系,它应该有效。

所以它会是这样的:

request.data["topic_id"] = topic.id

现在你选择做的事完全取决于你。

答案 1 :(得分:1)

恢复这个旧线程,因为它似乎是人们遇到的一个常见问题。我刚刚在 Django 3.1.6 上完成了这项工作。

由于 TopicContent 模型已经链接,序列化器很简单

class ContentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Content
        fields = ('title', 'body', 'topic')

不需要 topic = TopicSerializer(read_only=False),这将要求您使用 POST 创建一个新主题。现在,POST 的正文可以是

{
    "title": "My blog post",
    "body" : ".....",
    "topic": 3
}

如果你想让你的 GET 输出看起来像

{
    "title": "My blog post",
    "body" : ".....",
    "topic": {
        "id": 3
        "name": "announcements"
    }
}

覆盖to_representation

class ContentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Content
        fields = ('title', 'body', 'topic')

    def to_representation(self, instance):
        response = super().to_representation(instance)
        response['topic'] = TopicSerializer(instance.topic).data
        return response

正确使用 to_representation 的功劳归功于 @PdotNJ 的 answer