Как использовать field.set () в функции создания для Django REST Framework ModelSerializer? - PullRequest
0 голосов
/ 06 февраля 2020

У меня есть промежуточная модель под названием RoomMarket, соединяющая 2 связанные модели. Market - RoomMarket - Room. Когда я попытался вставить запись для RoomMarket, используя вложенный сериализатор, доступный для записи, я получил ошибку:

TypeError at / room_markets /
Прямое назначение на переднюю сторону многие-ко-многим набор запрещен. Вместо этого используйте region.set ().

Вот мой упрощенный models.py:

class RecordStatus:
    published = 'Published'
    drafted = 'Drafted'
    hidden = 'Hidden'
    status = [
        (published, 'Published'),
        (drafted, 'Drafted'),
        (hidden, 'Hidden'),
    ]


class Market(models.Model, RecordStatus):
    name = models.CharField(max_length=100)
    regions = models.ManyToManyField(Region)
    languages = models.ManyToManyField(Language)
    status = models.CharField(max_length=20, choices=RecordStatus.status, default=RecordStatus.published)

    @property
    def get_product_variations(self):
        return Product.objects.filter(distributor__region__market=self).distinct().count()


class Room(models.Model, RecordStatus):
    style = models.ForeignKey(Style, on_delete=models.CASCADE)
    markets = models.ManyToManyField(Market, through='RoomMarket')
    image = models.ImageField(upload_to='room_images', width_field=None, height_field=None,
                              max_length=250, null=True, blank=True)
    name = models.CharField(max_length=50)
    status = models.CharField(max_length=20, choices=RecordStatus.status, default=RecordStatus.published)


class RoomMarket(models.Model):
    market = models.ForeignKey(Market, on_delete=models.CASCADE)
    room = models.ForeignKey(Room, on_delete=models.CASCADE)
    landing_page = models.BooleanField(blank=True, null=True)

    def __str__(self):
        return '%s - %s' % (self.market, self.room)

Вот мой serializers.py:

class CustomRelatedField(serializers.RelatedField):
    def display_value(self, instance):
        return instance

    def to_representation(self, value):
        return str(value)

    def to_internal_value(self, data):
        model = self.queryset.model
        return model.objects.get(id=data)


class MarketSerializer(serializers.ModelSerializer):
    regions = CustomRelatedField(many=True, queryset=Region.objects.all())
    languages = CustomRelatedField(many=True, queryset=Language.objects.all())
    variation = serializers.ReadOnlyField(source='get_product_variations')
    landing_page = serializers.SerializerMethodField(source='get_landing_page')

    class Meta:
        model = Market
        fields = ['id', 'regions', 'languages', 'name', 'status', 'variation', 'landing_page']
        depth = 1

    @staticmethod
    def get_landing_page(market):
        queryset = RoomMarket.objects.filter(market=market)
        if queryset.exists():
            for r in queryset:
                return r.room.id
        else:
            return '-'


class RoomSerializer(serializers.ModelSerializer):
    style = CustomRelatedField(many=False, queryset=Style.objects.all())
    markets = CustomRelatedField(many=True, queryset=Market.objects.all())

    class Meta:
        model = Room
        fields = ['id', 'markets', 'style', 'image', 'name', 'status']


class RoomMarketSerializer(serializers.ModelSerializer):
    market = MarketSerializer()
    room = CustomRelatedField(many=False, queryset=Room.objects.all())

    class Meta:
        model = RoomMarket
        fields = ['id', 'market', 'room', 'landing_page']

    def create(self, validated_data):
        # create market data for Market model.
        market_data = validated_data.pop('market')
        market = Market.objects.create(**market_data)

        # create RoomMarket and set market FK.
        room_market = RoomMarket.objects.create(market=market, **validated_data)

        # return RoomMarket instance.
        return room_market

Вот мой views.py:

class RoomMarketView(viewsets.ModelViewSet):
    permission_classes = [permissions.DjangoModelPermissions]
    queryset = RoomMarket.objects.all()
    serializer_class = RoomMarketSerializer

Формат JSON, который я использую для POST-данных:

{
    "market": {
        "regions": [1,2],
        "languages": [1,2],
        "name": "Asia",
        "status": "Published"
    },
    "room": 2,
    "landing_page": true
}

Я прочитал несколько SO вопросов по этому вопросу, но большинство случаев касаются формы / представления, а не функции create в ModelSerializer класс. Что я должен изменить в create функции моей промежуточной модели ModelSerializer класса?

1 Ответ

1 голос
/ 06 февраля 2020

Проблема заключается в том, что при создании экземпляра market вы передаете объект region и language методу create(). Вы должны передать его set() вместо:

def create(self, validated_data):
    # create market data for Market model.
    market_data = validated_data.pop('market')
    regions = market_data.pop("regions")
    languages = market_data.pop("languages")
    market = Market.objects.create(**market_data)
    market.regions.set(regions)
    market.languages.set(languages)
...