Home > Enterprise >  How to create a model by specifying the field names of the Foreign key that are unique together rath
How to create a model by specifying the field names of the Foreign key that are unique together rath

Time:07-20

I'm trying to configure REST framework serializer to POST a Target object by specifying the 3 field names of it's Foreign relation Market that are unique together (symbol, exchange, type), rather than specifying the primary keys of the Market object.

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)

Instead of this :

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

I would like to post in this form :

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

To achieve this I created a class ModelSerializer and added the 3 custom fields that uniquely specify a market, as suggested in Specifying fields explicitly.

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')

However when I push the data it throws a Bad Request 400. How can I tell him to use this fields as a Foreign key selector ?

CodePudding user response:

You have a couple of choices, I think the best approach might be to try overriding the create / update methods in your serializer, parse the serialized data for the symbol, type and exchange. Then get the Market object with that data and use that to create the Target object.

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'])
  • Related