In web development, we have a unfortunate double meaning for the word models and as obvious as the separation of those two seems to seasoned developers, it shows again and again that it’s not as obvious to beginners.
Anyone who took a class on object-oriented design in the last 20 years has inevitable heard about MVC. While there are recurring (and consistently fruitless) discussions about what exactly is what, what should be named how and how they should interact, the basic common understanding is more or less:
You separate your application into models, views and controllers (or models, templates and views). And ideally, you should be able to tamper with/replace either mostly independently of the other two.
This article concentrates on the models part which is the data the other components work with and display to the user. Ideally it should be a pleasant API that abstracts away the storage intricacies. However…
There Is More Than One Kind of Models
Django calls its ORM classes models. Pyramid calls the skeleton part for SQLAlchemy models.
I’m going to claim that that’s rather misleading since unless you’re building a simplistic CRUD application, your application models are not the same as ORM models but a composition of them. And if you call into your ORM from your views and controllers, nothing is abstracted from you: you’re still consciously accessing a database, you just have a more pleasant API to do so.
Therefore having ORM calls in your views1 is a code smell.
The easiest way out would be adding generic methods to your ORM classes but then you’re still just forcing the square peg of the data model (that may or may not have been constructed with your application in mind) into your round hole of application logic.
Instead, write your own models that offer the API that makes sense, that you need and ideally that you love. Then look how to implement it using the ORM models at hand. In other words: build a comfortable facade – why should your views care about your storage technology anyway?
Small update from 2019-Hynek: what 2013-Hynek was talking here about – but didn’t have the necessary lingo for – is nothing less than the repository pattern. One of the most useful design patterns ever imagined.
Isolation of global state. Global state is rightfully considered evil. Databases are the epitome of global state. Isolating access to – and modification of – global state from the rest of code is thus a good thing.
Better testability. This mostly stems from the previous point but it’s still worth reiterating: clear boundaries make testing easier.
A bare class is easier to test than a full-blown view. In true TDD spirit you start writing libraries which you wire up later. Most of those libraries won’t need database access, just pass along classes that act on concrete data. In those that do need database access, you’d ideally pass in the database connection/session on construction which frees you from patching altogether.
On the other hand it’s easier to test edge cases in your views with fake application models instead of patching the ORM/loading tons of fixtures.
Easier migration. If you decide to switch technologies (MangoDB!) or simply modify some access patterns (caching anyone?) you’ll find all the database logic in one place. No hunting for ORM calls throughout all of your views.
I find it a bit surprising that most introductory material on the mainstream web frameworks ignores this chasm. So I asked around and all were like “duh, of course they’re different”. And yet, most articles seem to assume that ORM models are application models.
I hope this article made you realize there’s more to it.
Post Scriptum: Why No Example Code?
Since I’ve published the article, people were consistently yelling at me to show some example code. I was hesitant to add code in the first place because I wanted to present a big picture and make people think for themselves. If I add code, people will start bikeshedding it and draw conclusions on the overall point by judging the example in question.
But the looks of your application models are a question of good API design which is hardly just a sub-point of an article. Also the big benefits emerge only when you have more complex data models which is hard to demonstrate in a simple example. So go out and learn about good APIs, here’s some inspiration:
- Architecture Patterns with Python (2019)
- API Design for Library Authors by Chris McDonough (important bit of wisdom @ 21:04) (2013).
- Good API Design by Alex Martelli (2011).
- How To Design A Good API and Why it Matters by Joshua Bloch (2007).
I.e. the part of your application that fills the templates with data, usually called
views.pyin Django and Pyramid. Not the templates. ↩︎