Django自定义Inline FormSet的clean校验逻辑单元测试问题
Django自定义Inline FormSet单元测试解决方案
报错原因
你遇到的Select a valid choice. That choice is not one of the available choices.错误是两个传值问题导致的:
- 关联字段(外键、选择字段)直接传入了Python模型实例,Django表单校验时要求传入的是字符串格式的主键值
- 布尔字段直接传入了Python布尔类型对象,表单提交数据是字符串类型,需要传入
'True'/'False'格式的值
正确的测试写法
1. 手动构造符合要求的提交数据
把所有非字符串类型的字段值做序列化转换:
data = { 'client_agreements-TOTAL_FORMS': '2', 'client_agreements-INITIAL_FORMS': '2', # 主键字段传字符串格式的主键值 'client_agreements-1-id': str(self.second_client_agreement.pk), # 外键字段同样传字符串格式的关联对象主键 'client_agreements-1-agreement': str(self.second_client_agreement.agreement.pk), # 布尔字段传字符串格式的布尔值 'client_agreements-1-is_default': 'False', 'client_agreements-0-id': str(self.online_client_agreement.pk), 'client_agreements-0-agreement': str(self.first_client_agreement.agreement.pk), 'client_agreements-0-is_default': 'True', }
2. 自动生成前缀避免拼写错误
手动拼字段前缀容易出错,可以用formset自带的add_prefix方法自动生成正确的字段名:
# 先定义不带前缀的表单数据 form_datas = [ { "id": self.online_client_agreement.pk, "agreement": self.first_client_agreement.agreement.pk, "is_default": True }, { "id": self.second_client_agreement.pk, "agreement": self.second_client_agreement.agreement.pk, "is_default": False } ] # 实例化空formset拿到前缀规则 base_formset = formset(instance=self.second_client) data = {} # 自动生成管理表单字段 data.update(base_formset.management_form.initial) # 自动生成带前缀的表单字段 for idx, item in enumerate(form_datas): for k, v in item.items(): data[base_formset.add_prefix(f"{idx}-{k}")] = str(v) if v is not None else ""
3. 校验clean逻辑
不需要执行save操作,调用is_valid()就会触发clean方法,直接校验结果即可:
client_formset = formset(data=data, instance=self.second_client) # 执行校验(会触发自定义clean逻辑) is_valid = client_formset.is_valid() # 调试用:打印错误信息排查问题 # print(client_formset.errors) # print(client_formset.non_form_errors()) # 断言你的自定义校验逻辑是否生效 # 示例:校验是否触发了“只能有一个默认协议”的错误 assert "只能存在一个默认协议" in client_formset.non_form_errors()
内容的提问来源于stack exchange,提问作者Alexander Skopintsev
相关产品推荐
相关产品推荐

