avatarBuilescu Daniel

Summary

The author shares advanced Django tips and tricks learned from their experience working at Google in Prague, covering topics such as project setup, ORM tricks, middleware, lesser-known gems, security, frontend integration, performance optimization, and testing.

Abstract

The author, who worked at Google in Prague, shares their experiences and advanced Django tips they learned while working on large-scale projects. They cover various topics, including project setup best practices, advanced ORM tricks, middleware creation and order, lesser-known Django features, security enhancements, frontend integration with React/Vue and WebSocket, performance optimization strategies, and automated testing techniques. The author aims to help Django developers improve their skills and build more efficient and secure applications.

Opinions

  • The author emphasizes the importance of proper project setup, including file organization, environment management, and configuration handling.
  • They highlight the benefits of using advanced ORM techniques, such as optimized queries, aggregation, and custom managers, to improve database querying efficiency.
  • The author stresses the significance of middleware in Django and provides guidance on creating custom middleware and maintaining middleware order.
  • They introduce lesser-known Django features, such as database routers and object-level permissions, to help developers build more flexible and secure applications.
  • The author discusses security best practices, including advanced CSRF and XSS protections, and object-level permissions to safeguard applications from potential threats.
  • They explore frontend integration techniques, such as using Django with React/Vue and WebSocket, to create modern and responsive web applications.
  • The author shares performance optimization strategies, including database indexing, query optimization, caching, and parallel test execution, to help developers build faster and more efficient applications.
  • They advocate for automated testing and provide insights into effective mocking, patching, and parallel test execution techniques to ensure code quality and reliability.

Advanced Django Tips I Wish I Knew Earlier (Psst… Got ’Em from Google!)

Unpacking my Google adventure with Django! Discover tips that reshaped my coding journey. Come, let’s explore together!

Photo by Vlada Karpovich: https://www.pexels.com/photo/woman-taking-notes-while-working-with-a-laptop-4050297/

It’s funny how some of the most pivotal moments in our lives can unfold in the unlikeliest of places. For me, it wasn’t the tech hubs of Silicon Valley or the bustling streets of New York, but rather the historic charm of Prague where my Django journey began in earnest.

In the bustling tech environment of Google, situated in the historically rich city of Prague, I had the unique privilege of diving deep into the Django Rest Framework. While Django, with its “batteries-included” approach, had always been a cornerstone in web development, it was the Django Rest Framework where my focus predominantly lay. I had the opportunity to work extensively on certain functionalities — some you might recognize, but which I’m bound not to detail due to confidentiality.

This guide isn’t just a recollection, but a curated compilation of the nuances, the hacks, and the advanced methodologies I absorbed during my time there. It’s more than a ‘how-to’; it’s a ‘why this way’. These aren’t just tips — they are tales from the trenches, insights from large-scale real-world applications.

Whether you’re looking to master the Django Rest Framework or seeking to hone your skills with core Django (which I also engaged with, albeit to a lesser degree), I’ve distilled my experiences into actionable advice just for you. And while I can’t explicitly detail some of the big-name projects I touched, believe me when I say: the strategies here have been battle-tested in arenas you’d recognize.

So, as we Czechs say, “Pojďme na to!” (Let’s get to it!). Dive in and let’s unravel the Django magic together.

Don’t forget to give me a follow on medium, it helps me keep going ❤️

For those who wish to stay updated on my latest posts or join a community of like-minded developers, consider joining my Discord group.

It’s a great place to learn, share knowledge, and grow together as we delve deeper into the fascinating world of programming.

Django Project Setup Like a Pro

Ah, the thrill of starting a new Django project! It’s like setting off on a new adventure, every single time. But as with any journey, the preparation often determines the success of the trip. Let’s set up our Django project in a way that’s sustainable, scalable, and learned from the best — drawing from the large-scale projects at Google.

File Organization: Best Practices from Google’s Vaults

Django’s default project structure is simple, and for a good reason: it provides a generic setup that fits many use cases. But when we talk about projects that scale, like the ones at Google, you need a tad more organization:

  • Apps Folder: Create a dedicated apps folder where all your Django apps reside. This keeps your project neat and allows for better separation of concerns.
myproject/
├── apps/
│   ├── users/
│   ├── products/
│   └── ...
├── manage.py
└── ...
  • Core App: A common practice is to have a ‘core’ app for main models or utilities that are shared across different apps.
  • Configurations: Split settings into different files: base.py, dev.py, prod.py. Use the base.py for common settings and extend/override them in environment-specific files.
  • Templates and Static: Have a dedicated top-level directory for global templates and static files, making them easier to manage.

