Django project structure
This post talks about Django applications framework, not Django projects. The naming is difficult, sorry!
I’m writing this post after a while of trying out some of the ideas of structuring applications put forward by Octopus Energy.
What’s the point?
How many times have you created a Django application inside a project and thought it made perfect sense?
Is the answer “never”?
In my case I’ve never felt I’ve done it perfectly. I always realise it could be better later down the line. You don’t have energy or budget to change it then. It’s an already working code that does not need to be touched. Not a priority in any meaning of the word.
Django applications are a great way to be able to package an add-on.
Why subject yourself to building a project that contains multiple
applications that all end up being interdependent on each other and will not
ever be used in separation from this project? Like literally, all models will
end up depending on each other. You’ll have a
utils app that your project utilises too. You are living an illusion of
building independent applications that do not depend on each other. Independent
for what sake? It’s not like you will ever make them into separate packages.
And they probably won’t be independent anyway.
Most projects I build contain non-reusable applications. It makes absolutely no sense to divide the project into multiple folders the way that the “best practice” seems to dictate. Most projects I’ve worked on never really had a need to divide projects into artificially segregated applications other than a need for a ritual.
It looks like Engines in Rails
make so much more sense. Only use them when you need them, not as a default.
django-admin startapp command now and stop the madness.
Some people say that Django applications way is great because “all Django developers” know it. While it’s true, there’s more to programming than Django. While it seems a lot of people learn Django first and programming second, I don’t think it’s a valid excuse to use the applications framework as a default. I think everyone should make an assessment on a project basis.
I’d love if you could re-jig the application after you’ve deployed it, but the migration system makes it almost impossible to do later down the line. Once a Django model is added to an app, you really can’t move it without legwork.
suggests to do is to just don’t bother with the whole
applications thing. All the models should live in one application only - they
call the application
data. Then you categorise models into separate files
yourself. They’ll always be part of the
data application. I like this because
it means rather than opting into using applications without thinking, opt in
only when you actually need them.
That way you avoid having to manage migrations between multiple applications. You can move your models around into different files later on without having to perform a surgery on your database or manually edit migration files.
data ├── migrations │ ├── 0001_initial.py │ └── __init__.py └── models ├── comment.py ├── __init__.py ├── post.py └── user.py
Save yourself some time by not performing pointless rituals of deciding what model should live in which app. Keep it simple, stupid.
Have you ever asked yourself how do Rails or Laravel developers survive without rituals of slicing projects into “re-usable” or “pluggable” modules?
User interface and data interdependency
It feels so weird to section your project into applications that contain both data and UI logic when you think about it. Let’s say you have four interfaces:
- User interface for the front-end users
- User interface for the back-end users
- Command line commands executed by cron or developers
- API used by a third-party service
Django applications revolve around models. Then you are asked to stick all the GUI & API views, and command line interface into the app that utilise the models used in this app. Have you ever done it perfectly? I never have. It never scratches an itch for me.
What if two different user interfaces that revolve around the same model utilise
forms, would you mix them in the
forms.py file? Would you mix the views for
different kind of interfaces in the
If your view uses models from multiple application and it’s not clear which application it should live in, how do you decide that? Or do you just create yet another application just for those views to live in?
Octopus Energy conventions suggest you break up with the Django applications approach and segregate your interface (views, forms, serialisers, templates) into folders by the actual interface.
You end up with something like:
interfaces ├── api │ ├── __init__.py │ ├── serializers │ │ ├── comments.py │ │ ├── __init__.py │ │ ├── posts.py │ │ └── users.py │ ├── urls │ │ ├── comments.py │ │ ├── __init__.py │ │ ├── posts.py │ │ └── users.py │ └── views │ ├── comments.py │ ├── __init__.py │ ├── posts.py │ └── users.py ├── cli │ └── management │ ├── commands │ │ ├── __init__.py │ │ └── update_comments_count.py │ └── __init__.py ├── dashboard │ ├── forms.py │ ├── __init__.py │ ├── urls.py │ └── views.py └── website ├── forms.py ├── __init__.py ├── urls.py └── views.py
Business logic - does it belong on views or models?
Answer is - neither.
Models are data - they are dumb. They just store a collection of primitive values and give you the ability to access or mutate them. Usually trying to do more on models turns projects into chaos.
Views are the user interface. Nothing to do with actually changing the state of model instances or deciding when to send an email to someone, right? Views just deal with the displaying UI and validating user inputs, and triggering the business logic. They should not actually contain that business logic.
It’s definitely a problem faced by a lot of developers - do you put X on a model or a view? Do you keep your models or views thin? How do you decide which side of the relationship contains the method to perform a certain operation? How do you test business logic encapsulated in an interface without turning tests into bloat? It does not ever feel right whichever way you do it.
My answer would be to quit this demagogy and create a specific business logic layer.
Mitchel Cabuloy with whom I work at Torchbox put forward an idea for something called “service objects” which take care of exporting the business logic onto another layer. It’s worth checking out!