Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a response of multiple objects using rest_framework and Django

I'm new to the Django framework and the Django REST framework, but I got the basic setup and implementation running. It's working like a charm when I call the domain for single objects, e.g. http://mydomain.com/location/1 (where 1 is the primary key). This gives me the JSON response like:

{"id": 1, "location": "Berlin", "country": 2}

.. and http://mydomain.com/country/2 responses like:

{"id": 2, "country": "Germany"}

What I need: Now I need to get multiple locations, e.g. when calling the domain http://mydomain.com/all_locations/. I would expect a response like:

[
  {"id": 1, "location": "Berlin", "country": 2},
  {"id": 2, "location": "New York", "country": 4},
  {"id": 3, "location": "Barcelona", "country": 5},
  {"id": 4, "location": "Moscow", "country": 7}
]

This is optional: In a second step I would love to have multiple countries and locations in one response when I call http://mydomain.com/mix_all_locations_countries/, for example:

[
  {"locations":
    {"id": 1, "location": "Berlin", "country": 2},
    {"id": 2, "location": "New York", "country": 4},
    {"id": 3, "location": "Barcelona", "country": 5},
    {"id": 4, "location": "Moscow", "country": 7}
  },
  {"countries":
    {"id": 1, "country": "Brazil"}
    {"id": 2, "country": "Germany"},
    {"id": 3, "country": "Portugual"}
    {"id": 4, "country": "USA"},
    {"id": 5, "country": "Spain"},
    {"id": 6, "country": "Italy"}
    {"id": 7, "country": "Russia"}
  }
]

Here's my implementation so far (just showing the implementation of location):

in models.py:

class Location(models.Model):
    # variable id and pk are always available
    location = models.CharField(max_length=100)
    country = models.ForeignKey("Country")

in serializers.py:

class LocationsSerializer(serializers.ModelSerializer):
    country_id = serializers.Field(source='country.id')

    class Meta:
        model = Location
        fields = (
            'id',
            'location',
            'country_id',
        )

in views.py:

class LocationAPIView(generics.RetrieveAPIView):
    queryset = Location.objects.all()
    serializer_class = LocationSerializer

in urls.py:

url(r'^location/(?P<pk>[0-9]+)/$', views.LocationAPIView.as_view(), name='LocationAPIView')

What I tried: I think I do not need to modify the model and serializer, because it's working for single objects when calling the domain mentioned above. So I tried to implement a LocationsViewSet in views.py and added a new url in urls.py, but I failed. Any idea how I could implement it? Maybe just define a method in LocationAPIView and change define a url similar to this:

url(r'^all_locations/$', views.LocationAPIView.get_all_locations(), name='LocationAPIView')

Thanks in advance, I'll appreciate any help.

Best regards, Michael

like image 489
Michael Avatar asked Aug 29 '13 10:08

Michael


1 Answers

First up, let's just forget about viewsets for the moment - they make some things more simple, but they also introduce an extra layer of abstraction that I don't think you should be concerned with right now.

The first thing you mention as needing is a list endpoint equivalent to your current detail endpoint. You've pretty much already got that, you just need to introduce an extra view alongside your existing view.

views.py:

class LocationListAPIView(generics.ListAPIView):
    queryset = Location.objects.all()
    serializer_class = LocationSerializer

class LocationDetailAPIView(generics.RetrieveAPIView):
    queryset = Location.objects.all()
    serializer_class = LocationSerializer

Now wire up both views in your URLconf.

urls.py:

url(r'^location/$', views.LocationListAPIView.as_view(), name='location-list'),
url(r'^location/(?P<pk>[0-9]+)/$', views.LocationDetailAPIView.as_view(), name='location-detail')

Note that I've also changed the URL name style to make it more standard with the usual Django conventions.

Next up, you wanted a combined location + countries view. You won't just be able to use an existing generic view for that since it's fairly custom behaviour, but it's easy enough to write a view for...

views.py:

class CombinedAPIView(APIView):
    def get(self, request):
        locations = Location.objects.all()
        countries = Country.objects.all()

        location_serializer = LocationSerializer(locations, many=True)
        country_serializer = CountrySerializer(countries, many=True)

        return Response({
            'countries': country_serializer.data,
            'locations': location_serializer.data
        })

And wire the view up.

urls.py:

url(r'^combined/$', views.CombinedAPIView.as_view(), name='combined-list')

Note that you don't need to use serializers at all when generating the responses, you could just as well pull out all the required fields on each instance, building data for the response explicitly in the view itself, but it's a nice standard way to map model instances into dictionaries of data.

Hopefully that'll give you enough to get started with. :)

like image 144
Tom Christie Avatar answered Nov 08 '22 17:11

Tom Christie