这是我的两个模型:
class Skill(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name + " - ID: " + str(self.id)
class Experience(models.Model):
consultant = models.ForeignKey("Consultant", related_name="experience", on_delete=models.CASCADE)
project_name = models.CharField(max_length=100)
company = models.CharField(max_length=100)
company_description = models.TextField(null=True, blank=True)
from_date = models.DateField()
to_date = models.DateField()
project_description = models.CharField(max_length=100)
contribution = models.TextField()
summary = models.TextField()
is_pinned = models.BooleanField(default=False)
role = models.CharField(max_length=100, null=True)
skill = models.ForeignKey("Skill", related_name="experience", on_delete=models.CASCADE)
我想用DRF做一些很常见但显然不可能开箱即用的事情:我想有一个端点/经验/与POST方法,我可以发送技能ID列表(技能字段,外键)。例如:
{
"project_name": "Project AVC",
"company": "XYZ Company",
"company_description": "Description of XYZ Company",
"from_date": "2022-01-01",
"to_date": "2022-12-31",
"project_description": "Description of Project ABC",
"contribution": "Contributions to Project ABC",
"summary": "Summary of Experience",
"is_pinned": false,
"role": "Consultant",
"skills_ids": [1,2,3],
"consultant": 1
}
如果数据库中有ID为1,2,3的技能记录,则它将在经验表中创建3条记录(c的每个技能一条)。如果没有这样的id的技能,那么在验证过程中,它应该返回一个错误给用户,通知用户。
字段的名称可以是skill、skills、skill_ids...没关系。
这是我创建的ExperienceSerializer:
class ExperienceSerializer(serializers.ModelSerializer):
skills = serializers.PrimaryKeyRelatedField(
many=True,
queryset=Skill.objects.all(),
write_only=True
)
class Meta:
model = Experience
exclude = ['skill']
def create(self, validated_data):
skills_data = validated_data.pop('skills', [])
experience = Experience.objects.create(**validated_data)
for skill in skills_data:
experience.skill.add(skill)
return experience
但这给了我一个错误
django.db.utils.IntegrityError:关系“coody_portfolio_experience”的列“skill_id”中的空值违反了非空约束DETAIL:失败行包含(21,BOOM,XYZ公司,2022-01-01,2022-12-31,项目ABC的描述,对项目ABC的贡献,经验总结,1,null,f,顾问,XYZ公司的描述)。
我还尝试使用serializers.ListField,但它似乎不太适合这个序列化器。
也尝试了from this answer方法,所以我的序列化器如下所示:
class ExperienceSerializer(serializers.ModelSerializer):
skill_ids = serializers.ListField(
child=SkillSerializer(),
write_only=True
)
class Meta:
model = Experience
fields = (
'consultant',
'project_name',
'company',
'company_description',
'from_date',
'to_date',
'project_description',
'contribution',
'summary',
'is_pinned',
'role',
'skill',
'skill_ids'
)
def create(self, validated_data):
skill_ids = validated_data.pop('skill_ids')
experience = Experience.objects.create(**validated_data)
experience.set(skill_ids)
return experience
我将答案从child = serializers.IntegerField修改为child=SkillSerializer(),因为它给了我一个child未被示例化的错误。现在还注意到ListField的使用。
这是我在这个版本中的有效载荷:
{
"project_name": "BOOM",
"company": "XYZ Company",
"company_description": "Description of XYZ Company",
"from_date": "2022-01-01",
"to_date": "2022-12-31",
"project_description": "Description of Project ABC",
"contribution": "Contributions to Project ABC",
"summary": "Summary of Experience",
"is_pinned": false,
"role": "Consultant",
"skill_ids": [3, 4,2,1],
"consultant": 1
}
错误400:
{
"skill": [
"This field is required."
],
"skill_ids": {
"0": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
},
"1": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
},
"2": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
},
"3": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
}
}
}
也试过this example here,但没有用。花些时间阅读这篇解释嵌套序列化问题的整篇文章,但我不认为它与我的问题有很大关系。所有我想要的是一个列表被发送在邮政
老实说,我现在正在进入一个兔子洞,只是尝试不同的部分在一起,但我不知道如何DRF希望我做这些事情,他们的文档是可怕的,缺乏简单的例子。
如果有人可以张贴的例子,但也与解释,而不仅仅是解决方案,将不胜感激
1条答案
按热度按时间wnavrhmk1#
使用当前关系,如果你的payload包含
"skills_ids": [1,2,3],
,那么你将创建three
不同的Experience
示例,每个示例包含一个技能,这是你想要的**NOT
**,这是不好的做法。相反,
many-to-many
关系更合适,将多个技能关联到Experience
,反之亦然,从而避免数据库中的重复值。这也是您在
experience.skill.add(skill)
上使用的语法,这就是您如何使用这种关系将Skill
附加到Experience
。但是,实际上,除了让框架为您工作之外,您不需要做任何事情!models.py
serializers.py
有效载荷