In DRF how do I serialize a related model(OneToOne) and display the data not in a list data type but single value instance?

Question:

The code is given below with the present output and expected output.
In the ProductPriceMapping table the ProductDetail table and PriceList are related with a OneToOne relation, but When the data for Price is fetched with the related_name argument there must be one value for one product, the data is being displayed is a list data type.

models.py

from django.db import models

class PriceList(models.Model):
    priceCode = models.BigAutoField(primary_key= True)
    maxRetailPrice= models.FloatField(max_length=20)
    baseDiscount = models.FloatField(max_length=20, default=0)
    seasonalDiscount = models.FloatField(max_length=20, default=0)

    def __str__(self):
        return '%s'% (self.maxRetailPrice)

class ProductDetail(models.Model):
    productCode = models.BigAutoField(primary_key=True)
    productName = models.CharField(max_length=100)
    manufacturer = models.CharField(max_length=100)

    def __str__(self):
        return self.productName

class ProductPriceMapping(models.Model):
    productPriceCode= models.BigAutoField(primary_key=True)
    productCode= models.ForeignKey(ProductDetail,on_delete=models.CASCADE,related_name='price')
    priceCode= models.OneToOneField(PriceList,on_delete=models.CASCADE)

    def __str__(self):
        return '%s' % (self.priceCode)

serializers.py

from rest_framework import serializers
from .models import CategoryDetail, EmployeeDetail, ProductCategoryMapping, ProductPriceMapping, SalaryDetail, ProductDetail, PriceList

class ProductPriceListSerializer(serializers.ModelSerializer):
    class Meta:
        model = PriceList
        fields = ('priceCode','maxRetailPrice',
                  'baseDiscount', 'seasonalDiscount')

class ProductPriceMappingSerializer(serializers.ModelSerializer):
    class Meta:
        model= ProductPriceMapping
        fields= ('productPriceCode','productCode', 'priceCode')

class ProductDetailsSerializer(serializers.ModelSerializer):
    category= serializers.StringRelatedField(many= True, read_only= True)
    price = serializers.StringRelatedField( many= True, read_only= True)
    class Meta:
        model = ProductDetail
        fields = ('productCode', 'productName', 'manufacturer','category', 'price')

The result of API as it looks:

[
    {
        "productCode": 1,
        "productName": "NeoChef",
        "manufacturer": "LG",
        "category": [
            "1: Microwave Oven"
        ],
        "price": [
            "26000.0"  ##expected the price value not be in a list
        ]
    },
    {
        "productCode": 2,
        "productName": "The Frame",
        "manufacturer": "Samsung",
        "category": [
            "2: Television"
        ],
        "price": [
            "120000.0" ##expected the price value not be in a list
        ]
    },
    {
        "productCode": 3,
        "productName": "Galaxy S22+",
        "manufacturer": "Samsung",
        "category": [
            "3: Smart Phone"
        ],
        "price": [
            "79000.0" ##expected the price value not be in a list
        ]
    }
]

Expected result:

[
    {
        "productCode": 1,
        "productName": "NeoChef",
        "manufacturer": "LG",
        "category": [
            "1: Microwave Oven"
        ],
        "price": "26000.0"  
    }
]```
Asked By: Jabed Akhtar

||

Answers:

In your case, the price field is not a one-to-one related field, you either need to change the productCode to OneToOneField or if you don’t want to change the DB field, you can achieve the same result simply with SerializerMethodField. In the first case, removing many=True argument from the serializer field should help. In second case, SerializerMethodField will help you to make your custom representation, e.g.:

class ProductDetailsSerializer(serializers.ModelSerializer):
    category= serializers.StringRelatedField(many=True, read_only=True)
    price = serializers.SerializerMethodField()

    class Meta:
        model = ProductDetail
        fields = ('productCode', 'productName', 'manufacturer','category', 'price')

    def get_price(self, obj):
        # If it's guaranteed that there will be only one related object, or retrieve the needed object depending on your demands
        return str(obj.price.first())
Answered By: Ersain

One simple way is to use MethodField

class ProductPriceMappingSerializer(serializers.ModelSerializer):
    priceCode = serializers.SerializerMethodField()

    class Meta:
        model= ProductPriceMapping
        fields= ('productPriceCode','productCode', 'priceCode')
    
    @staticmethod
    def get_priceCode(obj):
        return obj.priceCode.maxRetailPrice  # or any other fields that you like to show in your response

but if you want to show all of the priceList fields based on your other serializer you can do something like:

class ProductPriceMappingSerializer(serializers.ModelSerializer):
    priceCode = ProductPriceListSerializer()

    class Meta:
        model= ProductPriceMapping
        fields= ('productPriceCode','productCode', 'priceCode')
Answered By: Roham

Thank you guys, I solved in this way:

models.py

class ProductPriceMapping(models.Model):
    productPriceCode= models.BigAutoField(primary_key=True)
    productCode= models.OneToOneField(ProductDetail,on_delete=models.CASCADE,related_name='price')
    priceCode= models.ForeignKey(PriceList,on_delete=models.CASCADE)

    def __str__(self):
        return '%s' % (self.priceCode)

serializers.py

class ProductDetailsSerializer(serializers.ModelSerializer):
    category= serializers.StringRelatedField(many= True, read_only= True)
    price = serializers.StringRelatedField(read_only= True)
    class Meta:
        model = ProductDetail
        fields = ('productCode', 'productName', 'manufacturer','category', 'price')
Answered By: Jabed Akhtar