Ji.Hoon. Kim
Ji.Hoon. Kim

Reputation: 31

django rest framework return a custom object using ModelSerializer and ModelViewSet

I have three models, three serializers, one modelviewset below. I am using django-rest-framework to make a rest api for android.

The restaurant model was created first. Then I created a star model and an image model.

What I want to do is to add star and image objects into restaurant objects.

finally I've got what I want result but I think my viewset code looks like wrong..

Is there another way not to use "for loop"?

Models

class Restaurant(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    address = models.CharField(max_length=255)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    weather = models.ForeignKey(Weather, on_delete=models.CASCADE)
    distance = models.ForeignKey(Distance, on_delete=models.CASCADE)
    description = models.TextField('DESCRIPTION')

    def __str__(self):
        return self.name

class Star(models.Model):
    restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    rating = models.IntegerField('RATING')

    def __str__(self):
        return self.restaurant


class RestaurantImage(models.Model):
    id = models.AutoField(primary_key=True)
    restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
    path = models.CharField(max_length=255)

Serializer

class StarSerializer(serializers.ModelSerializer):
    class Meta:
        model = Star
        fields = ('id', 'restaurant', 'user', 'rating', )


class RestaurantDetailSerializer(serializers.ModelSerializer):
    category = CategorySerializer()
    weather = WeatherSerializer()
    distance = DistanceSerializer()

    class Meta:
        model = Restaurant
        fields = ('id', 'name', 'address', 'category', 'weather',
                  'distance', 'description', )


class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = RestaurantImage
        fields = ('id', 'path', 'restaurant')

ViewSet

class RestaurantDetailInfoViewSet(viewsets.ModelViewSet):
    queryset = Restaurant.objects.all()
    serializer_class = RestaurantSerializer

    def list(self, request, *args, **kwargs):
        restaurant_list = Restaurant.objects.all()
        restaurant_result = []
        for restaurant in restaurant_list:
            restaurantInfo = Restaurant.objects.filter(id=restaurant.pk)
            restaurant_serializer = RestaurantDetailSerializer(restaurantInfo, many=True)
            ratingAverageValue = Star.objects.filter(restaurant=restaurant.pk).aggregate(Avg('rating'))
            images = RestaurantImage.objects.filter(restaurant=restaurant.pk)
            image_serializer = ImageSerializer(images, many=True)

            restaurant_dic = {
                'restaurant': restaurant_serializer.data,
                'ratingAverage': ratingAverageValue['rating__avg']
                if ratingAverageValue['rating__avg'] is not None else 0,
                'images': image_serializer.data
            }
            restaurant_result.append(restaurant_dic)

        return Response(restaurant_result)

Result

[
{
    "restaurant": [
        {
            "id": 1,
            "name": "restaurant1",
            "address": "address1",
            "category": {
                "c_id": 1,
                "name": "foodtype1"
            },
            "weather": {
                "w_id": 1,
                "name": "sunny"
            },
            "distance": {
                "d_id": 1,
                "name": "inside"
            },
            "description": "description1"
        }
    ],
    "ratingAverage": 2.6667,
    "images": [
        {
            "id": 1,
            "path": "imagepath",
            "restaurant": 1
        }
    ]
},

Upvotes: 2

Views: 2972

Answers (1)

Sachin
Sachin

Reputation: 3664

Solution:

class RestaurantDetailSerializer(serializers.ModelSerializer):
    category = CategorySerializer()
    weather = WeatherSerializer()
    distance = DistanceSerializer()

    images = ImageSerializer(many=True, read_only=True)
    ratingAverage = serializers.SerializerMethodField(read_only=True)

    def get_ratingAverage(self, restaurant):
        ratingAvgVal = Star.objects.filter(
                         restaurant=restaurant
                       ).aggregate(Avg('rating'))['rating__avg']
        return ratingAvgVal if ratingAvgVal is not None else 0

    class Meta:
        model = Restaurant
        fields = ('id', 'name', 'address', 'category', 'weather',
                  'distance', 'description', 'images', 'ratingAverage', )

Explanation:

Here, I have nested the ImageSerializer in the RestaurantSerializer class, since you needed all the fields you've defined in ImageSerializer.
Then, for ratingAverage, I have used the SerializerMethodField which returns the value calculated (your logic) in the method I've defined for it, i.e. get_ratingAverage, which takes the Restaurant instance reference passed as an argument to the method for the field.

Upvotes: 2

Related Questions