Django: 'int' object is not iterable when different serializers in a Viewset

Viewed 811

I have an issue when making a GET request on this endpoint: /api/vacancy/{hotel_id}/. It should return vacancy information for a specific Hotel ID.

In my schema, the Vacancy model contains the following properties:

  • hotel_id as a foreign key.
  • room_id as a foreign key.
  • vacancy as integer.
  • rented as integer.

In order to get the relevant information, I intended on calling the HotelDetailsSerializer through the Vacancy Viewset, thinking it might be easier than having to filter the Vacancy table according to the hotel_id value provided in the URL, as it would automatically provide the Vacancy collection related to that specific Hotel ID.

Making a GET request on http://localhost:8000/api/hotels/1 (as shown below) provides the information I need. However when I'm trying to get this response via the Vacancy Viewset with this end-point http://localhost:8000/api/vacancies/1 I get the following error:

TypeError at /api/vacancies/1/
'int' object is not iterable

So is what I'm trying to do even possible in the first place, or is there a better way or some best practise to apply here?

Thanks for your responses!

Here is my Vacancy Serializer file:

from rest_framework import serializers
from ..models.model_vacancy import Vacancy
from .serializers_room import *
from .serializers_hotel import *


class VacancyIndexSerializer(serializers.ModelSerializer):
    """
    Serializer listing all Vacancies models from DB
    """

    room_id = RoomIndexSerializer(allow_null=True)

    class Meta:
        model = Vacancy
        fields = [
            'room_id',
            'hotel_id',
            'vacancy',
            'rented'
        ]

class VacancyDetailsSerializer(serializers.ModelSerializer):
    """
    Serializer showing details of a Vacancy model from DB
    """

    room_id = RoomDetailsSerializer()

    class Meta:
        model = Vacancy
        fields = [
            'room_id',
            'vacancy',
            'rented'
        ]
  
  
class VacancyCreateSerializer(serializers.ModelSerializer):
    """
    Serializer to create a Vacancy model in DB
    """

    class Meta:
        model = Vacancy
        fields = [
            'room_id',
            'hotel_id',
            'vacancy',
            'rented'
        ]

    def create(self, validated_data):
        vacancy = Vacancy.objects.create(**validated_data)
        return stock

class VacancyUpdateSerializer(serializers.ModelSerializer):
    """
    Serializer declaring fields to be provided to update the Vacancy instance.
    """

    class Meta:
        model = Vacancy
        fields = [
            'room_id',
            'hotel_id',
            'vacancy',
            'rented'
        ]

Its Viewset in which I use another Serializer for the retrieve class:

from rest_framework import viewsets
from ..models.model_vacancy import Vacancy
from ..serializers.serializers_vacancy import *
from ..serializers.serializers_hotel import *


class VacancyViewSet(viewsets.ModelViewSet):
    """
    Vacancy ViewSet calling various serializers depending on request type (GET, PUT etc.)
    """
    queryset = Vacancy.objects.order_by('id')

    # mapping serializer into the action
    serializer_classes = {
        'list': VacancyIndexSerializer,
        'retrieve': HotelDetailsSerializer,
        'create': VacancyCreateSerializer,
        'update': VacancyUpdateSerializer,
        'partial_update': VacancyUpdateSerializer
  
    }

    # Your default serializer
    default_serializer_class = VacancyIndexSerializer

    def get_serializer_class(self):
        """
        Method to detect request type (GET, PUT etc.) and select corresponding serializer.
        """
        return self.serializer_classes.get(self.action, self.default_serializer_class)

And here is my other Serializer:

from rest_framework import serializers
from ..models.model_hotel import Hotel
from .serializers_vacancy import *


class HotelIndexSerializer(serializers.ModelSerializer):
    """
    Serializer listing all Hotels models from DB
    """

    class Meta:
        model = Hotel
        fields = [
            'id',
            'name',
        ]

class HotelDetailsSerializer(serializers.ModelSerializer):
    """
    Serializer showing details of a Hotel model from DB
    """

    vacancy = VacancyDetailsSerializer(many=True)

    class Meta:
        model = Hotel
        fields = [
            'vacancy'
        ]
  
class HotelCreateSerializer(serializers.ModelSerializer):
    """
    Serializer to create a Hotel model in DB
    """

    class Meta:
        model = Hotel
        fields = [
            'name',
        ]

class HotelUpdateSerializer(serializers.ModelSerializer):
    """
    Serializer declaring fields to be provided to update the Hotel instance.
    """

    class Meta:
        model = Hotel
        fields = [
            'name',
        ]

In Postman when I'm requesting a GET on http://localhost:8000/api/hotels/1 I get:

{
    "vacancy": [
        {
            "room_id": {
                "room": "first",
                "name": "first one",
                "description": "description"
            },
            "vacancy": 10,
            "rented": 0
        },
        {
            "room_id": {
                "room": "second",
                "name": "second one",
                "description": "description"
            },
            "vacancy": 10,
            "rented": 0
        }
    ]
}
1 Answers

You can't force ModelViewset to return a hotel for retreive actions when it is configured for a vacancy model.

By specifying HotelDetailsSerializer there you produce an error cause when you fetch /api/vacancies/1/ it is a vacancy instance with ID=1 that's being passed to that serialzier which is obviously not right, you should only pass hotel to HotelDetailsSerializer.

Now, a hotel can have multiple vacancies assigned to it (you showed it yourself in /api/hotels/1 response). So if you want to fetch stocks by hotel_id then a request should return multiple vacancies for that bar, not 1.

An API to fetch multiple vacancies is GET /api/vacancies/, all you need is to pass a query param to filter it like this: GET /api/vacancies/?hotel=1

You can achieve it with django-filter

After installing it as stated in docs, add it to your VacancyViewSet:

filterset_fields = ['hotel']
Related