Sitemap
CodeX

Everything connected with Tech & Code. Follow to join our 1M+ monthly readers

Wait, where’s my pineapple?

5 min readApr 10, 2021

--

Photo by engin akyurt on Unsplash

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.

Brief introduction

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:

https://gist.github.com/ewelina29/c025651c47948e46e630dbbf1d4e211b

So we have aPizza model with fields:

  • name ,
  • price (not important in this case),
  • ingredients(ManyToManyField to Ingredient, because every pizza can have many ingredients),
  • sauce (ForeignKey) to Sauce model 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.

https://gist.github.com/ewelina29/86f4492aa832fe65aa20c4aa6316aa16

And very basic PizzaSerializer inheriting from ModelSerializer :

https://gist.github.com/ewelina29/f4e2dfc0d5e664cd8088231fbca44e40

One more line of code in urls.py:

https://gist.github.com/ewelina29/3ba603e239e383d794c027f914435036

And quick look into our Ingredients and 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:

{
"name": "Hawaiian",
"price": 29.99,
"sauce": 1,
"ingredients": [1,2,5]
}

And send a GET request to fetch the list. Let’s take a look at the response:

Response of the GET request

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:

https://gist.github.com/ewelina29/ada20176e8230e519c8913e7598076fe

This way we can create a new serializer that will handle PizzaListAPIView:

https://gist.github.com/ewelina29/2d85154150efb213817472971689f233

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.

https://gist.github.com/ewelina29/810a907e956c77e9770971f3013674aa

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:

https://gist.github.com/ewelina29/bc1a789b965a72747d6f50f67d63da4c

I’ve added two extra fields: selected_sauce and 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 sauce and 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 read_onlyor 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…

https://gist.github.com/ewelina29/d4bf88c1215feb99286f1e369d50caa5

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:

https://gist.github.com/ewelina29/3c415961609a79fa185577a14e3b4fd6

And update urls.py:

https://gist.github.com/ewelina29/e9cb1c26cee1046710a9d68c2f026b2d

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 get_serializer_class() method.

Now the code looks much cleaner and every serializer is responsible only for one action.

Conclusion

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 ;).

--

--

CodeX
CodeX

Published in CodeX

Everything connected with Tech & Code. Follow to join our 1M+ monthly readers

Ewelina Bosko
Ewelina Bosko

Written by Ewelina Bosko

Passionate about programming and writing :). I work as a software developer, mostly in Django and ReactJS. You can reach me here: https://pogromcykodu.pl

No responses yet