Django Serializers - как обновить поле PrimaryKeyRelatedField вложенного отношения

У меня есть следующие модели

Модель компании:

class Company(SafeDeleteModel):

    category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True)

    code = models.CharField(max_length=10, null=True)
    name = models.CharField(max_length=100)
  
    addresses = GenericRelation('Address')

Модель адреса:

В этой модели у меня есть общее отношение от адреса к компании. Компания может иметь множество адресов.

class Address(SafeDeleteModel):
    content_type = models.ForeignKey(ContentType, related_name='model_addresses', on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField(null=True)
    content_object = GenericForeignKey('content_type', 'object_id')

    name = models.CharField(max_length=100, null=True, blank=True)

    street = models.CharField(max_length=200, null=True, blank=True)

    external_number = models.CharField(max_length=20, null=True, blank=True)
    internal_number = models.CharField(max_length=20, null=True, blank=True)

    colony = models.ForeignKey(Colony, on_delete=models.CASCADE, null=True, blank=True)

    locality = models.ForeignKey(Locality, on_delete=models.CASCADE, null=True, blank=True)

    municipality = models.ForeignKey(Municipality, on_delete=models.CASCADE, null=True, blank=True)

    state = models.ForeignKey(State, on_delete=models.CASCADE, null=True, blank=True)

    country = models.ForeignKey(Country, on_delete=models.CASCADE, null=True, blank=True)

    zip_code = models.CharField(max_length=20, null=True, blank=True)

Сериализатор адресов:

class AddressSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()
   
    country = CountrySerializer(read_only = True)
    country_id = serializers.PrimaryKeyRelatedField(queryset = Country.objects.all(), source="country")

    colony = ColonySerializer(read_only = True)
    colony_id = serializers.PrimaryKeyRelatedField(source="colony", queryset = Colony.objects.all(), required=True)

    locality = LocalitySerializer(read_only = True)
    locality_id = serializers.PrimaryKeyRelatedField(queryset = Locality.objects.all(), source="locality", allow_null=True)

    municipality = MunicipalitySerializer(read_only = True)
    municipality_id = serializers.PrimaryKeyRelatedField(queryset = Municipality.objects.all(), source="municipality", allow_null=True)

    state = StateSerializer(read_only = True)
    state_id = serializers.PrimaryKeyRelatedField(queryset = State.objects.all(), source="state", allow_null=True)

    class Meta:
        model = Address
        fields = '__all__'

В сериализаторе, country, colony, locality, municipality, state - это объекты других Моделей, и я добавил PrimaryKeyRelatedField с окончанием *_id, чтобы получить id каждого из них и использовать его для отправки из front-end, если есть какие-либо изменения.

Сериализатор компании:

class CompanySerializer(serializers.ModelSerializer):
    category = CategorySerializer(read_only=True)
    category_id = serializers.PrimaryKeyRelatedField(queryset = Category.objects.all(), source='category', allow_null=True)

    
    addresses = AddressSerializer(many=True, read_only=False)


    class Meta:
        model = Company
        fields = '__all__'

    def create(self, validated_data):
        errors = {}
        addresses = validated_data.pop('addresses')
        
        if errors:
            raise serializers.ValidationError(errors)

        obj = Company.objects.create(**validated_data)

        address_list = []
        for address in addresses:
            address_id = address['id']
            address.pop('id', None)
            address_list.append(Address.objects.create(**address))
        obj.addresses.add(*address_list)

        return obj

    def update(self, instance, validated_data):
        errors = {}
        if 'addresses' in validated_data:
            addresses = validated_data.pop('addresses')
        else:
            addresses = []
        
        instance = super().update(instance, validated_data)

        address_list = []
        for address in addresses:
            address_id = address['id']
            if address_id != 0:
                address_instance = Address.objects.get(id = address_id)
                address_instance.zip_code = address['zip_code']
                address_instance.street = address['street']
                address_instance.external_number = address['external_number']
                address_instance.internal_number = address['internal_number']
                address_instance.colony_name = address['colony_name']
                address_instance.colony_id = address['colony_id']
                address_instance.country_id = address['country_id']
                address_instance.references = address['references']
                address_instance.save()
            elif address_id == 0:
                address.pop('id', None)
                address_list.append(Address.objects.create(**address))
        instance.addresses.add(*address_list)
        return instance

JSON запрос на обновление компании и адресов

{
   "id":25,
   "category":{"id":1,"code":"002","name":"Category B"},
   "category_id":1,
   "name":"COMPANY SA DE CV",
   "addresses":[
      {
         "id":1,
         "country_id":2,
         "colony":{"id":1,"code":"001","name":"Buenos Aires"},
         "colony_id":1,
         "locality_id":1,
         "municipality_id":1,
         "state_id":1,
         "street":"",
         "external_number":"",
         "internal_number":"",
         "zip_code":"56343",
         "content_type":36,
         "object_id":25
         
}]
}

Моя проблема в том, что если я посылаю (PATCH или PUT) в запросе colony_id, country_id или другие PrimaryKeyRelatedField, я получаю ошибку, так как они не появляются в validated_data. Я пробовал с read_only=False, write_only=True в этих полях

    ...
    File "/Users/obedramales/Sites/webegin-project/webegin/app/serializers.py", line 214, in update
    address_instance.colony_id = address['colony_id']
KeyError: 'colony_id'

Это когда он был обновлен из компании, но если я обновляю только адрес напрямую, то все работает нормально.

Я хотел бы знать, как правильно поступить, пожалуйста?

Простите, если мой английский не очень хорош, это первый раз, когда я участвую здесь.

Заранее спасибо за помощь!

Вам следует использовать поля, определенные в классе Address в models.py

    ...
    for address in addresses:
        address_id = address['id']
        if address_id != 0:
            address_instance = Address.objects.get(id = address_id)
            try:
                country = Country.objects.get(id = address['country_id'])
                address_instance.country = country
                address_instance.save()
            except Country.DoesNotExist:
                raise serializers.ValidationError({'country_id': 'invalid'})
        elif address_id == 0:
            address.pop('id', None)

Я попробовал изменить следующую строку кода

address_instance.country_id = address['country_id']

to

address_instance.country = address['country']

и это работает, Когда я посылаю country_id обновляет правильный id, но необходимо, чтобы country_id было объявлено

country = CountrySerializer(read_only = True)
country_id = serializers.PrimaryKeyRelatedField(source="country", queryset = Country.objects.all())
Вернуться на верх