DjangoCon Europe 2019: Making your life (h)APIer with Django
Writeup of the DjangoCon Europe 2019 talk »Making your life (h)APIer with Django« by Emma Delescolle
Emma Delescolle: Long-time pythonista, Django fan, co-maintainer and co-author of DRF-Schema-Adapter and other OSS libraries. She is from Belgium and has been involved in OSS at different levels for about 15 years now.
APIs
Let's talk about Django APIs. The most prevalent API type Django developers implement are REST APIs with the usual
GET
(list/show), POST
(create), PATCH
& PUT
(update), DELETE
HTTP
verbs. People also may mention
that it's important to make these APIs discoverable – going to get back to that later.
APIs are needed for a variety of things – searchable dropdowns are the most simple use case, but there are larger use cases like completely separate frontends (in the browser via js frameworks, or via standalone desktop/mobile clients), or just providing the API directly or via API clients to users.
Also, the concept is just proven to work, so people fall back to writing an API, and more often than not, they choose to go with REST.
DRF improvements
Within the Django ecosystem, Django Rest Framework has become the de-facto standard. Earlier on there was also Tastypie, which worked similar to frameworks like Rails, making prototyping very easy. In Django Rest Framework, you have viewsets which are provided with serializers and querysets, and produce proper JSON output. Tastypie just had you register a resource from a model, which is great for speed, but a bit awkward for modifications to the standard output.
Especially with many models, starting with DRF can be a lot of work, and it would be so nice to just do something like importing the Django admin urls. So here is an improvement to DRF:
from drf_auto_endpoint.router import router, register from drf_auto_endpoint.endpoints import Endpoint @register class ProductEndpoint(Endpoint): model = Product router.register(ProductEndpoint) urlpatterns = [url(r'^api/', include(router.urls))] INSTALLED_APPS.append('drf_auto_endpoint')
And then the URLs will just be auto generated. This is brilliant to do for rapid prototyping. For customisations, you can do many things similar to admin modifications, by providing order/filter fields.
Similarly, serializers are abstracted and simplified via a serializer factory, which wraps DRF in a series of factories. This keeps you from repeating standard default information. The combination of auto generated viewsets, urls, and serializers makes for very fast prototyping and modifications.
Discoverability
People say "discoverability", they expect to see URLs instead of IDs in foreign keys. This is nice for humans, but gets
repetitive fast. DRF already allows you to provide metadata classes, which modify the response to OPTIONS
requests.
OPTIONS
usually gives you additional information regarding expected formats, encodings, required fields, etc.
We want to expand this data by providing metadata regarding related models, for example.
Actions
REST APIs usually need interactive elements, so-called action endpoints, which trigger certain actions that can't or shouldn't be simple model updates. DRF already tells you about actions you can perform on the URL you are on, but you would want to see related (child) action URLs and their required attributes, too.
Custom adapter implications
So custom adapters can be very helpful. Many custom adapters claim to use JSONSchema, but since its functionality doesn't include foreign keys etc, that's never quite true. Emma's tooling provides adapters for Angular, React, and Ember (most complete), which heavily assists in form and page generation off of the DRF metadata output. Adding your own adapter is not hard either, though!
For ember, you usually define models and APIs, since it's a complete MVC framework. These models mirror the Django models very directly. So the custom adapter includes a js file that provides the model as rendered by DRF. This results in you being able to render complete pages and forms without knowing anything about the models themselves.
See: DRF-Schema-Adapter