Django REST Framework中用ModelSerializer创建关联多字段失败求助
我帮你分析下问题所在,以及对应的解决办法:
首先,你遇到的'contactphonenumber_set' is an invalid keyword argument for this function错误,原因很明确:contactphonenumber_set是Django为反向关联自动生成的管理器名称,并不是Contact模型的实际字段,所以你直接把它传给Contact.objects.create(**validated_data)时,Django肯定不认识这个参数。
查询时能显示关联的手机号,是因为DRF的嵌套序列化器可以通过source='contactphonenumber_set'来读取反向关联的数据,但创建操作默认不会自动处理这种嵌套的关联数据,必须手动在序列化器的create方法里处理。
解决步骤
1. 修改ContactSerializer的create方法
你需要从validated_data中先取出电话号码的数据,创建完Contact实例后,再单独创建关联的ContactPhoneNumber记录。修改后的代码如下:
class ContactSerializer(serializers.HyperlinkedModelSerializer): phone_numbers = ContactPhoneNumberSerializer(source='contactphonenumber_set', many=True) url = serializers.HyperlinkedRelatedField( view_name='contacts:detail', read_only=True ) class Meta: model = Contact fields = ('url', 'id', 'first_name', 'last_name', 'date_of_birth', 'avatar', 'phone_numbers') def create(self, validated_data): # 先取出嵌套的电话号码数据,从validated_data中移除 phone_numbers_data = validated_data.pop('contactphonenumber_set', []) # 创建联系人主记录 contact = Contact.objects.create(**validated_data) # 逐个创建关联的电话号码 for phone_data in phone_numbers_data: ContactPhoneNumber.objects.create(contact=contact, **phone_data) return contact
2. 优化:批量创建提升性能
如果一次提交多个电话号码,用bulk_create会比循环创建更高效,修改后的create方法可以改成这样:
def create(self, validated_data): phone_numbers_data = validated_data.pop('contactphonenumber_set', []) contact = Contact.objects.create(**validated_data) # 构建所有要创建的电话号码对象列表 phone_number_instances = [ ContactPhoneNumber(contact=contact, **data) for data in phone_numbers_data ] # 批量创建 ContactPhoneNumber.objects.bulk_create(phone_number_instances) return contact
3. 检查模型字段匹配
另外注意到你的ContactPhoneNumberSerializer里包含了primary、created、modified字段,但你的模型里并没有定义这些字段,这会导致序列化/反序列化报错。你需要给ContactPhoneNumber模型添加这些字段:
class ContactPhoneNumber(models.Model): contact = models.ForeignKey(Contact, on_delete=models.CASCADE) phone = models.CharField(max_length=100) # 添加缺失的字段 primary = models.BooleanField(default=False) created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) class Meta: db_table = 'contact_phone_numbers'
4. (可选)支持更新操作
如果之后需要支持更新联系人同时更新电话号码,你还需要重写update方法,逻辑类似:先更新主联系人信息,再处理电话号码的增删改。
测试验证
现在你发送包含电话号码的POST请求时,比如这样的JSON数据:
{ "first_name": "Anshuman", "last_name": "Upadhyay", "date_of_birth": "2018-05-15", "contactphonenumber_set": [ {"phone": "1234567890", "primary": true}, {"phone": "0987654321"} ] }
就能同时创建联系人以及关联的电话号码记录了。
内容的提问来源于stack exchange,提问作者Anuj TBE

