”Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.” – (John F. Woods)
While working on projects using Django web framework, I was wondering each time if the solution for a problem could have been implemented in other ways. Thus implied reading a lot of official documentation, consulting forums and “googling”. I’ve learned for myself that those solutions I’ve found best to implement, could have been done in another way. As a scope of this article, I have decided to share my thoughts and opinions regarding best practices for Django web framework and what should be taken into account while using it.
Please note the fact that all the points listed below are common and they suggest best practices in succeeding with a Django project. Here we go!
Django is Python-based framework, so it will be obvious to say that PEP 8 (Python Enhancements Proposals) formatting and writing rules should be followed as a standard way of styling your code. It would be worth mentioning those basic rules like:
- Use 4 spaces per indentation level;
- Separate top-level functions and classes by 2 blank lines;
- Document classes and methods;
- Method definitions inside a class should be separated by one blank line.
There are also some other interesting rules related to PEP8 convention that you could check out. In addition, if you are a completely lazy person for which following a link means a time-consuming action, you can install a plugin in your preferred IDE that will simplify your life.
PEP 8 Line of code length
One more thing worth to be mentioned, related to PEP 8 conventions, is the maximum allowed line length that sticks to the rule of 80 characters per line. No wonder that every developer, at least one time in his life, has had to deal with a console where the max line length was restricted to 80 characters. So it could be considered as a part of safe length value that text-wrapping editors or developers can use without being afraid of losing clarity and understandability of code.
On the other hand, this rule from PEP 8 is flexible, it is allowed to extend the limit of 80 characters per line length to 100 characters and the reason such a provision has been decided is: “For code maintained exclusively or primarily by a team[…] it is okay to increase the nominal line length from 80 to 100 characters.”
Considering both mentioned facts, what exact rule would be the best one to choose? The answer isn’t obvious, because it still depends on the scope of the project being developed:
- In case of an open-source project, stick to the option of 80 characters per line. Contributors and visitors will be, most-probably, grumbling because of line length issue;
- On private projects, we can forget about that limit and extend it to 100 characters, since we can take advantage of bigger and modern monitors. At the moment of writing this article I checked out my console and the maximum line length it accepts was 160 characters.
Also, consider the fact that choosing the standard of 80 characters is not a reason of naming your variables, functions or classes in an ambiguous manner. Having readable and understandable names is much more important than fitting the code in environments used decades ago.
Good way of using imports
A nice way of starting to write Python code is to think of the required imports for the created classes. I have always wondered if there is an order that should be followed for them, and yes, again PEP 8 conventions proved this concept to be applicable as well. Therefore, here is the order for imports suggested by PEP 8:
- standard library imports;
- related third party imports;
- local application/library specific imports.
A blank line between each group of imports should be inserted as well.
Of course, analyzing different projects I saw a little of abstraction and deviation from that rule that’s related to Django projects type, so the order of grouping the imports described above can be adjusted to this specific framework, and the result might look as following:
This list of imports looks pretty good from the point of view of imports order and it would work as expected. But what if one day it would be decided to reuse or even change the name of the application, i.e. “warehouse” to something more relevant? Besides renaming your application, you would have to search through all your project files and rename your imports in order to avoid further issues.
To prevent this from happening you would probably use relative imports. With relative imports, you can adjust the imports from the 4th group (Fig.1) to something like that:
| from warehouse.models import Order
from warehouse.forms import OrderForm
| from .models import Order
from .forms import OrderForm
Note: Also, do not use the “import *” statement since it will load all the unnecessary Python libraries components in the current class, which might produce unpredictable results.
As a conclusion related to all these coding techniques please consider following a consistent and rigid coding style. Projects consisting of different styles and arrangements are much harder to maintain, slow in development and it increases the chances of developer’s mistakes.
Layout of a Django web project
Django by itself imposes a default layout for its projects, but would that structure be a good one to follow? Well, for newcomers to this particular framework, it wouldn’t be reasonable and meaningful every time.
What I’d rely on while starting a new project in Django, would be a structure consisting of 3 levels of hierarchy:
Now what would all these three layers stand for and what should they store:
- repository_root – the root repository of a Django project. Beside django_project_root it should contain all high-level components and files required for project deployment like: gitignore, requirements.txt, docs/ (directory) and README.rst file (file format for technical documentation part).
django-admin.py startprojectcommand should be run from repository_root folder.
- django_project_root – the element that will hold all the applications related to a Django project (all python files), configuration_root, static and media directories.
- configuration_root – the last root element that will keep track of all project settings related files, urls.py and it must be a valid Python package (containing __init__.py file).
So, in the end we would have a considerable transformation of the default Django project layout to something like:
With that structure, all the required modifications related to file location values should be performed in the settings.py files.
Managing properly project settings
Django project settings are obviously controlled and managed by the settings module, in which the developer might specify exactly the value of certain parameters or they will be handled according to their default value. Settings are loaded once the server starts, so no wonder that changing some parameters might be tricky to perform on a Production server.
Regarding settings, I have learned to following simple rules:
- Settings files should be version-control tracked (especially the ones related to the Production environment);
- Stick to DRY (Do not repeat yourself) concept. I will talk about it later in the article;
- Secret keys should be kept out of version-control.
For the DRY concept, I would suggest splitting the settings.py file (file auto-generated by Django web-framework at the project creation part) into multiple Python files that would inherit from base.py file except for the fact that it would consist of additional settings related to a specific server where your project would be deployed. So, normally we’d have the following files:
- base.py – All common parameters values of the project across all existing environments.
- dev.py – Local development specific settings including the debug mode, log level, and activation of developer tools like django-debug-toolbar.
- test.py – Settings for running specific tests.
- production.py – This is the settings file, where you should be careful when changing something, it is used by your live production server. This is the server that hosts the real live website and it contains production-level settings only.
Therefore, what settings file you would like to choose while running your Django project, depends on what you specify while starting it. Suppose that you would like to run the project locally to debug some stuff. You’d run the command:
python manage.py runserver –settings=config.settings.local
Also I’ve seen some projects under development within a team of 3 and more developers and they preferred to split the dev.py file, so that it could be used by different developers depending on their needs, like following:
Of course, all these custom local settings files were inheriting from dev.py file itself. The reason of this customization was because of their needs in simulating a specific problem related to settings module, or maybe they thought they’re too important, so they decided to have their own settings file.
As I mentioned at the beginning of this article, I had to deal with all these discussed points, I was trying to find the best answer and hopefully I did it. Following them would make your code and entire structure of the application look more cleaner and readable. I think that at least for some of you it might be interesting to take in consideration when you’ll start developing a Django project by yourself, and don’t forget, MAY THE FORCE BE WITH YOU!