Environment Management: No More “But it works on my machine!”

Pipenv: This is a marriage between pip and virtualenv. It provides an isolated environment for your Django project.

Installation is straightforward:

pip install pipenv

For a new project, run:

pipenv --three  # Use Python 3
pipenv install django

Activate the environment with:

pipenv shell

Docker: For even more consistent environments, especially when collaborating with teams, Docker is a godsend.

Here’s a basic Dockerfile for Django:

FROM python:3.8

ENV PYTHONUNBUFFERED 1

WORKDIR /app
COPY requirements.txt /app/
RUN pip install --upgrade pip && pip install -r requirements.txt

COPY . /app/

And your docker-compose.yml:

version: '3'

services:
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/app
    ports:
      - "8000:8000"

Now, you can easily run: docker-compose up.

Configurations: Secrets from the Pros

Managing configurations can be tricky. Here’s a robust approach:

  • Environment Variables: Use environment variables for sensitive info like SECRET_KEY, API keys, and database credentials. This keeps them out of your codebase.
  • Django-environ: A helper tool for environment variables, making it easier to manage.

Install:

pip install django-environ

Usage in settings.py:

import environ

env = environ.Env()
SECRET_KEY = env('SECRET_KEY')
  • Multiple Settings Files: As mentioned earlier, split your settings for different environments. Use the DJANGO_SETTINGS_MODULE environment variable to switch between settings seamlessly.

Remember, the structure and tools are merely the foundation. As we dive deeper into Django, we’ll discover the magic that turns this foundation into a sprawling, efficient digital empire. So let’s march forward with these pro setup tips under our belt!

Advanced ORM Tricks

While walking through the cobblestone streets of Prague, one can’t help but admire the intricate details of its historical buildings. It’s not just the grandeur but the tiny details that make it all come alive. Similarly, with Django’s ORM, it’s the advanced tricks that can truly elevate your database querying game. So, let’s dive deep into the art of efficient querying.

Optimized Queries: Precision is Key

No one likes wastage, especially when it comes to database queries. Every unnecessary column or row fetched can weigh down your app. Here’s how to ensure precision:

  • Select Only What You Need: Use only() and defer() to control the fields you retrieve.
# Fetches only the name and age fields
User.objects.only('name', 'age')

# Fetches all fields except the profile_picture
User.objects.defer('profile_picture')
  • Use select_related and prefetch_related: These help in reducing the number of queries, especially with ForeignKey and ManyToMany relationships.
# Reduces queries when accessing user.profile
User.objects.select_related('profile').all()

# Efficiently fetches books for authors
Author.objects.prefetch_related('books').all()
  • Avoid the N+1 Problem: Be cautious when looping through querysets and accessing related fields. Using the above methods can help sidestep this common pitfall.

Aggregation and Annotation: Summing Up and More

  • Basic Aggregations: Use Sum, Avg, Count, Max, and Min for quick calculations.
from django.db.models import Avg, Max

# Find the average rating of all books
Book.objects.aggregate(Avg('rating'))

# Get the highest price of all products
Product.objects.aggregate(Max('price'))
  • Annotations: Add computed fields on the fly. Useful for adding extra info without altering the model.
from django.db.models import F

# Annotate books with half price for a sale
Book.objects.annotate(sale_price=F('price') / 2)
  • Complex Aggregations: Dive deeper with FilteredRelation and conditional expressions for tailored aggregations based on certain conditions.

Custom Managers and QuerySets: Tailor-made Efficiency

  • Custom Managers: These allow you to add extra methods to your models to make querying more intuitive.
class PublishedBookManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(status='published')

class Book(models.Model):
    ...
    objects = models.Manager()  # Default manager
    published = PublishedBookManager()  # Custom manager

# Now, you can do:
Book.published.all()
  • Custom QuerySets: Extend the base QuerySet class to add your own methods. Even better, combine it with custom managers.
class BookQuerySet(models.QuerySet):
    def published(self):
        return self.filter(status='published')

class Book(models.Model):
    ...
    objects = BookQuerySet.as_manager()

# Usage:
Book.objects.published()

Understanding and mastering the ORM is akin to learning the beautiful nuances of a classical composition. It might seem daunting at first, but with each trick and technique, the melody becomes clearer. With these advanced ORM techniques, you’re not just querying; you’re orchestrating a symphony of efficient data retrieval. Onward, maestro!

