用于 m-m 关系的 Django Rest api json 输入

问题描述 投票:0回答:1

我正在尝试发布 json 文件并将模型保存到数据库中。 AttributeName 和 AttributeValue 已正确生成,但保存的属性没有“nazev_atributu_id”和“hodnota_atributu_id”。

[     
    
  {
    "AttributeName": {
      "id": 2,
      "nazev": "Barva"
    }
  },
  {
    "AttributeValue": {
      "id": 2,
      "hodnota": "modrá"
    }
  },

  {
    "Attribute": {
      "id": 1,
      "nazev_atributu_id": 2,
      "hodnota_atributu_id": 2
    }
  }
]

模型.py

from django.db import models


class AttributeValue(models.Model):
    unique_id = models.AutoField(primary_key=True)
    id = models.IntegerField(unique=True)
    hodnota = models.CharField(max_length=255, null=True, blank=True)

    def __str__(self):
        return f"AttributeValue with id: {self.id}"


class AttributeName(models.Model):
    unique_id = models.AutoField(primary_key=True)
    id = models.IntegerField()
    nazev = models.CharField(max_length=255, null=True, blank=True)
    kod = models.CharField(max_length=255, null=True, blank=True)
    zobrazit = models.BooleanField(default=False)

    def __str__(self):
        return f"AttributeName with id: {self.id}"


class Attribute(models.Model):
    unique_id = models.AutoField(primary_key=True)
    id = models.IntegerField(unique=True)
    nazev_atributu_id = models.ManyToManyField(AttributeName, through='AttributeAttributeNameMapping')
    hodnota_atributu_id = models.ManyToManyField(AttributeValue, through='AttributeAttributeValueMapping')

    def __str__(self):
        return f"Attribute with id: {self.id}"


class AttributeAttributeNameMapping(models.Model):
    attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
    attribute_name = models.ForeignKey(AttributeName, on_delete=models.CASCADE)

    class Meta:
        unique_together = ('attribute', 'attribute_name')


class AttributeAttributeValueMapping(models.Model):
    attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
    attribute_value = models.ForeignKey(AttributeValue, on_delete=models.CASCADE)

    class Meta:
        unique_together = ('attribute', 'attribute_value')

序列化器

from rest_framework import serializers
from .models import *

class AttributeNameSerializer(serializers.ModelSerializer):
    class Meta:
        model = AttributeName
        fields = '__all__'


class AttributeValueSerializer(serializers.ModelSerializer):
    class Meta:
        model = AttributeValue
        fields = '__all__'


class AttributeSerializer(serializers.ModelSerializer):
    nazev_atributu_id = serializers.SlugRelatedField(queryset=AttributeName.objects.all(),slug_field='id')
    hodnota_atributu_id = serializers.SlugRelatedField(queryset=AttributeValue.objects.all(),slug_field='id')

    class Meta:
        model = Attribute
        fields = ["unique_id", "id", "nazev_atributu_id", "hodnota_atributu_id"]

    def create(self, validated_data):
        # Extract many-to-many fields from the validated data
        print("**************************")
        print(validated_data)
        print("**************************")
        nazev_atributu_id = validated_data.pop('nazev_atributu_id')
        hodnota_atributu_id = validated_data.pop('hodnota_atributu_id')
        # Create the Attribute instance
        attribute = Attribute.objects.create(**validated_data)
        print("???????????????????")
        print(nazev_atributu_id.__dict__)
        print(hodnota_atributu_id.__dict__)
        print("?????????????")
        print(attribute, type(attribute))
        print(attribute.__dict__)
        print("------------------")
        print(attribute.nazev_atributu_id)
        print(attribute.hodnota_atributu_id)
        print("------------------")
        # Set many-to-many relationships
        attribute.nazev_atributu_id.add(nazev_atributu_id)
        attribute.hodnota_atributu_id.add(hodnota_atributu_id)
        print("////////////////////")
        print(attribute.__dict__)
        print("////////////////////")
        return attribute

但还是不明白为什么属性前后相同

    attribute.nazev_atributu_id.add(nazev_atributu_id)
    attribute.hodnota_atributu_id.add(hodnota_atributu_id)

这是调用post请求后的打印输出:

**************************
{'id': 1, 'nazev_atributu_id': <AttributeName: AttributeName with id: 2>, 'hodnota_atributu_id': <AttributeValue: AttributeValue with id: 2>}
**************************
???????????????????
{'_state': <django.db.models.base.ModelState object at 0x000001E7E56E99D0>, 'unique_id': 206, 'id': 2, 'nazev': 'Barva', 'kod': None, 'zobrazit': False}
{'_state': <django.db.models.base.ModelState object at 0x000001E7E56E9070>, 'unique_id': 214, 'id': 2, 'hodnota': 'modrá'}
?????????????
Attribute with id: 1 <class 'api.models.Attribute'>
{'_state': <django.db.models.base.ModelState object at 0x000001E7E56E9310>, 'unique_id': 78, 'id': 1}
------------------
api.AttributeName.None
api.AttributeValue.None
------------------
////////////////////
{'_state': <django.db.models.base.ModelState object at 0x000001E7E56E9310>, 'unique_id': 78, 'id': 1}
////////////////////

观点

class LoadJson(viewsets.ModelViewSet):
    def create(self, request):
        post_successful = True
        over_all_result_ok = []
        over_all_result_nok = []
        sorted_json = sort_json_data(request.data, get_model_ordering())
        for item in sorted_json:
            model_type = list(item.keys())[0]
            model_values = list(item.values())[0]
            result = serialize_model(model_type, model_values)
            if result[0] is False:
                post_successful = False
                add_result_into_dict(over_all_result_nok, model_type, result)
            else:
                add_result_into_dict(over_all_result_ok, model_type, result)
        if post_successful:
            return Response({"status": over_all_result_ok}, status=status.HTTP_201_CREATED)
        return Response({"status": over_all_result_nok}, status=status.HTTP_400_BAD_REQUEST)

