Wait, where’s my pineapple?
When you create simple CRUD API views in Django Rest Framework I guess that most of the time you create only two endpoints: one for list/create actions and the second one for retrieve/update/destroy actions.
Implementing only one view for multiple actions means having also only one serializer for them. Less code, more ‘DRY’, isn’t it? However, sometimes it’s not so good, because our API may require different formats of data in every action. So what to do if that happens to us?
There are many solutions and different approaches to that problem and I’ll try to present a few of them.
Let’s assume we have a pizza restaurant (who doesn’t love pizza?) and we need to implement an app in which our clients could make the orders.
The models look like the ones below:
So we have a
Pizza model with fields:
price(not important in this case),
Ingredient, because every pizza can have many ingredients),
Saucemodel because every pizza can have only one sauce).
Let’s implement a very simple
PizzaAPIView inheriting from
generics.ListCreateAPIView that would handle list/create actions.
And very basic
PizzaSerializer inheriting from
One more line of code in
And quick look into our
Sauces tables to check what’s available in our restaurant’s kitchen :).
Ok, let’s create the best pizza ever :).
Here’s how the JSON (for a POST request) should look:
And send a GET request to fetch the list. Let’s take a look at the response:
Hmm, everything works but…where is my pineapple?! I don’t want to see on our list ids of sauces and ingredients but their names.
How to do that with not so much effort and keeping the code clean?
Multiple views — multiple serializers
The first solution is to split our view into two separate ones:
This way we can create a new serializer that will handle
I have overridden the field
sauce to return the name of the sauce (using
source property) and added
depth = 1 to
Meta class to get as a result the full object.
There’s one thing left. We need to create a separate endpoint for every view.
Now our result will look like that:
Yeah, pizza with pineapple! That’s what I like :)
But this approach required creating an extra view and endpoint. Maybe there’s a better solution?
One view — one serializer
Why not give it a try and mix everything in one view and one serializer? Let’s get back to our basic
PizzaAPIView and modify
PizzaSerializer a little bit:
I’ve added two extra fields:
selected_ingredients to return the objects in our desired format. I also assigned them the
read_only property so that they are available only in the response of the GET method.
There’s one more thing. By assigning to
ingredients fields the
write_only property, I’m sure that these two fields will be required in the POST request data but they won’t be displayed in the response.
This approach allows us to handle two methods in one view and one serializer. But is it always the best solution? The answer depends on you.
If you ask for my opinion, I feel it’s quite messed up when there are different names for the same field in the list and create views. Also, the moment when I need to take a look at every field definition and check if it’s
write_onlymakes me feel overwhelmed with too much information gathered in one class what makes the code not clean enough.
That’s why I was looking for some other solutions and here’s what I found…
One view — multiple serializers
Let’s get back to our basic view and modify it a little bit. Django Rest Framework comes out with many methods that we can simply override to achieve our goals. (If you want to know more about them, check out here.
One of these methods is
get_serializer_class(). This method is being called when we send a request and Django is looking for the serializer class. By default, it returns the
serializer_class that is defined in the view. But if we override the method, the real magic happens…
What’s going on here? Just a simple checking if our request method is POST (it means that we are creating a new object). If yes, we tell Django to use
PizzaCreateSerializer. In any other case,
PizzaListSerializer will be used.
Then we can create our serializers:
Wow, that’s amazing! We’ve just used two serializers in one view. And we can go even further and use as much as we want just by implementing any logic we can imagine in the
Now the code looks much cleaner and every serializer is responsible only for one action.
As you can see almost every problem has many solutions and every solution can be right in some contexts, so it’s really helpful to know different approaches and use them alternatively depending on your needs. Don’t hesitate to experiment with your code — it’s the best way to discover something new and (hopefully) something better! :)
P.S. I hope you won’t hate me for that pineapple on pizza, but I truly love it ;).