Django DRF根据model的字段动态限制另一字段的内容
在定义model时,有时有这种需求:将该model的某一字段的值限定在一定条件内,并且依据该model里的另一字段来限定。比如我现在创建测试集,需要选择多个测试脚本,但是我必须将测试脚本动态地限定在和测试集同样的项目里。
其实这个问题不是django restframework才有的,django都会有这种需求,只是这里用django restframework时解决了。
先看最初的model:
class TestSuite(models.Model):
name = models.CharField(max_length=100)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
script = models.ManyToManyField(Script)
create_time = models.DateTimeField(default=timezone.now)
update_time = models.DateTimeField(default=timezone.now)
假设project有A和B两个,分别对应着多个不同的script。那么在创建test suite时,就应该限制用户不能去选择非当前project的script。比如test suite的project字段是A,就不能让用户选择到B的script,也不能成功提交。
如果不是动态地限定的话,实现是比较简单的,可以用limit_choices_to:
# 限定script只能选择project id为2的项目里
script = models.ManyToManyField(Script, limit_choices_to={'project': 2})
如果要动态地限定,script跟随project字段来限定,那么实现会麻烦一些,当然下面这种是不可行的:
# 这是不可行的
script = models.ManyToManyField(Script, limit_choices_to={'project': project})
对于django restframework来说,可以在serializers里加上自定义的校验。加上对应的函数,比如校验script就用validate_script,当script的project值匹配上test suite的project的值,那么就通过,否则就抛出错误提示400.
class TestSuiteSerializer(serializers.ModelSerializer):
def validate_script(self, value):
script_match_project = True
project = self.context['request'].data["project"][0]
for v in value:
if v.project_id != int(project):
script_match_project = False
break
if script_match_project:
return value
else:
raise serializers.ValidationError("Cannot select scripts from another project")
class Meta:
model = TestSuite
fields = "__all__"
重新启动后便能看到效果,选择不匹配的project是无法成功保存的,只有选择了匹配的project才行。该需求得到解决。