def get_model_ordering():
    # model_ordering = [
    #     'AttributeName', 'AttributeValue', 'Attribute',
    #     'Product', 'ProductAttributes', 'Image',
    #     'ProductImage', 'Catalog'
    # ]
    model_ordering = [
        'AttributeName', 'AttributeValue', 'Attribute',
    ]
    return model_ordering


def sort_json_data(json_data, model_ordering):
    sorted_list = []
    for process in model_ordering:
        for item in json_data:
            if list(item.keys())[0] == process:
                sorted_list.append(item)
    return sorted_list


def add_result_into_dict(over_all_result, model_type, result):
    over_all_result.append(
        {
            "model_type": model_type,
            "result": result[0],
            "message": result[1]
        }
    )
    return over_all_result


def serialize_model(model_type, model_values):
    serializer_class = SerializersLink().get_serializer_class(model_type)
    if serializer_class:
        serializer = serializer_class(data=model_values)
        if serializer.is_valid():
            serializer.save()
            return True, f"Json data '{model_values}' for model '{model_type}' are correct according serializer"
        else:

            return False, f"Invalid json data '{model_values}' input in model type: '{model_type}'"
    else:
        return False, f"Not implemented model type: '{model_type}'"

class LoadJson(viewsets.ModelViewSet):
    def create(self, request):
        post_successful = True
        over_all_result_ok = []
        over_all_result_nok = []
        sorted_json = sort_json_data(request.data, get_model_ordering())
        for item in sorted_json:
            model_type = list(item.keys())[0]
            model_values = list(item.values())[0]
            result = serialize_model(model_type, model_values)
            if result[0] is False:
                post_successful = False
                add_result_into_dict(over_all_result_nok, model_type, result)
            else:
                add_result_into_dict(over_all_result_ok, model_type, result)
        if post_successful:
            return Response({"status": over_all_result_ok}, status=status.HTTP_201_CREATED)
        return Response({"status": over_all_result_nok}, status=status.HTTP_400_BAD_REQUEST)

class SerializersLink:
    def __init__(self):
        # self._map_dict = {
        #     "AttributeName": AttributeNameSerializer,
        #     "AttributeValue": AttributeValueSerializer,
        #     "Attribute": AttributeSerializer,
        #     "Product": ProductSerializer,
        #     "ProductAttributes": ProductAttributesSerializer,
        #     "Image": ImageSerializer,
        #     "ProductImage": ProductImageSerializer,
        #     "Catalog": CatalogSerializer,
        # }
        self._map_dict = {
            "AttributeName": AttributeNameSerializer,
            "AttributeValue": AttributeValueSerializer,
            "Attribute": AttributeSerializer,
        }

    def get_serializer_class(self, model_name):
        if model_name in self._map_dict:
            return self._map_dict[model_name]
        return False
python django django-models django-rest-framework django-serializer
1个回答
0
投票

使用自定义 BaseManyToManySerializer:

class BaseManyToManySerializer(serializers.ModelSerializer):

    def to_internal_value(self, data):
        data = dict(data)
        for field_name, model_class in self.get_id_conversion_mappings():
            self._convert_ids_to_unique_ids(data, field_name, model_class)
        return super().to_internal_value(data)

    def _convert_ids_to_unique_ids(self, data, field_name, model_class):
        if field_name in data:
            ids = data.get(field_name)
            if not isinstance(ids, list):
                ids = [ids]
            objects = model_class.objects.filter(id__in=ids)
            id_map = {}
            for obj in objects:
                if obj.id not in id_map:
                    id_map[obj.id] = []
                id_map[obj.id].append(obj.unique_id)
            data[field_name] = [unique_id for id in ids for unique_id in id_map.get(id, [])]

    def create(self, validated_data):
        many_to_many_data = {field_name: validated_data.pop(field_name, []) for field_name, field_class in
                             self.get_many_to_many_fields()}
        instance = self.Meta.model.objects.create(**validated_data)
        for field_name, field_class in self.get_many_to_many_fields():
            if field_name in many_to_many_data:
                field_value = many_to_many_data[field_name]
                if hasattr(instance, field_name):
                    getattr(instance, field_name).set(field_value)
        return instance

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        for field_name, field_class in self.get_many_to_many_fields():
            if hasattr(instance, field_name):
                representation[field_name] = [item.id for item in getattr(instance, field_name).all()]
        return representation

    def get_id_conversion_mappings(self):
        raise NotImplementedError("Subclasses must implement this method.")

    def get_many_to_many_fields(self):
        raise NotImplementedError("Subclasses must implement this method.")

具体用法:

class AttributeSerializer(BaseManyToManySerializer):
    nazev_atributu_id = serializers.PrimaryKeyRelatedField(queryset=AttributeName.objects.all(), many=True,
                                                           required=False)
    hodnota_atributu_id = serializers.PrimaryKeyRelatedField(queryset=AttributeValue.objects.all(), many=True,
                                                             required=False)

    class Meta:
        model = Attribute
        fields = ["unique_id", "id", "nazev_atributu_id", "hodnota_atributu_id"]

    def get_id_conversion_mappings(self):
        return [
            ('nazev_atributu_id', AttributeName),
            ('hodnota_atributu_id', AttributeValue)
        ]

    def get_many_to_many_fields(self):
        return [
            ('nazev_atributu_id', AttributeName),
            ('hodnota_atributu_id', AttributeValue)
        ]
© www.soinside.com 2019 - 2024. All rights reserved.