Поля Serializers.validated_data были заменены исходным значением в DRF
Я пытаюсь создать api, где пользователь может создавать программы и добавлять к ним правила. Правила должны выполняться в порядке приоритета. Я использую Django rest framework для достижения этой цели и пытаюсь достичь этого с помощью Serializer без использования ModelSerializer. Предложите свое решение, используя класс serializers.Serializer
В одной программе может быть много правил, а одно правило может быть во многих программах. Поэтому я использую отношения многие_к_многим, а также хочу, чтобы пользователь мог изменять порядок правил в программе, для этого я использую сквозную таблицу Priority, в которой я отслеживаю отношения между правилом и программой с помощью поля priority
models.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)
serializers.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)
Когда я делаю запрос get к 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": []
},
]
Я получил список правил в поле rules, но когда я делаю post запрос с этим 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
А также, я получаю список OrderedDict в priority_set, вместо этого мне нужен словарь объектов правил
Это то, что я хочу получить после процесса сериализации,
{
"name": "DAI=AS",
"description": "Date and Invoice equal Amount Sumif",
"rules": [
{
"rule_id": 1
},
{
"rule_id": 2
}
]
}
Заранее спасибо
Решение 1:
Поля в проверенных данных переименовываются с помощью атрибута source. Вы можете использовать переименованный атрибут в методе 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:
Вы можете сделать rules
с именем источника priority_set
как 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})