Hello! In this lesson, we're moving into two of the most critical areas for a professional Django developer: performance and security.
First, we'll cover the most important database optimization you can make: fixing the "N+1 query problem" using `select_related` and `prefetch_related`. Then, we'll spend the rest of the lesson on security, covering CSRF attacks, the "big picture" of securing an app, the difference between authentication and authorization, and how Django keeps user passwords safe. These are essential, non-negotiable topics for a full-stack role.
Both `select_related` and `prefetch_related` are powerful performance tools designed to solve the "N+1 query problem".
The N+1 Problem: Imagine you fetch 100 blog posts. Then, in your template, you loop through them and print each author's name (`post.author.name`). Django will hit the database 101 times: 1 query to get the 100 posts, and then N (100) more queries, one for each post, to fetch the author. This is extremely slow.
Analogy: The Inefficient Shopper
| Feature | `select_related` | `prefetch_related` |
|---|---|---|
| SQL Method | SQL JOIN | Separate `IN` query |
| Number of Queries | One (can be large) | Two (or more) small queries |
| Use For | `ForeignKey`, `OneToOneField` | `ManyToManyField`, Reverse `ForeignKey` |
Code Example: Assume `Book` has a `ForeignKey` to `Author`. An `Author` can write many `Books`.
# 1. The N+1 Problem (BAD):
# (Assuming 2 books in the database)
print("--- N+1 Query ---")
books = Book.objects.all()
for book in books:
print(book.author.name) # DB Hit!
# Output:
# --- N+1 Query ---
# (Query 1: Fetches all Books)
# (Query 2: Fetches Author for Book 1)
# Author One
# (Query 3: Fetches Author for Book 2)
# Author Two
# Total: 3 Queries
# 2. select_related (GOOD for ForeignKey):
print("--- select_related ---")
books = Book.objects.select_related('author').all()
for book in books:
print(book.author.name) # No DB Hit!
# Output:
# --- select_related ---
# (Query 1: Fetches all Books AND Authors with a JOIN)
# Author One
# Author Two
# Total: 1 Query
# 3. prefetch_related (GOOD for reverse relation):
print("--- prefetch_related ---")
authors = Author.objects.prefetch_related('book_set').all()
for author in authors:
print(f"{author.name}'s books:")
# .all() here does NOT hit the DB
for book in author.book_set.all():
print(f" - {book.title}")
# Output:
# --- prefetch_related ---
# (Query 1: Fetches all Authors)
# (Query 2: Fetches all Books WHERE author_id IN (1, 2))
# Author One's books:
# - Book Alpha
# Author Two's books:
# - Book Beta
# Total: 2 Queries
CSRF (Cross-Site Request Forgery) is a major web vulnerability. It's an attack that tricks a logged-in user into performing an action they didn't intend to.
Analogy: The Malicious Postcard
Django's `CsrfViewMiddleware` implements a secret token system to ensure only forms from your site can submit data.
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
Django is "secure by default" but still requires correct configuration. Securing an app is about layered defense.
Analogy: You don't just buy one big lock for your house (a firewall). You also lock your windows (XSS protection), use a deadbolt (CSRF), and have an alarm system (logging/monitoring).
Here is a checklist based on the OWASP Top 10 (Open Web Application Security Project):
This is a fundamental security concept. Both are handled by Django, but they answer two very different questions.
Analogy: A Corporate Building
| Concept | Authentication (AuthN) | Authorization (AuthZ) |
|---|---|---|
| The Question | Who are you? | What can you do? |
| Django System | `django.contrib.auth` (User model, `authenticate()`, `login()`) | Permissions Framework (Groups, `user.has_perm()`) |
| Example Check | `if request.user.is_authenticated:` | `if request.user.is_staff:` or `if user.has_perm('app.add_post')` |
| CBV Mixin | `LoginRequiredMixin` | `PermissionRequiredMixin` |
Django does not store passwords. It stores hashes of passwords. This is a one-way process designed to make it impossible to recover the original password, even if the database is stolen.
Analogy: The Meat Grinder
Django uses a robust, industry-standard method:
A stored password hash in Django's database looks like this:
pbkdf2_sha256$390000$aBcD1eFgH2iJ$kLmN3oPqR4sT5uV6w...
This string contains the algorithm (pbkdf2_sha256), the iterations (390000), the salt (aBcD1e...), and the final hash (kLmN3o...).
How Login Works:
Django never un-hashes the password.
2 questions · no sign-up · misses join your review queue, on this device only
Progress is stored in this browser only - no sign-up, nothing sent anywhere.
The same topic at architecture level, in the system design curriculum.