πŸ“œ ⬆️ ⬇️

Top 10 Mistakes Made by Django Developers


In this tutorial, we will look at the major mistakes of Django developers and find out how to avoid them. The article can be useful even for experienced developers, because they also make such mistakes as support for very heavy settings or name conflicts in static resources.


Django is a free, open source network Python framework that helps solve common problems in development. It allows you to create flexible, well-structured applications. In Django, there are already many modern features out of the box. For example, for me, features such as Admin, the Object Relational Mapping (ORM) tool, Routing and Templating, make Django the first candidate for choosing a development tool. Creating an application requires a lot of effort, and enjoying my work, like any developer, I want to spend as little time as possible on routine tasks. Django helps a lot with this without sacrificing the flexibility of the application.


Django's killer feature is a powerful configurable admin interface that is automatically (automagically?) Generated based on your model's scheme and admin models. You feel like a magician. Using the Admin interface, a user can configure many things, including an access control list (access control list, ACL), permissions and actions at the row level (row-level), filters, sort orders (orders), widgets, forms, additional URL helpers and more. I believe that the admin needs every application. It is only a matter of time when such a panel will be needed by your main application. In Django, it is created quickly and conveniently.


Also in Django there is a powerful ORM, working out of the box with all the major databases. It is "lazy": unlike other ORMs, it accesses the database only as needed. It has support for basic SQL statements (and functions) that you can use from your source Python code along with all the other features of the language.
Django has a very flexible and powerful templating engine. Many standard filters and tags are available, you can also create your own. Django supports other engines as its own templates, provides an API for easy integration with other engines through standard shortcut-functions for processing templates.


The framework has many other important features, such as a URL router, which parses incoming requests and generates new URLs based on the routing scheme. In general, Django is pleasant to work with, and when you need help, just read the documentation .


Mistake # 1. Using Python Global Environment for Project Dependencies.


Do not use the global Python environment for your project's dependencies, because this can lead to dependency conflicts. Python cannot work with multiple versions of packages at the same time. This will become a problem if different projects need different, incompatible versions of the same package.


Usually this error is made by newcomers to Python and Django development, who are not aware of the isolation features of the Python environment.


There are many ways to isolate the environment, the most common are:



Mistake number 2. No dependency binding in the requirements.txt file


Each new Python project must begin with a requirements.txt file and a new isolated environment. Usually you install all packages with pip/easy_install , without forgetting the requirements.txt . It is usually easier ( perhaps more correctly) to deploy projects on servers or on team members' machines.


It is also important in the requirements.txt file to bind (pin) specific versions of your dependencies. Typically, different versions of a package provide different modules, functions, and function parameters. Even in younger versions, dependency changes may be such that it breaks your package. This is a very serious problem if you have a live project and you plan to deploy it regularly, because without a version control system your build system will always install the latest available version of the package.


In production, always bind packages! I use a very good pip-tools tool for this. It provides a set of commands to help manage dependencies. The tool automatically generates requirements.txt , in which not just your dependencies are attached, but in general the entire tree, that is, the dependencies of your dependencies.


Sometimes you need to update some packages in the list of dependencies (for example, only the framework or utility). If you resort to pip freeze, then you do not know which dependencies are used by which packages, and therefore you cannot update them. The pip-tools tool automatically binds packages according to the dependencies you attach, and so it automatically decides which packages to upgrade. And thanks to the comments used in requirements.txt you always know which package came from which dependency.


If you are even more careful, you can backup the source files of your dependencies. Keep a copy in your file system, Git-folder, S3-folder, FTP, SFTP - anywhere, just to hand. There are situations when an exception to the list of a relatively small package breaks a large number of packages in npm . Pip allows you to download all the necessary dependencies as source files. Read more about this by running the pip help download command.


Mistake number 3. Using old-fashioned Python-functions instead of class representations (Class-based Views)


Sometimes it is advisable to use small Python functions in the application file views.py , especially for test or utilitarian views. But usually in applications you need to use class-based views (CBV).


CBVs are general-purpose views that provide abstract classes that implement common web development tasks. CBVs are created by professionals and cover most of the sought-after behaviors. They have a well-structured API, and CBV will give you the opportunity to enjoy all the benefits of OOP. Your code will be cleaner and more readable. Forget about the difficulties of using standard Django view functions for creating lists, CRUD operations, form processing, etc. You can simply extend the suitable CBV to your view and override functions or class properties that configure view behavior (usually the function returns a property, and you can add any logic that can turn your code into spaghetti, if instead of CBV you resort to presentation functions).


For example, you can use different mixins in your project that override the basic CBV behaviors: creating view contexts, checking at the row level (authorization), automating template paths based on application structures, integrating smart caching, and more.


