【问题标题】:How to create multiple objects (related) with one request in DRF?如何在 DRF 中使用一个请求创建多个对象(相关)?
【发布时间】:2015-04-09 20:14:58
【问题描述】:

我有一个类代表一个 Job,一个类代表一个 Tag,它描述了一个 Job,然后我有一个类在它们之间建立关系(联结表),所以一个 Job 可以由多个标签来描述:

class JobTag(models.Model):
    job = models.ForeignKey(Job, unique=False, related_name='jobtags')
    tag = models.ForeignKey(Tag, unique=False, related_name='Tag_For_Job')

    created_time = models.DateTimeField(auto_now_add = True)
    modified_time = models.DateTimeField(auto_now = True)

    class Meta:
        unique_together = ('job', 'tag',)

    def __unicode__(self):
        return 'Tag '+self.tag.name +' for job '+ self.job.name

然后我有序列化器:

class TagSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Tag
        fields = ('url','name','badge_pic')
        read_only_fields = ('name','badge_pic')

class JobTagSerializer(serializers.HyperlinkedModelSerializer):
    tag = TagSerializer()
    class Meta:
        model = JobTag
        fields = ('tag',)
        depth=1

class JobSerializer(serializers.HyperlinkedModelSerializer):
    jobtags=JobTagSerializer(many=True)
    class Meta:
        model = Job
        fields = ('url','name', 'employer','jobtags','description')
        read_only_fields = ('employer',)

所以 GET 请求的 http 响应是:

{
        "url": "http://127.0.0.1:8000/api/jobs/2/",
        "name": "Odprac mi sneh",
        "employer": "http://127.0.0.1:8000/api/users/4/",
        "jobtags": [
            {
                "tag": {
                    "url": "http://127.0.0.1:8000/api/tags/2/",
                    "name": "Odhadzovanie snehu",
                    "badge_pic": "http://127.0.0.1:8000/media/pictures/tags/0005.jpg"
                }
            }
        ],
        "description": "blablabla"
    }

我的问题非常明显,我如何创建一个作业实例并通过一个 POST http 请求将它们与相关的 JobTags 一起保存?

我尝试重复此方法http://www.django-rest-framework.org/api-guide/serializers/#writable-nested-representations

class JobSerializer(serializers.HyperlinkedModelSerializer):
        jobtags=JobTagSerializer(many=True)
        class Meta:
            model = Job
            fields = ('url','name', 'employer','jobtags','description')
            read_only_fields = ('employer',)
        def create(self, validated_data):
            jobtag_data = validated_data.pop('jobtags')
            job = Job.objects.create(**validated_data)
            JobTag.objects.create(job=job, **jobtag_data)
            return job

但它返回“** 后的 create() 参数必须是映射,而不是列表”错误,那么 request.data json 应该是什么样子?

或者这种方法不能用于我的情况,我应该做一些完全不同的事情吗?

如果有任何帮助,我将不胜感激。

编辑

如果我尝试访问列表:

def create(self, validated_data):
        jobtag_data = validated_data.pop('jobtags')
        job = Job.objects.create(**validated_data)
        JobTag.objects.create(job=job, **jobtag_data[0])
        return job

我生成另一个错误:“无法分配“OrderedDict()”:“JobTag.tag”必须是“Tag”实例。” 所以我猜我发布的 json 格式错误?我尝试以这种方式发布数据:

{
        "name": "Odprac mi sneh",
        "jobtags": [
            {
                "tag": {
                    "url": "http://127.0.0.1:8000/api/tags/2/"
                }
            }
        ],
        "description": "veela sneu nemam ruky makam makam makamam",           
    }

【问题讨论】:

  • 你不能遍历jobtag_data 并为每个项目创建一个JobTag 实例吗?
  • 看起来validated_data中的jobtags用一些OrdedDict表示,这是不可接受的,我对问题进行了编辑

标签: python django django-rest-framework


【解决方案1】:

如果其他人面临这个问题,我想出的最合适的解决方案是:在创建对象时使用作业标签的超链接序列化并将嵌套序列化用作输出:

我为每种情况编写了序列化程序,用于序列化发送到客户端的数据:

class JobTagNestedSerializer(serializers.HyperlinkedModelSerializer):
    tag = TagSerializer()
    class Meta:
        model = JobTag
        fields = ('tag',)
        depth=1



class JobNestedSerializer(serializers.HyperlinkedModelSerializer):
    jobtags=JobTagNestedSerializer(many=True,read_only=True)
    class Meta:
        model = Job
        fields = ('url','name', 'employer','jobtags','description')
        read_only_fields = ('employer',)

以及用于创建新作业,因此对于从客户端发送到 DRF 的数据:

class JobTagSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = JobTag
            fields = ('tag',)

class JobCreateSerializer(serializers.HyperlinkedModelSerializer):
        jobtags=JobTagSerializer(many=True,required=False)
        class Meta:
            model = Job
            fields = ('url','name', 'employer','jobtags','description')
            read_only_fields = ('employer',)

        def create(self, validated_data):
            tag_data = validated_data.pop('jobtags')
            job = Job.objects.create(**validated_data)
            for tag in tag_data:
                d=dict(tag)
                JobTag.objects.create(job=job, tag_id=d['tag'].pk)
            return job

所以 DRF 期望来自客户端的 POST json 看起来像:

{
        "name": "Odprac mi sneh",
        "employer": "http://127.0.0.1:8000/api/users/4/",
        "jobtags": [
            {
                "tag": "http://127.0.0.1:8000/api/tags/2/"
            },
            {
                "tag": "http://127.0.0.1:8000/api/tags/5/"
            }
        ],
        "description": "veela sneu nemam ruky makam makam makamam"
    }

【讨论】:

  • 是的,例如“127.0.0.1:8000/api/tags/2”是相关资源的 uri(超链接),因此我使用 HyperlinkedModelSerializer。如果您打算使用 modelSerializer,您可能应该使用相关资源的另一个唯一标识符,例如主键(我想)。
【解决方案2】:

我认为您应该在 POST 数据中提供每个标签的 id 而不是 url,如下所示:

{
    "name": "Odprac mi sneh",
    "tags": [
        {
            "id": 2
        },
        {
            "id": 3
        }
    ],
    "description": "veela sneu nemam ruky makam makam makamam"
}

然后,在您的 create 方法中,您应该能够遍历标签:

def create(self, validated_data):
    tag_data = validated_data.pop('tags')
    job = Job.objects.create(**validated_data)

    for tag in tag_data:
        JobTag.objects.create(job=job, tag_id=tag["id"])

    return job

【讨论】:

  • 这种方式序列化数据的验证不理解你的“标签”字段,我想如果我将我的序列化器从超链接序列化重写为模型序列化器,这可能会起作用,因此外键关系由 PrimaryKey 表示,而不是网址。但我还是宁愿保留超链接序列化。无论如何,非常感谢您的帮助,我真的很感激。
  • 没问题,很高兴您解决了问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-12
  • 1970-01-01
  • 2015-10-14
  • 2021-09-27
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
相关资源
最近更新 更多