Django under the hood: django REST framework - Tom Christie

Tags: django, djangocon

(One of the summaries of a talk at the 2014 django under the hood conference).

Tom Christie will give a two-part talk. The first is about django REST framework, the 3.0 beta will be out tomorrow. The second part is about API design in general.

REST framework 3.0

They made a few fundamental changes in 3.0. Why did they do this?

For instance for serializers. Serialization is from an object to a serialization format like json. It is is the deserialization that is the hard part: you have to do validation on incoming data and transform it if needed and convert it to an object. How it happened till now:

serializer = ExampleSerializer(data=request.DATA, ...)
serializer.is_valid()
# serializer.object exists now

This .is_valid() performs some validation on the serializer and it also instantiates an object. In 3.0, the validation still happens, but it only works on the serializer, not on an object. It returns validated data instead of an object. The validated data can afterwards be saved to an object explicitly.

Another change is that there’s a separate .create() and .update() method to split out the behaviour regarding creating new database objects or updating existing ones. The previous version determined itself whether a create or update was needed and it was often mixed in a wrong way.

This change was done because of relations. It is tricky to save a django User+Profile object when the user doesn’t exist yet. A profile needs a foreign key to a saved user… The code here did a dirty trick. A trick that you needed to do by hand to if you wanted to do something similar yourself.

You can look at this as an encapsulation problem (see Tom’s blog article). You can use the scheme of “fat models, thin views”. The idea is to say “never write to a model field or call .save() directly”. You instead always use model methods and manager methods for state changing operations. The advantage is that all your model-related code is encapsulated in one place.

Regarding the user/profile problem, you could write a custom model manager for users with a custom create() method which always creates User and Profile instances as a pair.

In REST framework 3.0, you can now use a similar method as you can now have a .create() model on the serializer that controls the actual creation process. You can still use the ModelSerializer shortcut, but your validation behaviour is made visible and explicit. “unique_together” and field validation are helped by this.

Note: I missed something here as my late-2010 macbook pro crashed again with the hardware GPU bug that apple refuses to fix.

Field validation in 2.0 can be hard to track, because you don’t know if something failed because of a serializer validation or because of a database validation. A max on an integer field will only fail when saving to the database: in 2.0 the error would come out of .is_valid(). In 3.0, the serializer validations will run OK and only the database save() will fail. Nice and explicit.

There are now more methods that you can override on the serializer, like to_internal_value(), to_representation().

Serializers are used as input for renderers. Simple ones like a JSON renderer, but also an elaborate one like the HTML renderer for browser forms. The serializer output is almost exactly like regular form data, except that there’s an extra data.serializer attribute, pointing back at the serializer so that the renderer can query it for extra information.

There’s a way to give extra hints to a renderer: add a style dictionary to your fields. The contents of the dict is intentionally left unspecified, you can do what you want. It is the renderer that will decide what to do with it. Probably you’ll only use this to customize the HTML renderer.

The big 3.0 picture:

  • Easier to override save behaviour.

  • More explicit validation.

  • Better model encapsulation.

  • Less complex internal implementation.

  • Supports both Form and API output.

  • Template based form rendering.

  • Nested forms and lists of forms.

In the 3.1 version, you’ll be able to use the forms as exposed by Django REST framework as almost-regular forms in your django site.

Generic API design thoughts

There are quite some hypermedia formats coming out. HAL, JSON-LD for instance. He doesn’t think they’re compelling yet.

There are hypermedia formats that are successes.

  • Like plain old RSS. A simple link of lists, at the core. It is domain specific. It is machine readable. There are many tools that can read it.

  • HTML is the closest we’ve come to a universal platform. It is document centric, though, not data centric. It is not intended for programmatic interaction.

Keep in your mind one of your websites. In your mind, peel off the javascript. Peel off the css next. Then you still have quite a lot of document-centric structure left!

Can’t you peel back the html, too? And be left with the actual data? So: less documented-oriented but data object oriented? Say, an abstract object interface? A Data Object Model? That way a mobile application and a regular html application can interact with the same content.

(Note: I don’t know if he really meant the Data Object Model to abbreviate to DOM, which is obviously already the DOM abbreviation as known from html…)

A document object might have some meta attributes like uri, title, description. And other attributes like lists, objects, strings, nested documents. A “link” in turn can have a uri, some fields, etc.

Could you build generic client application on top of such a thing?

A “document” could have a ‘board’ attribute with the current state of a chess board, for instance. If you work with such a document’s Data Object Model, you could call methods that modify the board (play_move('d2', 'd4')) directly…

Also imagine the level of functional testing you could do on your application. Especially as opposed to what you could do with bare html!

According to Tom, it is time to start addressing the fundamental issues of system communication. You’ve got to think big for this.

So: how do we design abstract object interfaces and describe the actions and transforms they expose?

Come work for us!
 
vanrees.org logo

Reinout van Rees

My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.

Weblog feeds

Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):