Search code examples
pythondjangodjango-rest-frameworkdjango-serializer

django rest framework serializer method field how to


How do I handle this serializer method field? I feel that using initial_data is wrong, do i need to validate data when extracting it from the database? Do I need to use a serializer when extracting data from database?

class ListingSerializer(serializers.ModelSerializer):

    rooms = serializers.SerializerMethodField()

    # This one works as expected
    def get_rooms(self, obj):
        rooms = list(Room.objects.filter(listing__id=obj.id).values())
        serializer = RoomSerializer(data=rooms, many=True)
        return serializer.initial_data
    
    # This one gives serializer errors
    def get_rooms(self, obj):
        rooms = list(Room.objects.filter(listing__id=obj.id).values())
        serializer = RoomSerializer(data=rooms, many=True)
        if serializer.is_valid():
            return serializer.data
        return serializer.errors

    class Meta:
        model = Listing
        fields = "__all__"
class Listing(models.Model):
    id = models.CharField(
        primary_key=True, default=generate_uuid, editable=False, max_length=36
    )
    agent = models.ForeignKey(
        "users.User", on_delete=models.CASCADE, blank=True, null=True
    )

    # property data
    title = models.CharField(max_length=100, blank=False, null=False)
    description = models.CharField(max_length=1000, blank=False, null=False)
    floor = models.IntegerField(blank=False, null=False)
    floor_count = models.IntegerField(blank=False, null=False)
    price = models.DecimalField(max_digits=10, decimal_places=2, default=Decimal(0.00))

    # address
    street = models.CharField(max_length=60, blank=False, null=True)
    house_no = models.CharField(max_length=10, blank=False, null=True)
    door_no = models.CharField(max_length=10, blank=False, null=True)
    city = models.CharField(max_length=20, blank=False, null=True)
    country = models.CharField(max_length=20, blank=False, null=True)
    postal_code = models.IntegerField(blank=False, null=True)

    # property reports
    tilstand_report = models.FileField(upload_to="reports", blank=False, null=True)
    water_consumption_report = models.FileField(
        upload_to="reports", blank=False, null=True
    )
    energy_level_report = models.FileField(upload_to="reports", blank=False, null=True)
    property_tax_report = models.FileField(upload_to="reports", blank=False, null=True)

    # metadata
    is_active = models.BooleanField(default=True)
    create_time = models.BigIntegerField(blank=True, null=True)

    def save(self, *args, **kwargs):
        self.create_time = int(datetime.now().timestamp() * 1000)
        super().save(*args, **kwargs)

    class Meta:
        ordering = ["create_time"]

Solution

  • You can use the RoomSerializer as a sub serializer:

    class ListingSerializer(serializers.ModelSerializer):
        rooms = RoomSerializer(source='room_set', many=True)
    
        class Meta:
            model = Listing
            fields = '__all__'

    The source=… should specify the related_name=… of the ForeignKey in the Room model. If you did not specify a related_name=…, the default is modelname_set, so here room_set.