I created the Django Template Names package, which standardizes the template names for your views based on the application name and the name of the view class. I use it every day and save a lot of time when choosing names. Just paste the mixin into your CBV - class Detail(TemplateNames, DetailView): - and it will start working! Of course, you can redefine my functions and add mobile responsive templates, other templates for user-agents or something else.


Mistake number 4. Writing "fat" (fat) representations and "thin" (skinny) models.


If you have application logic transferred from model to view, it means that the view contains code belonging to the model. That is, the presentation becomes "thick", and the model - "thin."


And you need to write "fat" models and "thin" representations.


Break logic by small methods in model. This will allow to use them repeatedly and from numerous sources (admin user interface, frontend user interface, API endpoints, numerous views). It takes only a few lines of code, and you don’t have to copy-paste a bunch of lines. The next time you write the functionality of sending a letter to the user, expand the model using the email function, and do not write logic in the controller.


This will make your code more convenient for unit testing, because you can test your email logic in one place, rather than doing it in each controller. Read more about this issue in Django Best Practices . The solution is simple: write β€œthick” models and β€œthin” representations. Start it in the next project or refactor the current one.


Mistake number 5. Huge, bulky settings file


Even in the new Django-project settings file there are a lot of these settings. And in real projects, the file grows to 700+ lines that are difficult to maintain, especially when development, production and staging environments need different configurations.


You can manually split the configuration file and create separate loaders, but I want to recommend a great, well-tested Python package Django Split Settings , of which I am a co-author.


The package provides two functions β€” optional and include , which support wildcards for paths and import your configuration files into the same context. Because of this, you can simply create configurations by declaring configuration records in previously uploaded files. The package does not affect the performance of Django and can be used in any projects.