Middleware Magic

Imagine standing in the heart of Prague, at the crossroads of multiple alleys. Each alley has its unique charm, its rhythm, and its footfall. Now, think of Django’s middleware as these alleys, playing a crucial role in how requests and responses pass and are processed. Let’s unlock the magic that lies within middleware and discover the intricate details that many might overlook.

Creating Custom Middleware: Crafting Your Alleyway

Middlewares in Django are fantastic. They allow you to process requests and responses globally before reaching the view or after leaving it. Here’s how to create one:

  • Decide the Use Case: Let’s say, for instance, we want a middleware that tags each request with the user’s preferred language (based on a cookie or a header).
  • Start Simple: Create a new file named middleware.py in your app's directory. Begin by defining the structure:
class LanguageMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Our middleware logic will go here

        response = self.get_response(request)
        return response
  • Add Logic: Now, fill in the logic. Let’s extract the preferred language from a cookie:
def __call__(self, request):
    # Default to English if no cookie is set
    preferred_language = request.COOKIES.get('preferred_language', 'en')
    request.preferred_language = preferred_language

    response = self.get_response(request)
    return response
  • Register the Middleware: Add your middleware to the MIDDLEWARE setting in your settings.py:
MIDDLEWARE = [
    ...
    'your_app_name.middleware.LanguageMiddleware',
    ...
]
  • Test It Out: In your views, you can now access request.preferred_language to get the user's preferred language.

Order Importance: Sequencing the Alleys

Middleware order in Django is not just a list; it’s a well-orchestrated dance of requests and responses. Here’s why:

  • Top-to-Bottom, Then Bottom-to-Top: When a request comes in, Django processes middleware from the top of the MIDDLEWARE list to the bottom. After the view processes the request, the response goes through middleware from bottom to top.
  • Why Order Matters: If middleware A modifies the request and middleware B, later in the sequence, reads that modification, A must come before B. Similarly, if you want middleware C to modify a response after middleware D has processed it, C should be listed after D.
  • Google’s Approach: Large projects, like those at Google, meticulously maintain middleware order to ensure efficient and error-free request-response cycles. Google projects prioritize authentication and logging middlewares at the top. They also maintain a centralized document detailing the purpose of each middleware, ensuring that developers are aware of the logic and sequence.

Tips for Maintaining Order:

  • Document: Always document the purpose and behavior of custom middlewares.
  • Be Explicit: When adding new middleware, understand its relation to existing ones.
  • Review: Periodically review the middleware sequence, especially after adding or removing some, to ensure efficiency.

Middleware in Django can sometimes feel like the unsung hero of the framework, silently ensuring that requests and responses dance harmoniously together. With a keen understanding of their creation and order, you’re well on your way to mastering yet another facet of Django, adding another layer to your expertise. So, let’s continue threading these alleys, discovering more magic with each turn!

Django’s Lesser-Known Gems

Just as Prague boasts hidden courtyards and secret passages, Django too shelters a myriad of hidden treasures within its vast framework. While most developers are acquainted with its more prominent features, it’s these lesser-known tools and mechanisms that can significantly elevate your Django game. Let’s embark on this exciting exploration together.

Database Routers: A Smooth Guide through Multiple Databases

When one database isn’t enough, Django’s database routers come to the rescue, guiding queries to their correct destinations.

  • Setting Up Multiple Databases: In your settings.py, you can define multiple databases:
DATABASES = {
    'default': {
        ...
    },
    'archive': {
        ...
    }
}
  • Creating a Router: Define a class to route database operations. For instance, to route all operations on an ArchivedModel to the 'archive' database:
class ArchiveRouter:
    def db_for_read(self, model, **hints):
        if model == ArchivedModel:
            return 'archive'
        return None

    def db_for_write(self, model, **hints):
        return self.db_for_read(model, **hints)
  • Register the Router: In settings.py, add your router:
DATABASE_ROUTERS = ['path.to.ArchiveRouter']

ContentTypes Framework: For Dynamic Relations and More

