If you are new to Django and need to create RESTful APIs, use Django Rest Framework (avoid Piston or Tastypie). It’s fairly easy to get REST APIs up and running, but things can get a little confusing when you need to customize functionality.

Two problems I seem to solve a lot are adding filters via query parameters and excluding fields via query parameters.

Let’s say you have an API call that lists all the buildings in your database:

https://www.somesite.com/api/v1/buildings

returns

{
"id": 110,
"submission": 120,
"description": "A Building",
"street": "701 Peach Blvd",
"city": "West Palm Beach",
"state": "FL",
"zipcode": "33401",
"county": "Orleans",
"latitude": 24.706871,
"longitude": -81.0611,
},
{
"id": 111,
"submission": 120,
"description": "A Building",
"street": "701 Peach Blvd",
"city": "West Palm Beach",
"state": "FL",
"zipcode": "33401",
"county": "Orleans",
"latitude": 24.706871,
"longitude": -81.0611,
},

{
"id": 112,
"submission": 122,
"description": "A Building",
"street": "701 Peach Blvd",
"city": "West Palm Beach",
"state": "FL",
"zipcode": "33401",
"county": "Orleans",
"latitude": 24.706871,
"longitude": -81.0611,
},

Now lets say I want to be able to filter out only data points which have submission=120. Unfortunately DRF does not offer this functionality out of the box. To do this, you need to add some code to APIView:

class BuildingView(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated, )
queryset = models.Building.objects.all()
serializer_class = serializers.BuildingSerializer

def get_queryset(self):
""" allow rest api to filter by submissions """
queryset = models.Building.objects.all()
submission = self.request.query_params.get('submission', None)
if submission is not None:
queryset = queryset.filter(submission=submission)

return queryset

As you can see, I am overriding get_queryset and checking to see if the submission get query parameter is being passed. If it is, then I return a filter queryset rather than the queryset containing all objects in the database.

Sometimes it is also useful to be able to filter the json responses. Say I only way to see the id, latitude, and longitude from the above json response. You can easily provide this functionality by updating your Serializer

class BuildingSerializer(serializers.ModelSerializer):
""" `Building` model serializer """

def __init__(self, *args, **kwargs):

super(BuildingSerializer, self).__init__(*args, **kwargs)
request = self.context.get("request")
if request and request.query_params.get('fields'):
fields = request.query_params.get('fields')
if fields:
fields = fields.split(',')
allowed = set(fields)
existing = set(self.fields.keys())
for field_name in existing - allowed:
self.fields.pop(field_name)

As you can see, here I am overriding the BuildingSerializer constructor. Again, I am checking to see if fields query parameter is being passed, and if it is, I filter the response by the fields that are provided (comma separated list). This code will enable API calls as follows:

https://www.somesite.com/api/v1/buildings?fields=id,latitude,longitude

returns

{
"id": 110,
"latitude": 24.706871,
"longitude": -81.0611,
},
{
"id": 111,
"latitude": 24.706871,
"longitude": -81.0611,
},

{
"id": 112,
"latitude": 24.706871,
"longitude": -81.0611,
},

You can also combine both methods:

https://www.somesite.com/api/v1/buildings?fields=id,latitude,longitude&submission=120

returns

{
"id": 110,
"submission": 120,
"latitude": 24.706871,
"longitude": -81.0611,
},
{
"id": 111,
"submission": 120,
"latitude": 24.706871,
"longitude": -81.0611,
},

DRF is really an awesome piece of software. You just need to get used to some of these edge cases.