python - Serializers.validated_data 字段已更改为 DRF 中的源值

标签 python django django-rest-framework

我正在尝试创建一个 api,用户可以在其中创建程序并向其添加规则。规则必须按优先顺序执行。我正在使用 Django rest 框架来实现这一点,并且我试图在不使用 ModelSerializer 的情况下使用 Serializer 来实现这一点。使用序列化器提供您的解决方案。序列化器类

一个程序可以有很多规则,一个规则可以在多个程序中。所以,我正在使用 many_to_many 关系,我还希望用户更改程序中规则的顺序,为了实现这一点,我使用了一个名为 Priority 的直通表,我在其中使用优先级字段跟踪规则和程序之间的关系

模型.py

class Program(models.Model):
    name = models.CharField(max_length=32)
    description = models.TextField(blank=True)

    rules = models.ManyToManyField(Rule, through='Priority')

class Rule(models.Model):
    name = models.CharField(max_length=20)
    description = models.TextField(blank=True)
    rule = models.CharField(max_length=256)

class Priority(models.Model):
    program = models.ForeignKey(Program, on_delete=models.CASCADE)
    rule = models.ForeignKey(Rule, on_delete=models.CASCADE)

    priority = models.PositiveIntegerField()

    def save(self, *args, **kwargs):
        super(Priority, self).save(*args, **kwargs)

序列化器.py

class RuleSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=20)
    description = serializers.CharField(allow_blank=True)
    rule = serializers.CharField(max_length=256)

    def create(self, validated_data):
        return Rule.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.rule = validated_data.get('rule', instance.rule)
        instance.save()
        return instance

class PrioritySerializer(serializers.Serializer):
    rule_id = serializers.IntegerField(source='rule.id')
    rule_name = serializers.CharField(source='rule.name')
    rule_rule = serializers.CharField(source='rule.rule')
    priority = serializers.IntegerField()

class ProgramSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)
    description = serializers.CharField(style={'base_template': 'textarea.html'})
    rules = PrioritySerializer(source='priority_set', many=True)

    def create(self, validated_data):
        rules_data = validated_data.pop('rules')
        program_obj = Program.objects.create(**validated_data)

        priority = 0
        for rule in rules_data:
            rule_obj = Rule.objects.get(pk=rule.rule_id)
            priority += 1
            Priority.objects.create(program=program_obj, rule=rule_obj, priority=priority)

        return program_obj

views.py

class ProgramList(APIView):
    """
    List all programs, or create a new program.
    """
    def get(self, request, format=None):
        programs = Program.objects.all()
        serializers = ProgramSerializer(programs, many=True)
        return Response(serializers.data)

    def post(self, request, format=None):
        serializer = ProgramSerializer(data=request.data, partial=True)
        print(serializer.initial_data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

当我向 http://localhost:8000/api/programs/ 发出获取请求时,我得到了这个响应

[
    {
        "id": 1,
        "name": "some",
        "description": "hahah",
        "rules": [
            {
                "rule_id": 3,
                "rule_name": "DAIEA",
                "rule_rule": "date,and,invoice,equal,amount",
                "priority": 1
            },
            {
                "rule_id": 2,
                "rule_name": "DAIEA",
                "rule_rule": "date,and,invoice,equal,amount",
                "priority": 2
            },
            {
                "rule_id": 1,
                "rule_name": "DAI=A",
                "rule_rule": "date,and,invoice,equal,amount",
                "priority": 3
            }
        ]
    },
    {
        "id": 2,
        "name": "DAI=AS",
        "description": "Date and Invoice equal Amount Sumif",
        "rules": []
    },
]

我在规则字段下获得了规则列表,但是当我使用此 request.data 发出发布请求以在 rule.id 的帮助下创建一个包含规则的新程序时,

发布数据:

{
    "name": "DAI=AS",
    "description": "Date and Invoice equal Amount Sumif",
    "rules": [
        {
            "rule_id": 1
        },
        {
            "rule_id": 2
        }
    ]
}

在序列化过程之后,validated_data 包含 priority_set 字段而不是 rules 字段,如下所示

{
    'name': 'DAI=AS', 
    'description': 'Date and Invoice equal Amount Sumif', 
    'priority_set': [
        OrderedDict([('rule', {'id': 1})]), 
        OrderedDict([('rule', {'id': 2})])
     ]
}

我不希望序列化程序将规则更改为 priority_set

此外,我在 priority_set 中获取 OrderedDict 列表,而不是我需要规则对象字典

这就是我在序列化过程之后想要的,

{
    "name": "DAI=AS",
    "description": "Date and Invoice equal Amount Sumif",
    "rules": [
        {
            "rule_id": 1
        },
        {
            "rule_id": 2
        }
    ]
}

提前致谢

最佳答案

解决方案一:

验证数据中的字段使用源属性重命名。您可以在序列化程序的 create 方法中使用重命名的属性。

对于嵌套的序列化程序,验证数据似乎包含 OrderedDict。你可以把它转换成普通的Dict,并且可以得到规则id。

class ProgramSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)
    description = serializers.CharField(style={'base_template': 'textarea.html'})
    rules = PrioritySerializer(source='priority_set', many=True)

    def create(self, validated_data):
        rules_data = validated_data.pop('priority_set')
        program_obj = Program.objects.create(**validated_data)

        priority = 0
        for rule in rules_data:
            rule_obj = Rule.objects.get(pk=dict(rule)["rule"]["id"])
            priority += 1
            Priority.objects.create(program=program_obj, rule=rule_obj, priority=priority)

        return program_obj