Django’s ContentTypes framework lets models relate to any model in the system, providing a more dynamic approach to data relations.

  • Understanding GenericForeignKey: Unlike regular ForeignKeys, a GenericForeignKey can link to any model.
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class Comment(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()
  • Usage: With this setup, a Comment can be related to any model – be it Article, Image, or even User.

Signals vs. Overriding the Save Method: The Delicate Balance

Django’s signals and the save() method offer ways to add custom logic around database operations. But which one should you use?

Signals:

  • Pros: Decoupled, clean, and reusable. Great for operations that need to span multiple apps or models.
  • Cons: Can become confusing if overused or not documented.
  • Example:
from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=MyModel)
def my_callback(sender, instance, **kwargs):
    ...

Overriding the Save Method:

  • Pros: Direct, clear, and confined to a specific model. Ideal for model-specific operations.
  • Cons: Can become cumbersome for extensive operations.
  • Example:
class MyModel(models.Model):
    ...
    def save(self, *args, **kwargs):
        # Custom logic here
        super().save(*args, **kwargs)

Which to Choose?:

  • For model-specific, small tweaks, go with overriding the save() method.
  • For larger, more global operations, or ones that involve multiple models, consider using signals.

Diving into these tucked-away treasures of Django is akin to discovering the hidden passages of a grand castle. While they may not always be in the limelight, their value is undeniable. Whether you’re building a small project or a vast, sprawling application, understanding these tools can give you that extra edge. Armed with this knowledge, let’s continue our journey, eager to unearth more of Django’s secrets!

Security Like You’ve Never Seen

Security in the digital realm is akin to a fortress in the physical world. You wouldn’t leave the gates of a castle unguarded, and similarly, you can’t afford to have vulnerabilities in your applications. Django offers a robust set of tools to fortify your web castle, and while you might be familiar with the basics, it’s time to take you on a deeper dive into its advanced features.

Advanced CSRF and XSS Protections: The Invisible Shield

Cross-Site Request Forgery (CSRF) and Cross-Site Scripting (XSS) are two of the most potent threats facing web applications today. Thankfully, Django has built-in mechanisms to defend against these.

Deepening CSRF Protections:

  • Middleware and Decorators: Always ensure CsrfViewMiddleware is enabled. To enforce CSRF protection on specific views, use the @csrf_protect decorator.
  • Custom Handlers: For applications requiring unique behaviors, create custom CSRF failure handlers.
  • Rotating Tokens: Regularly rotate CSRF tokens to minimize the window of vulnerability.

XSS Protections:

  • Auto-escape: Django templates automatically escape content, which prevents most XSS attacks. Ensure you’re not inadvertently marking content as safe using the |safe filter unless you're certain it's not malicious.
  • User-Generated Content: When dealing with user-generated content, always sanitize input and use libraries like bleach to clean the content.

Permissions and Object-level Permissions: Customized Access for All

While Django provides a built-in system for model-level permissions, there are times when this isn’t granular enough. That’s when object-level permissions come into play.

Understanding Default Permissions:

  • Django automatically creates add, change, and delete permissions for every model. For custom permissions, update your model's Meta class:
class MyModel(models.Model):
    ...
    class Meta:
        permissions = [("can_view_secret_data", "Can view secret data")]

Object-Level Permissions with django-guardian:

  • Install the library using pip install django-guardian.
  • Add 'guardian' to your INSTALLED_APPS and AUTHENTICATION_BACKENDS.
  • To assign a permission to a user for a specific object:
from guardian.shortcuts import assign_perm

assign_perm('view_article', user, article_instance)
  • Check permissions with:
user.has_perm('view_article', article_instance)

Best Practices:

  • Least Privilege Principle: Always assign the minimum required permissions to users and roles.
  • Regular Audits: Periodically review and prune permissions, ensuring that only active and necessary permissions are retained.

As we wrap up this section, it’s worth reflecting on the importance of security. It’s not just about tools or techniques but cultivating a mindset where security is paramount. The advanced features Django provides are top-notch, but they’re most effective when combined with vigilant practices and continuous learning. Like a seasoned knight, equip yourself with the best armor Django offers and stand confidently, guarding your digital kingdom.

Making Django Play Nicely with Frontend

In today’s vibrant web ecosystem, Django’s strength as a powerful backend tool is undisputed. However, the modern web isn’t just about what happens on the server. The intricate dance between backend and frontend is where the magic happens, and for that, we need to ensure Django waltzes smoothly with frontend frameworks. Let’s delve into making this partnership harmonious.

Django with React/Vue: Harmonizing Old Melodies with New Rhythms

The worlds of Django and frameworks like React or Vue might seem disparate, but with the right steps, they can work in perfect harmony.

