10

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

1 Answer 1

15

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. :)

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

4 Comments

Thanks Tom! I tried your approach and the second part with the CombinedAPIView is working great! However, the first suggestion is not working. It's probably because I'm using the generics.RetrieveAPIView?? Here is the error: Expected view LocationListAPIView to be called with a URL keyword argument named "pk". Fix your URL conf, or set the .lookup_field` attribute on the view correctly.`
After that I simply tried to use CombinedAPIView to test the url: url(r'^location/$', views.CombinedAPIView.as_view(), name='location-list'), This works, but it's not the solution :) Hope you can help me fix this error. Thanks in advance.
Ok, I fixed this using APIView instead. Anyway, do you have an idea how to fix it with generics.RetrieveAPIView?? Here is the solution which works for me: class LocationListAPIView(APIView): def get(self, request): locations = Location.objects.all() location_serializer = LocationSerializer(locations, many=True) return Response({ 'locations': location_serializer.data, })
"generics.RetrieveAPIView" - I just corrected a typo there, the list view should be extending generics.ListAPIView.

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.