Как настроить мой сериализатор Django на повторное использование модели, если она существует на основе ее уникального ограничения? - PullRequest
0 голосов
/ 09 апреля 2020

Я использую Django 2.0 и Python 3.7. У меня есть следующие модели, одна из которых используется как ManyToMany другой ...

class CoopTypeManager(models.Manager):

    def get_by_natural_key(self, name):
        return self.get_or_create(name=name)[0]


class CoopType(models.Model):
    name = models.CharField(max_length=200, null=False, unique=True)

    objects = CoopTypeManager()
...
class Coop(models.Model):
    objects = CoopManager()
    name = models.CharField(max_length=250, null=False)
    types = models.ManyToManyField(CoopType)
    address = AddressField(on_delete=models.CASCADE)
    enabled = models.BooleanField(default=True, null=False)
    phone = PhoneNumberField(null=True)
    email = models.EmailField(null=True)
    web_site = models.TextField()

У меня настроены эти сериализаторы ...

class CoopTypeField(serializers.PrimaryKeyRelatedField):

    queryset = CoopType.objects

    def to_internal_value(self, data):
        if type(data) == dict:
            cooptype, created = CoopType.objects.get_or_create(**data)
            # Replace the dict with the ID of the newly obtained object
            data = cooptype.pk
        return super().to_internal_value(data)
...
class CoopTypeSerializer(serializers.ModelSerializer):
    class Meta:
        model = CoopType
        fields = ['id', 'name']

    def create(self, validated_data):
        """
        Create and return a new `CoopType` instance, given the validated data.
        """
        return CoopType.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `CoopType` instance, given the validated data.
        """
        instance.name = validated_data.get('name', instance.name)
        instance.save()
        return instance
...
class CoopSerializer(serializers.ModelSerializer):
    types = CoopTypeSerializer(many=True)
    address = AddressTypeField()

    class Meta:
        model = Coop
        fields = ['id', 'name', 'types', 'address', 'phone', 'enabled', 'email', 'web_site']
        extra_kwargs = {
            'phone': {
                'required': False,
                'allow_blank': True
            }
        }

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep['types'] = CoopTypeSerializer(instance.types).data
        rep['address'] = AddressSerializer(instance.address).data
        return rep

    def create(self, validated_data):
        #"""
        #Create and return a new `Snippet` instance, given the validated data.
        #"""
        coop_types = validated_data.pop('types', {})
        instance = super().create(validated_data)
        for item in coop_types:
            coop_type, _ = CoopType.objects.get_or_create(**item)
            instance.types.add(coop_type)
        return instance

, но POST-запросы с такими полезными нагрузками, как приведенные ниже

{
        "name": "7899",
        "types": [
            {"name": "Library"}
        ],
        "address": {
            "formatted": "222 W. Merchandise Mart Plaza, Suite 1212",
            "locality": {
                "name": "Chicago",
                "postal_code": "60654",
                "state": 19313
            }
        },
        "enabled": true,
        "phone": "7739441426",
        "email": "myemail@msn.com",
        "web_site": "http://www.1871.com/"
}

приводят к этим ошибкам.

{"types":[{"name":["coop type with this name already exists."]}]}

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

Редактировать: Чтобы увидеть эту проблему в действии, репозиторий Git здесь - https://github.com/chicommons/maps, так что вы можете запустить

git clone https://github.com/chicommons/maps.git
cd maps
docker-compose up

и затем запустите тестовый скрипт

sh web/scripts/test_post_req.sh

1 Ответ

0 голосов
/ 20 апреля 2020

Вы пробовали это: coop_type, _ = CoopType.objects.get_or_create(name=item['name'])

...