Setting the Stage:

  • Begin with a standard Django setup, ensuring you have a dedicated app for your API (e.g., api).
  • For the frontend, initialize a React or Vue project within your Django project directory, e.g., frontend.

API with Django Rest Framework:

  • Use the Django Rest Framework (DRF) to create API endpoints, ensuring they return JSON data which React or Vue can consume.
  • Protect your API routes with authentication, leveraging DRF’s token-based authentication for seamless integration with frontend frameworks.

Integrating Frontend Build Process:

  • In your frontend’s build configuration (Webpack, for example), set the output directory to Django’s static folder.
  • This allows Django to serve the built frontend files directly.

CORS Handling:

  • Install django-cors-headers to handle Cross-Origin Resource Sharing (CORS).
  • Configure the middleware and set CORS_ORIGIN_WHITELIST in your Django settings.

Communication:

  • In your React or Vue components, make AJAX calls to your Django API endpoints, using libraries like Axios.
  • Handle data in the frontend, leveraging the reactive capabilities of these frameworks.

WebSocket with Django: Broadcasting the Modern Beats

Traditional HTTP requests are a tad old-school when you want real-time capabilities. Enter WebSockets and Django Channels.

Installation and Setup:

  • Install Django Channels and Channels Redis: pip install channels channels-redis.
  • In settings.py, configure ASGI application and the Channels layer with Redis as a backend.

Defining Consumers:

  • Create a consumers.py in your app directory.
  • Define a consumer, which is a class that handles WebSocket connections, similar to how views handle HTTP requests.

Routing WebSockets:

  • Create a routing.py in your app, defining WebSocket routes pointing to your consumers.
  • Update the project’s main routing to include these WebSocket routes.

Interacting from the Frontend:

  • Use WebSocket APIs in the browser or libraries like socket.io in React or Vue to establish a WebSocket connection to Django.
  • Implement real-time features, such as notifications, live chat, or real-time updates.

Juxtaposing Django with modern frontend tech might initially seem like mixing oil with water. But with a methodical approach, it’s more like crafting a symphony where each instrument plays its part, coming together to create a magnificent opus. As we continue our exploration, always remember that it’s not just about individual excellence but how these technologies harmonize to create a delightful user experience.

Performance Optimization Secrets

The backbone of any robust application lies not just in its features, but also in its performance. Serving data is an art, and when you’re managing volumes akin to what one would experience at a tech giant like Google, it’s paramount to ensure every millisecond counts. Let’s unwrap some of the optimization strategies I picked up while navigating through colossal data mountains.

Database Indexing and Query Optimization: A Dance with Data

Having vast amounts of data is great, but accessing it efficiently is where the challenge often lies. Here’s how you can make your database perform at its peak.

Understanding the Need for Indexes:

  • Databases scan tables row by row to find the desired data. With indexes, this search becomes faster, like using a bookmark in a lengthy novel.

Creating Indexes in Django:

  • Use the db_index=True attribute in your model fields that are frequently queried.
  • For complex queries, consider using Meta class's indexes option to define composite indexes.

Query Analysis:

  • Use Django’s QuerySet.explain() method to understand how your queries are executed.
  • Based on the analysis, adjust your queries or add necessary indexes to eliminate performance bottlenecks.

Prefetching and Select Related:

  • Use select_related for ForeignKey and OneToOneField relations when you know you'll be accessing related objects.
  • For other relations like ManyToManyField, use prefetch_related to minimize database hits.

Caching Strategies: The Art of Remembering

Making the same costly calculations or database requests over and over? It’s time to introduce caching to remember the results and serve them faster.

Why Cache?:

  • Reducing database load, improving response times, and offering a smoother user experience are the primary reasons.

Setting up Redis/Memcached with Django:

  • Install the necessary package: pip install django-redis or django-memcached.
  • Update the CACHES setting in your settings.py to use the chosen caching backend.

Cache in Action:

  • View Caching: Use Django’s cache_page decorator to cache entire views.
  • Fragment Caching: In templates, use the {% cache %} template tag to cache specific fragments.
  • Low-level Caching: For granular control, use Django’s cache API. Example: cache.set('key', 'value', timeout)

Cache Versioning and Invalidation:

  • Cache data can become stale. Use versioning to ensure you’re not serving outdated information.
  • Implement cache invalidation strategies. Manually delete cache keys or rely on timeouts.

In the world of web applications, performance can often be the difference between retaining a user or losing them to the abyss of the internet. With these strategies in your arsenal, your applications won’t just run; they’ll soar. While the journey to optimization is continuous, every step you take solidifies the foundation of an exceptional application. Always strive for better, and remember that sometimes, the difference between good and great is just a few milliseconds.

