Skip to content

Documentation and Examples

cootetom edited this page Jun 13, 2011 · 4 revisions

The best way to show how this package can be used is to show a code example. Have a read through this code then I'll explain some bits after.

from django.utils.decorators import method_decorator

from ClassBasedGenericAPI.decorators import basic_api_login_required, throttle
from ClassBasedGenericAPI.views import APIView
from forms import CustomerForm
from models import Customer

class CustomerAPI(APIView):
	'''
	API for the Customer modal. Dealing with singular model instances only.
	'''
	
	@method_decorator(throttle(10, time_period=60)) # no more than 10 in a minute.
	@method_decorator(basic_api_login_required(realm='Customers API'))
	def get(self, request):
		'''Return a single Customer with the given ID.'''
		try:
			customer = Customer.objects.get(id=request.DATA['id'])
		except:
			return self.http_response_not_found(request, 'Customer not found')
		
		# need to return serializable data so keep it as simple python data types.
		return {'name': customer.name,
				'address': customer.address,
				'city': customer.city,
				'state_province': customer.state_province,
				'country': customer.country,
				'website': customer.website
				}
	
	@method_decorator(throttle(10, time_period=60, namespace='writes'))
	@method_decorator(basic_api_login_required(realm='Customers API'))
	def post(self, request):
		'''Create a single Customer.'''
		form = CustomerForm(request.DATA)
		
		if form.is_valid():
			form.save()
		else:
			return self.http_response_bad_request(request)
			
		return self.http_response_ok(request, 'Customer created')
	
	@method_decorator(throttle(10, time_period=60, namespace='writes'))
	@method_decorator(basic_api_login_required(realm='Customers API'))
	def put(self, request):
		'''Update a single Customer.'''
		try:
			customer = Customer.objects.get(id=request.DATA['id'])
		except:
			return self.http_response_not_found(request, 'Customer not found')
		
		form = CustomerForm(request.DATA, instance=customer)
		
		if form.is_valid():
			form.save()
		else:
			return self.http_response_bad_request(request)
		
		return self.http_response_ok(request, 'Customer updated')
	
	@method_decorator(throttle(10, time_period=60, namespace='writes'))
	@method_decorator(basic_api_login_required(realm='Customers API'))
	def delete(self, request):
		'''Delete a single Customer.'''
		try:
			Customer.objects.get(id=request.DATA['id']).delete()
		except:
			return self.http_response_not_found(request, 'Customer not found')
		
		return self.http_response_ok(request, 'Customer deleted')

Class-based view

The first thing to notice is that we're using the class-based generic views from django for our API. The class here inherits from ClassBasedGenericAPI.views.APIView because the APIView is what will provide our API functionality.

View methods

Inside the class we can create view method functions for each request method (POST, GET, PUT, DELETE) that we want to support for this API call.

The view methods are just like normal class-based view methods in that the correct method is called depending on the request method used (POST, GET, PUT, DELETE).

Each method type should perform the appropriate action;

  • POST: used to create new data.
  • GET: used to retrieve data.
  • PUT: used to update existing data.
  • DELETE: used to delete existing data.

The APIView class processes the data being sent to the API calls and always places that data into request.DATA so that each method can access sent data from the same place.

Returning data from your view

Each view can either return data or a normal HttpResponse object. Data is usually returned as the result of a GET method call. It must be returned in a form that is serializable by one of the supported API format mixin's.

As well as data the APIView provides many standard responses for use in your API.

Throttling API calls

Sometimes an API will limit the amount of calls from a single user in an attempt to stop miss/over use. This can be performed in our API by using the decorator ClassBasedGenericAPI.decorators.throttle.

To throttle your API add the throttle decorator to your view methods as shown in the above example. The decorator accepts 3 arguments;

  • limit (required): how many times the API can be called.
  • time_period (defaults to 1 hour): length of time, in seconds, that the limit should apply to. e.g. 10 hits a minute.
  • namespace (defaults to ''): the namespace is a way of having different limits for different methods. The call count for an API method is incremented per namespace. In the above example we can see that the GET method will allow 10 hits per minute but the POST, PUT and DELETE methods will allow 10 hits per minute combined. So if the user calls the POST method 10 times in one minute then tries to call the PUT method they'll be throttled because these methods have their limit in the same namespace.

Requiring authentication

If you need to support authentication for access to your API methods then you can use the decorator ClassBasedGenericAPI.decorators.basic_api_login_required. This decorator supports base64 HttpBasicAuthentication. When placed on an API method it will look for the HttpBasicAuth username and password in the request and attempt a login for a matching django user. If no authentication is present then it will send a request for authentication back to the user.

URL's

To allow access to your API view methods you need to create URL's for them in your django urls.py file.

from django.conf.urls.defaults import patterns

from api import CustomerAPI

urlpatterns = patterns('',
	(r'^add_customer/?$', CustomerAPI.as_view()),
	(r'^get_customer.(?P<format>\w+)$', CustomerAPI.as_view()),
	(r'^update_customer/$', CustomerAPI.as_view()),
	(r'^delete_customer/$', CustomerAPI.as_view()),
)

I have used four different URL's here for good readability for API users but only one is actually needed because the correct API view method is called depending on the request methods used (POST, GET, PUT or DELETE).

Request data

The APIView will parse incoming data to an API call and put that data into request.DATA before calling the correct API method. It does this by asking each mixin to look for data in the request that it supports then go ahead and parse that data into request.DATA if it finds it. If the call is a normal form POST or query string GET method then the APIView itself will parse that data. If the call contains JSON content and the content type is "application/json" then the JSON mixin will parse that data in to request.DATA. Other mixin's can be created to extend the supported data for your own API.

Response data

The data sent back in a response is parsed depending on the 'format' variable in the URL. An example of this is shown in my 'get_customer' URL above. The format must be supported by one of the mixin's. For example if the URL was '/get_customer.json?id=1' then the format will be 'json' and the JSON mixin will serialize any data returned from your API view method into a json string as the response content.

Further reading