如何在 DRF 中通过一个请求创建多个对象(相关)?
How to create multiple objects (related) with one request in DRF?
我有一个 class 代表一个工作,一个 class 代表一个标签,它描述了一个工作,然后我有一个 class 建立关系(连接点 table) 之间,因此一个 Job 可以由多个 Tags 描述:
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 请求将它们与相关的 JobTag 持久化?
我尝试重复这个方法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() argument after ** must be a mapping, not list"错误,所以request.datajson应该是什么样子的?
或者这种方法不能用于我的情况,我应该做一些完全不同的事情吗?
如有任何帮助,我将不胜感激。
编辑
如果我尝试访问列表:
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
我产生了另一个错误:"Cannot assign "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",
}
我认为您应该在 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
如果其他人遇到这个问题,我想出的最合适的解决方案是这样的:
我为每种情况编写了序列化程序,用于序列化发送到客户端的数据:
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"
}
我有一个 class 代表一个工作,一个 class 代表一个标签,它描述了一个工作,然后我有一个 class 建立关系(连接点 table) 之间,因此一个 Job 可以由多个 Tags 描述:
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 请求将它们与相关的 JobTag 持久化?
我尝试重复这个方法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() argument after ** must be a mapping, not list"错误,所以request.datajson应该是什么样子的?
或者这种方法不能用于我的情况,我应该做一些完全不同的事情吗?
如有任何帮助,我将不胜感激。
编辑
如果我尝试访问列表:
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
我产生了另一个错误:"Cannot assign "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",
}
我认为您应该在 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
如果其他人遇到这个问题,我想出的最合适的解决方案是这样的:
我为每种情况编写了序列化程序,用于序列化发送到客户端的数据:
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"
}