Как создать модель, указав имена полей внешнего ключа, которые являются уникальными вместе, а не его pk?

Я пытаюсь настроить сериализатор REST-фреймворка на POST объекта Target, указав имена трех полей его Foreign relation Market, которые уникальны вместе (символ, обмен, тип), вместо того, чтобы указывать первичные ключи объекта Market.

models.py

class Exchange(models.Model):
    exid = models.CharField(max_length=12, unique=True)


class Market(models.Model):
    symbol = models.CharField(max_length=5)
    type = models.CharField(max_length=5)
    exchange = models.ForeignKey(Exchange, on_delete=models.CASCADE, related_name='market')

    class Meta:
        unique_together = ['symbol', 'type', 'exchange']


class Target(models.Model):
    weight = models.FloatField()
    exchange = models.ForeignKey(Exchange, on_delete=models.CASCADE, related_name='target')
    market = models.ForeignKey(Market, on_delete=models.CASCADE, related_name='target')
    dt = models.DateTimeField(null=True)

Вместо этого :

{
    "weight": 19.23,
    "market": 11,
    "dt": "2022-06-09"
}

Я хотел бы разместить сообщение в этой форме :

{
    "weight": 0.1923,
    "market_symbol": "ABC/USD",
    "market_type": "xyz",
    "market_exchange_exid": "my_exchange",
    "dt": "2022-06-09"
}

Для этого я создал класс ModelSerializer и добавил 3 пользовательских поля, которые однозначно определяют рынок, как было предложено в Указание полей явно.

class TargetSerializer(serializers.ModelSerializer):
    market_symbol = serializers.StringRelatedField()
    market_type = serializers.StringRelatedField()
    market_exchange = serializers.StringRelatedField()

    class Meta:
        model = Target
        fields = ('id', 'weight', 'dt', 'market_symbol', 'market_type', 'market_exchange')

Однако когда я проталкиваю данные, он выбрасывает ошибку Bad Request 400. Как я могу сказать ему, чтобы он использовал это поле в качестве селектора внешнего ключа?

У вас есть несколько вариантов, я думаю, что лучшим подходом может быть попытка переопределить методы create / update в вашем сериализаторе, разобрать сериализованные данные для символа, типа и обмена. Затем получить объект Market с этими данными и использовать его для создания объекта Target.

class MarketSerializer(serializers.ModelSerializer):
    class Meta:
        model = Market
        fields = ('id', 'symbol', 'type', 'exchange',)


class TargetSerializer(serializers.ModelSerializer):
    market_symbol = serializers.CharField(max_length=200, write_only=True)
    market_type = serializers.CharField(max_length=200, write_only=True)
    market_exchange = serializers.CharField(max_length=200, write_only=True)
    market = MarketSerializer(read_only=True)

    class Meta:
        model = Target
        fields = ('id', 'weight', 'dt', 'market', 'market_symbol', 'market_type', 'market_exchange')

    def create(self, validated_data):
        market = get_object_or_404(
            Market, 
            symbol=validated_data['market_symbol'],
            type=validated_data['market_type'],
            exchange=validated_data['market_exchange'],
        )
        return Target.objects.create(market=market, weight=validated_data['weight'], dt=validated_data['dt'])
Вернуться на верх