After working with Django for more than 4 years both professionally and personally, I have learned a lot of tips and tricks that I wish I knew when I started. In this article I will share some of the most important ones that I have learned over the years.

Since Django documentation is very good, I will not share many code snippets, but rather explain the concepts and provide links to the official documentation. All of these tips and tricks came from my own experience, the Django documentation, and Two Scoops of Django book.

Automate with Management Commands

If you use python manage.py runserver or python manage.py migrate, you’re already working with Django’s management commands! They’re a fantastic way to automate routine tasks, making them ideal for various actions, like sending emails, updating databases, or running scripts. Creating custom management commands lets you add commands tailored to your project’s unique needs.

Some common custom management commands I use include:

  • Cleaning up old database entries that are no longer needed
  • Sending automated reminders or daily reports
  • Generating and sending monthly performance reports to the marketing team

Custom commands can be scheduled with UNIX cron jobs or Windows Task Scheduler to run at specific times. For even more complex requirements, consider using Celery to manage periodic tasks asynchronously, allowing for retries and advanced scheduling options.

➡️ Writing custom django-admin commands

Organize URLs by App Namespaces

For large projects with multiple apps, organizing URLs by namespaces keeps things clean and reduces the risk of route conflicts. In Django, namespaces group related URLs, making it easier to navigate and reference them across templates and views.

For example, if you have blog and shop apps, adding an app_name in each app’s urls.py file lets you refer to each URL unambiguously:

# blog/urls.py
app_name = 'blog'
urlpatterns = [
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
]

# shop/urls.py
app_name = 'shop'
urlpatterns = [
    path('product/<int:pk>/', views.product_detail, name='product_detail'),
]

Using these namespaces in templates becomes straightforward and clear, avoiding conflicts with similarly named routes:

<a href="{% url 'blog:post_detail' pk=post.pk %}">Read more</a>

This structure not only improves readability but also allows you to reuse route names across different apps, making it easier to manage your growing codebase.

➡️ URL namespaces and included URLconfs

Cookiecutter for Clean Scaffolding

When I first started learning Django, I was overwhelmed by the amount of boilerplate code that I had to write for every new project and to be frank a lot of Django projects share the same structure, with Cookiecutter, you can generate a fully structured project template, complete with best practices and optimized settings, in seconds.

The Django Cookiecutter template, maintained by the Two Scoops of Django authors, is particularly helpful, as it includes everything you need for a modern Django project: optimized settings for security, testing, and deployment.

Beginners can use it to learn best practices, while advanced users can customize it with tools like Celery for task management, Docker for containerization, and other services.

Exploring the generated code is also a valuable learning experience, as it includes documentation references to explain each choice, providing further insight into Django’s architecture.

➡️ Django Cookiecutter

Keep the Frontend Lean with Vanilla HTML, Template Tags, and HTMX

Many developers lean toward single-page applications (SPAs) for dynamic interactivity, but Django’s templating engine is powerful enough to keep the frontend lean and efficient. Django’s templates, combined with template tags and filters, allow for extensive customization and interact seamlessly with Django’s routing system.

When you need just a bit of interactivity, HTMX can add dynamic, SPA-like features to your HTML without requiring a heavy JavaScript framework. HTMX lets you load content or submit forms asynchronously with ease, so you get smooth, modern functionality without compromising simplicity or loading times.

I go into more detail about the benefits of HTMX in comparison with frameworks like React and Vue here.

Set Up Sentry for Free Error Tracking

Logging goes beyond just tracking errors—it provides insight into how users interact with your application and helps identify performance bottlenecks. For production environments, I recommend using Sentry, which tracks errors with detailed stack traces, request data, and user insights, helping you troubleshoot more effectively.

Setting up Sentry is easy. After configuring it, Sentry will notify you via email whenever an error occurs and log the details in the Sentry dashboard. Here’s a basic setup:

import sentry_sdk

sentry_sdk.init(
    dsn="https://**************.ingest.de.sentry.io/*****",
    traces_sample_rate=1.0,
    profiles_sample_rate=1.0,
)

While the free plan is limited, it’s more than enough to understand the requests that are failing in a nice format and get notified when something goes wrong.

I have been using Sentry for 5 years and most of the time the free plan was enough for my needs, if you need advanced features like performance monitoring or release tracking, there are paid plans with more options.

➡️ Sentry for Django

Think Twice with Class-Based Views

When I was introduced to class based views, I was amazed by the amount of code that I could save by using them, it was shorter, cleaner and more readable. However, as I started to work on more complex projects, I realized that class-based views can be a double-edged sword, This is a really hot topic in the Django community.

Class-based views (CBVs) are great for reducing code duplication and making CRUD operations more readable. However, for unique or highly customized behavior, CBVs can become unwieldy, especially when you’re dealing with deeply nested inheritance or unexpected “magic” behavior.

For simpler projects, CBVs can improve development, especially for lists, detail views, or forms. However, for more complex use cases, consider using function-based views (FBVs) instead. FBVs allow for more straightforward logic without the overhead of inheritance, and they can often be easier to debug.

In practice:

  • Use CBVs when you need to follow a reusable, standardized pattern (CRUD).
  • Use FBVs for views requiring specific, tailored functionality.

➡️ Django Class-based views

Use Django Caching for a Performance Boost

Django’s built-in caching can reduce load times by serving content directly from cache rather than querying the database each time.

Caching should be done after you have optimized your database queries and views, however you can still benefit from caching even if you have a small website, one package that I really like using is django-cachalot.

Django Cachalot caches Django ORM queries, so repeated queries for the same data are served from the cache instead of the database. It supports automatic invalidation—when data is updated in the database, the relevant cache entries are also updated, so you don’t have to worry about stale data.

For this to work you will need to install a cache backend such as Redis, with Digital Ocean you can have a decent Redis instance for $15 a month which is more than enough for most websites.

➡️ Django Cachalot | Django’s cache framework

Use Django Shell Plus for Quick Debugging and Testing

What if I told you there’s a better way to interact with your database than using the Django admin panel or scattering print statements in your views?

Django comes with a built-in shell that lets you interact with your project directly from the command line. You can use it to test models, run queries, or debug code without having to start the server or print debug messages in views.

However, the default Django shell has its limitations, and this is where Django Shell Plus comes in.

The django-extensions package includes a variety of tools for Django development, and one of the most powerful is shell_plus. Shell Plus is an enhanced version of Django’s default shell command, automatically loading all your project’s models and settings. It even supports IPython and Jupyter Notebooks, giving you a richer interactive experience.

For developers who regularly work with Django’s ORM and want a more efficient way to interact with the database, shell_plus is a game-changer.

➡️ Django Shell | Django Extensions