Write better Django views

Posted on Fri 12 March 2021 in Better Django

image

Should you use class-based views (CBV's) or function-based views (FBV's) in Django? It seems a lot of people are pushing CBV's, touting them to be the "standard" way of writing views in Django. But why do FBV's still exist then? Just for backward-compatibility? This is my highly opinionated "view" (see what I did there??) on this matter.

The topic is hotly debated. Why are some people so passionate about CBV's? CBVs are said to be better because they abstract a lot of boiler-plate code into base classes and mixins. This is true. As a very basic example, instead of writing code to generate and return a rendered template, you can just use an existing base class for your view, and all you have to specify is the template name. And boom, Bob's your mother's cousin's uncle, twice removed. Seems like a no-brainer, right? We're saving at least 1-2 lines of code! (Yes, really)

But not all views are that simple. Soon you have CBVs that inherit from multiple base classes and mixins, you've overridden 7 methods of those classes, but your view still doesn't work. To figure out why, you read the documentation of all those classes, some of which have only their source code as documentation. You have 17 tabs open in your browser and it's a mess. Eventually you figure it out, and the problem was that the order in which those methods are run is different than what you expected. Is this all really worth it? Maybe you can figure these things out once, and then you just profit! But what about the next developer. They will read the code, and will have to go through the same process to figure out what is happening when and how. And if you don't touch that code again for the next months, will you remember these things next time?

I think you can see where I'm heading. One very important thing we're forgetting is the Zen of Python. In particular, I'm thinking of "Simple is better than complex", and "Explicit is better than implicit". CBVs are certainly not explicit. There's hidden stuff going on. A lot. And they are also not simple - there's a huge tree hierarchy behind the different base classes that Django provides. They hide tons of logic that is not obvious to the developer, and if you don't go and read that internal logic, you might have a hard time using those classes. But FBVs address these issues pretty well. They are explicit: you have one method that shows you all the logic, sequentially. And they are simple: there is no "API" that you need to know - no base classes that have complex, hidden logic. Ok, but what about the down-sides? What if you have many views with common logic? Then surely CBVs are the way to go? Yes and no. In a few cases I admit that CBVs could possibly, maybe, conceivably be beneficial. But for most cases FBVs will give you more advantages. And to reduce the repetition of common logic, there are alternative means.

I think the big issue is what we've been taught about abstraction and "DRY". Apart from solving a problem, those are the main things we do when coding. We actively look for things to abstract. And often (preaching to myself here mainly) we pull the trigger way too early. "Hey, this seems like a generic bit of code that we'll surely use again later!" is something I think to myself regularly. But at this point DRY is not even applicable yet, because we haven't repeated ourselves yet. But even if you find code in multiple places that is similar, we should be very careful to immediately create an abstraction. Instead, we should be thinking about how readable and maintainable that code would become as a result. And arguably, readability and maintainability will be more profitable to our employers than saving a few lines of code. Sometimes we should indeed abstract in order to reduce boiler-plate code, but when we do, we must do it veeeewy cawefuwy (my Elmer Fudd impression).

I get it. There's hardly a better feeling for developers than when you've written a piece of very clever code, and it just works. It might enable other developers (or your future self) to handle similar cases with just a line or two of code, and the thought of that appeals to us. But what we're forgetting is that often "similar" cases are just different enough that the abstraction needs to be tweaked ever so slightly. And this carries on, until the abstraction is nothing short of a Frankenstein. We've all been there, right? So why do we still go down that path?

Abstraction and DRY are at the heart of CBVs - and in most cases we save just a few lines of code. But it hurts the maintainability of the code in other ways. Future developers will spend hours figuring out the logic of the views, just so that there can be less code. Yes, fewer lines means less code to maintain. But often those fewer lines are harder to maintain because of the abstractions.

There. I've said it. You might not agree, especially if you've used CBV's for many years, and you're so used to them that they are not an issue for you at all. Often, when we're used to something, we don't see the down-sides anymore. So I hope this has helped in that sense.

Now, what I haven't done is SHOW you the differences between the two approaches, to prove the things I've claimed. But there's a resource that does a very, very excellent job at that, and you should go and read ALL of it, as soon as possible. It seriously blew my mind. My eternal thanks go to Luke Plant for spending hours upon hours to create this.

Django Views — The Right Way

For extra credit, go and read up about the pitfalls of abstractions. Abstractions have their place, but sometimes we think that place is "everywhere". At least that's how I code at times. Anyway, enough now. Go forth and code simply and explicitly. ;-)