Here is an example of a minimal configuration:


 from split_settings.tools import optional, include include( 'components/base.py', 'components/database.py', 'components/*.py', # the project different envs settings optional('envs/devel/*.py'), optional('envs/production/*.py'), optional('envs/staging/*.py'), # for any local settings optional('local_settings.py'), ) 

Mistake number 6. All-in-one application, poor application structure and incorrect allocation of resources.


Any Django project consists of several applications. In the terminology of Django, an application is a Python project containing at least __init__.py and models.py . In the latest versions of Django, models.py no longer needed, just __init__.py enough.


Django applications can contain Python modules that are specific to Django modules (views, URLs, models, admin panel, forms, template labels, etc.), static files, templates, database migrations, control commands, unit tests etc. You need to break your monolithic application into small reusable applications with simple logic.


You should be able to fully describe the purpose of the application in one or two short sentences. For example: "Allows the user to register and activate his account by mail."


It is recommended to name the project folder project and put the application in project/apps/ . Then put all application dependencies in your own subfolders.


Examples:



Always add application names as prefixes to subfolder names, because all static folders are merged into one. And if two or more applications have a js/core.js , then the last application in settings.INSTALLED_APPLICATIONS override all previous ones. Once I ran into such a bug in my project and spent about six hours debugging, until I realized that another developer redefined my static/admin/js/core.js , because the team members implemented the custom admin panel and gave their files same names.


Here is an example structure for a portal application containing many resources and Python modules.


 root@c5b96c395cfb:/test# tree project/apps/portal/ project/apps/portal/ β”œβ”€β”€ __init__.py β”œβ”€β”€ admin.py β”œβ”€β”€ apps.py β”œβ”€β”€ management β”‚ β”œβ”€β”€ __init__.py β”‚ └── commands β”‚ β”œβ”€β”€ __init__.py β”‚ └── update_portal_feeds.py β”œβ”€β”€ migrations β”‚ └── __init__.py β”œβ”€β”€ models.py β”œβ”€β”€ static β”‚ └── portal β”‚ β”œβ”€β”€ css β”‚ β”œβ”€β”€ img β”‚ └── js β”œβ”€β”€ templates β”‚ └── portal β”‚ └── index.html β”œβ”€β”€ templatetags β”‚ β”œβ”€β”€ __init__.py β”‚ └── portal.py β”œβ”€β”€ tests.py β”œβ”€β”€ urls.py └── views.py 11 directories, 14 files 

Thanks to this structure, you can at any time export the application to another Python package and use it again. You can even publish it in PyPi as an open source package or move it to another folder. You will get something like this project structure:


 root@c5b96c395cfb:/test# tree -L 3 . β”œβ”€β”€ deploy β”‚ β”œβ”€β”€ chef β”‚ └── docker β”‚ β”œβ”€β”€ devel β”‚ └── production β”œβ”€β”€ docs β”œβ”€β”€ logs β”œβ”€β”€ manage.py β”œβ”€β”€ media β”œβ”€β”€ project β”‚ β”œβ”€β”€ __init__.py β”‚ β”œβ”€β”€ apps β”‚ β”‚ β”œβ”€β”€ auth β”‚ β”‚ β”œβ”€β”€ blog β”‚ β”‚ β”œβ”€β”€ faq β”‚ β”‚ β”œβ”€β”€ pages β”‚ β”‚ β”œβ”€β”€ portal β”‚ β”‚ └── users β”‚ β”œβ”€β”€ conf β”‚ β”œβ”€β”€ settings.py β”‚ β”œβ”€β”€ static β”‚ β”œβ”€β”€ templates β”‚ β”œβ”€β”€ urls.py β”‚ └── wsgi.py └── static └── admin β”œβ”€β”€ css β”œβ”€β”€ fonts β”œβ”€β”€ img └── js 25 directories, 5 files 

Of course, the real project will be more difficult, but such a structure simplifies and makes many aspects more transparent.


Mistake number 7. STATICFILES_DIRS and STATIC_ROOT confuse newcomers to Django development


Static files are assets that do not change as the application is used. For example, JavaScript, CSS, images, fonts, etc. In Django, they β€œaccumulate” in a public directory during deployment.


In development mode β€” python manage.py runserver β€” Django searches for static files using the STATICFILES_FINDERS setting. By default, it tries to find the requested file in the folders listed in STATICFILES_DIRS . If it does not find it, it searches using django.contrib.staticfiles.finders.AppDirectoriesFinder , which checks the static folder of each application installed in the project. This scheme allows you to write reusable applications that come with their own static files.


In production, you distribute static data through a separate web server, for example nginx. It does not know anything about the structure of the Django project applications or about the folders in which your static files are distributed. Fortunately, Django provides us with the collect static management command - python manage.py collectstatic , which runs through STATICFILES_FINDERS and copies all static files from the static applications folders, as well as from the folders listed in STATICFILES_DIRS , to the one you specified. in the STATIC_ROOT directory. This allows you to resolve (resolution) resources in the form of static data using the same logic as the Django server in development mode, and collect all static files in one place for the web server.


Do not forget to run collectstatic in your production environment!


Mistake number 8. Use in production STATICFILES_STORAGE by default and Django template loaders


Let's talk about asset management of a production environment. We can provide the best UX if we use the β€œnever fail to expire” policy for assets (we can read more about it here ). This means that all our static files must be browsed for weeks, months, or even years. In other words, users only need to download resources once!


A cool idea, and it can be implemented in just a few lines in the nginx configuration for our folder with static files. But what about checking the relevance of the cache? If a user downloads our resource only once, what should you do if you update the logo, fonts, JavaScript or the text color in the menu? To solve this problem, you need to generate unique URLs and names for each static file every time you deploy!


To do this, you can use ManifestStaticFilesStorage as STATICFILES_STORAGE (be careful, hashing is enabled only in DEBUG=false mode) and issue the collectstatic command. This will lead to a decrease in the number of requests for resources from your production-site and make its drawing much faster.


Cool feature Django - cached template loader. It does not reload and parses template files each time it is drawn. Parsing a template is a very expensive operation, it requires a lot of computational resources. By default, Django templates parse with every request, which is bad, especially in production, where thousands of requests can be processed in a short period of time.


In the configuration section cached.Loader you can find a good example and details of the solution to the problem. Do not use the bootloader in development mode, because it does not reload the parsed patterns from the file system. You will need to restart your project using python manage.py startapp every time you change the template. This can be annoying during development, but ideal for a production environment.


Mistake number 9. Pure Python for utilities or scripts


Django has a great feature - management teams . Use them instead of inventing a bicycle in the form of writing scripts in pure Python for the utilities of your project.


Also pay attention to the Django Extensions package, which is a collection of custom extensions for Django. Perhaps someone has already implemented your team! There are many common target teams.


Mistake number 10. Cycling


There are thousands of ready-made solutions for Django and Python. Refer to the search engines before writing something that is not at all unique. Probably already have a solution.


No need to complicate. At first - we google! Install the quality package found, configure, extend and integrate it into your project. And if you can, contribute to open source.


Here is a list of my own Django packages to get you started:



Don't repeat yourself (DRY)!


I am a supporter of the DRY-concept, so I created Django skeleton - a handy tool with a number of nice features already out of the box:



This is a ready-to-use Django-skeleton for your next project, created from scratch. I hope it will save you a lot of time. Webpack has a minimal base configuration, but pre-configured .scss files are also configured using SASS to it.


')

Source: https://habr.com/ru/post/328352/


All Articles