解决方案 2:

您可以将源名称为 priority_setrules 设置为 read_only 并可以从请求数据中提取它。

class ProgramSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)
    description = serializers.CharField(style={'base_template': 'textarea.html'})
    rules = PrioritySerializer(source='priority_set', many=True, read_only=True)

    def create(self, validated_data):
        rules_data = self.context["request"].data["rules"]
        program_obj = Program.objects.create(**validated_data)

        priority = 0
        for rule in rules_data:
            rule_obj = Rule.objects.get(pk=rule["rule_id"])
            priority += 1
            Priority.objects.create(program=program_obj, rule=rule_obj, priority=priority)

        return program_obj

您需要将请求上下文传递给序列化器

serializer = ProgramSerializer(data=request.data, partial=True, context={"request": request})

解决方案 3:

Priority模型的program外键中添加related_name

program = models.ForeignKey(Program, related_name="rule", on_delete=models.CASCADE)

ProgramSerializer 中的 rules 字段重命名为其他名称,例如规则,并删除源。

class ProgramSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)
    description = serializers.CharField(style={'base_template': 'textarea.html'})
    rule = PrioritySerializer(many=True)

    def create(self, validated_data):
        rules_data = validated_data.pop('rule')
        program_obj = Program.objects.create(**validated_data)

        priority = 0
        for rule in rules_data:
            rule_obj = Rule.objects.get(pk=dict(rule)["rule"]["id"])
            priority += 1
            Priority.objects.create(program=program_obj, rule=rule_obj, priority=priority)

        return program_obj

现在您将能够使用 rule 键发布和检索数据。您可以根据需要重命名它。

关于python - Serializers.validated_data 字段已更改为 DRF 中的源值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69297409/

相关文章:

python - django rest 框架的多个 lookup_fields

python - 有没有更好的方法利用 redshift、python 和 powershell 来自动化我的报告?

python - Django Tastypie v0.11.1,POST请求,无法使用obj_create保存数据

Django Rest Framework 2.4 类型错误 : __init__() got an unexpected keyword argument 'allow_none'

django - 如何在 Django Rest Framework 的 get 方法中序列化查询集?

python - 使用 python Social auth 和 Satellizer 进行 Rest 身份验证

python - 将 Keras 模型导出为 TF 估计器 : trained model cannot be found

python - "Unsupported hash type"错误 w/Python 2.6.5 和 django 框架

javascript - 如何结合 Tornado 身份验证和 AngularJS?

python - Django 忘记密码功能更改默认电子邮件