REST Framework Intro
As this project is deeply based on Django REST Framework, it’s good to give a quick overview beyond the standard “serializer” flow that everyone is familiar with.
An overview of relevant components (yellow are our custom classes, blue are base classes):
Views
To understand Django REST Framework, it’s good to understand it’s base view classes have the following “pipeline”:
This is both the strength and weakness of Django REST Framework. Each step has swappable components and can be extended. However, the paginator doesn’t really take the rendering format into account, nor does the filtering know what attributes are rendered by the serializer. These weaknesses are handled by deeply inspecting the source code for possible hooks, and moving along with the natural flow that REST framework has.
The “parse” step happens in APIView.initial()
, which has pluggable components for:
Request parsing (for POST/PUT/PATCH)
Response formats / content negotiation
Version handling (bare)
Authentication
Permission checks
Throttling
The GenericAPIView
adds the following standard functionality to the view:
Serializer initialization
Filtering via pluggable backends
Pagination
Ofcourse, one can also subclass APIView
and do this manually.
Serializers
Where a view binds all request handling, the “serializer” defines what the layout of the input/output should look like. The serializer uses a composite design pattern for this.
Each item in the JSON dictionary is generated by a serializer Field
.
To generate special output, a new field subclass can be added with custom to_represention()
logic.
Serializer objects are also subclasses from Field
.
This allows to create nested object structures.
The serializer field just happens to generate a dictionary
instead of a single scalar in to_represention()
.
An array or listing happens by adding many=True
to the serializer initialization.
This little shortcut actually triggers behavior in Serializer.__new__()
to wrap the whole serializer into a ListSerializer
object.
The list serializer is also just a field. It just happens to generate a list in to_represention()
.
In the end, this whole “tree of field objects” walks through the data structure.
Each field has a source
(parsed into source_attrs
) that tells what model field
it should read. The whole structure cascades through relations and nestings by blindly reading
attributes that get_attribute()
and to_representation()
happen to do for each field subclass.
Viewsets
The “Viewset” logic builds on top of this to handle both the “listing” and “detail” view
in the same viewset class.
This project only offers a GET API, so it uses the ReadOnlyModelViewSet
.
They also offer a like a full GET/POST/PUT/DELETE API using ModelViewSet
.
Routers
The “router” is a special component that handles automatic URL creation, e.g. to create a separate listing and detail URL from a single “viewset”. This project overrides the router to implement the whole creation of all URLs.