Automated Testing — The Google Way

Testing. For some developers, it’s a word that elicits groans, for others, it’s akin to an art form. At Google, it’s in the DNA. Here’s a glimpse into how testing, specifically in the realms of Django, was approached in the tech corridors of the search giant. Let’s cut through the buzzwords and get to the essence.

Effective Mocking and Patching: Crafting Reality in a Controlled Environment

When testing, you’re not merely checking if a piece of code runs. You’re recreating complex, intertwined real-world scenarios in a controlled environment. Here’s how to achieve this with mocking and patching.

The Basics of Mock:

  • A Mock object follows the "duck typing" system. If it looks like a duck and quacks like a duck, it's a duck. With Mocks, you imitate objects and set expected behaviors and returns.

Utilizing Mock in Django:

  • Use Python’s inbuilt unittest.mock library. For example:
from unittest.mock import Mock
mock_query = Mock()
mock_query.filter.return_value = ['test_obj']

Patching in Action:

  • patch temporarily replaces an object with a mock. Extremely useful to mock out external services or changing behaviors of certain methods.
  • For instance, to mock Django’s ORM filter method:
from unittest.mock import patch
with patch('app.models.MyModel.filter', mock_query.filter):
    # Your test code here

Parallel Test Execution: Making Time Wait for You

The larger your application gets, the longer your tests take. Time, especially in agile environments, is of the essence. Here’s how to maximize efficiency with parallel testing.

The Why and How of Parallelism:

  • Parallel test execution is all about using system’s resources efficiently. If you have multi-core processors, why not use all of them?

Django’s Built-in Parallel Testing:

  • Starting with Django 3.2, there’s built-in support for running tests in parallel.
  • Use: ./manage.py test --parallel. Django will auto-detect the number of cores and run tests accordingly.

Potential Hurdles and Overcoming Them:

  • Database clashes: When tests run in parallel, there’s a risk of them trying to access the database simultaneously. Django circumvents this by creating a test database for each process.
  • Order-dependent tests: Ensure tests are atomic. Avoid relying on any state or sequence.

The lessons learned from Google are not just about technical know-how but also about the ethos of testing. It’s about a culture where quality assurance is more than just a phase — it’s an integral part of the development process. Embrace these methods, and let every deployment be one of confidence.

Conclusion

As we bring this extensive journey to a close, it’s important to sit back and contemplate the transformative power these tips can exert over a Django developer’s trajectory. From the streets of Prague to the vast digital infrastructure of Google, the lessons I’ve gathered, and subsequently shared, are distilled from real-world, high-stakes environments.

However, like every piece of knowledge, it’s true potential is only realized when it’s applied. So, as you switch back to your editor or command line, remember these insights, and consider how they might fit into your next project. And once you’ve had a chance to implement some of these strategies, I’d love to hear how they worked for you. Challenges faced, successes celebrated, every story adds to the collective wisdom of the developer community.

Ready to level up your programming skills? Become a supporter on my Patreon and unlock the best exclusive content, including step-by-step guides and highly explanatory videos coming soon!

🔒 What’s Included:

  • 📝 Exclusive Articles & Quizzes: Step-by-step guides and interactive exercises.
  • 🎥 In-Depth Videos: Extremely explanatory videos to make complex topics simple. (Soon)
  • 🛠 Code Reviews: Get personalized feedback on your projects.
  • 💡 Fast Q&A: Got questions? Get quick, expert answers.

Subscribe now and be the first to access all these goodies!

My Patreon

A Small Request

If this guide shed light on aspects of Django that were previously in the shadows or introduced you to a smarter way of doing things, do consider giving it a clap. It helps more than you might realize. Also, if you found value in what you read, a follow would mean the world to me, ensuring you’re always in the loop for future insights and deep-dives.

Appendix

For those hungry for more, and for the insatiably curious souls, here’s a list of resources and further reading to quench that thirst:

Till next time, keep coding, keep exploring, and never stop learning.

Thank you to my platinum Patreon supporters!

Builescu Daniel… only me right now.

If you want to see your name at the end of my articles don’t forget to subscribe on Patreon.

In Plain English

Thank you for being a part of our community! Before you go:

Django Tips
Django Rest Framework
Django Framework
Python Django Developer
Python Django Development
Recommended from ReadMedium