2

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
        }
    ]
},

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.