Chapter 2: Django QuerySet – Get Data
QuerySet – Get Data: How to actually GET data from the database using QuerySets
In other words: “How do I turn my Question and Choice models into real, useful data that I can show in templates, return in APIs, or process in views?”
Many beginners write Question.objects.all() once, see some objects, and think “okay I got data”. But real-world Django work is 80% about writing smart, efficient, readable get-data patterns.
Today we’re going to do this very slowly, very practically, like pair-programming — I’ll show you the exact lines, explain when SQL runs, what each pattern is good for, common traps, and the 20–25 most-used “get data” recipes you will copy-paste every single day.
Let’s open the shell right now:
|
0 1 2 3 4 5 6 |
python manage.py shell |
|
0 1 2 3 4 5 6 7 8 9 |
from polls.models import Question, Choice from django.utils import timezone from django.db.models import Sum, Count, Q, F from datetime import timedelta |
(Assume you have some questions + choices already — if not, create a few via admin or shell first.)
1. The Four Most Common “Get One Record” Patterns
Pattern A: Get one by primary key (most frequent in detail views)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
# Classic q = Question.objects.get(pk=5) # raises Question.DoesNotExist if missing # Safe version (recommended in views) from django.shortcuts import get_object_or_404 q = get_object_or_404(Question, pk=5) # raises Http404 if missing → perfect for web |
Pattern B: Get one by unique field (slug, username, email…)
|
0 1 2 3 4 5 6 |
q = get_object_or_404(Question, slug="best-biryani-hyderabad-2026") |
Pattern C: Get first matching record (newest, random, etc.)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Newest active question newest = Question.objects.filter(is_active=True).order_by("-pub_date").first() # Random one (not efficient on large tables) random_q = Question.objects.order_by("?").first() # First one that matches complex condition recent_fun = Question.objects.filter( is_active=True, category="fun", pub_date__gte=timezone.now() - timedelta(days=7) ).first() |
Pattern D: Get last record (oldest, last updated…)
|
0 1 2 3 4 5 6 7 |
oldest = Question.objects.order_by("pub_date").first() # oldest last_updated = Question.objects.order_by("-updated_at").first() # most recent update |
2. The Five Most Common “Get Many Records” Patterns
Pattern 1: Get latest N active items (homepage / list view)
|
0 1 2 3 4 5 6 7 8 9 10 11 |
latest = Question.objects \ .filter(is_active=True) \ .order_by("-pub_date") \ .select_related() \ .annotate(vote_count=Sum("choices__votes")) \ [:10] # ← LIMIT 10 |
→ In view:
|
0 1 2 3 4 5 6 |
context["latest_questions"] = latest |
→ In template:
|
0 1 2 3 4 5 6 7 8 |
{% for q in latest_questions %} <h3>{{ q.question_text }} ({{ q.vote_count|default:0 }} votes)</h3> {% endfor %} |
Pattern 2: Get items matching multiple conditions
|
0 1 2 3 4 5 6 7 8 9 10 11 |
fun_polls = Question.objects.filter( is_active=True, category="fun", pub_date__year=2026, vote_count__gt=5 # ← needs annotation or property ) |
With annotation:
|
0 1 2 3 4 5 6 7 8 9 |
fun_polls = Question.objects \ .filter(is_active=True, category="fun") \ .annotate(vote_count=Sum("choices__votes")) \ .filter(vote_count__gt=5) |
Pattern 3: Get related objects efficiently (avoid N+1)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# Bad – N+1 queries questions = Question.objects.all()[:5] for q in questions: print(q.choices.all()) # extra query per question # Good – 2 queries total questions = Question.objects \ .prefetch_related("choices") \ .all()[:5] for q in questions: for choice in q.choices.all(): print(choice.choice_text) |
|
0 1 2 3 4 5 6 7 8 9 10 |
# If you need question → choice direction choices = Choice.objects \ .select_related("question") \ .filter(votes__gt=0) \ .order_by("-votes") |
Pattern 4: Get aggregated / summarized data
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Stats for dashboard stats = Question.objects.aggregate( total_questions=Count("id"), total_votes=Sum("choices__votes"), avg_votes_per_poll=Avg("choices__votes"), most_voted=Max("choices__votes") ) print(stats) # {'total_questions': 42, 'total_votes': 150, ...} |
Per-object annotation:
|
0 1 2 3 4 5 6 7 8 9 |
qs = Question.objects.annotate( vote_count=Sum("choices__votes"), choice_count=Count("choices") ).order_by("-vote_count") |
Pattern 5: Complex filtering with Q objects (OR conditions)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from django.db.models import Q # Questions that are fun OR sports AND active qs = Question.objects.filter( Q(category="fun") | Q(category="sports"), is_active=True ) # NOT (politics) AND (recent OR high votes) qs = Question.objects.filter( ~Q(category="politics"), Q(pub_date__gte=timezone.now() - timedelta(days=7)) | Q(vote_count__gt=20) ) |
Your Quick Practice Session (Do This in Shell Right Now)
-
from polls.models import Question
-
Get one question by slug:
Python01234567q = get_object_or_404(Question, slug="your-slug-here")print(q) -
Get latest 5 active:
Python012345678latest = Question.objects.filter(is_active=True).order_by("-pub_date")[:5]for q in latest:print(q.question_text, q.pub_date) -
Annotate vote count:
Python012345678qs = Question.objects.annotate(vote_count=Sum("choices__votes"))[:5]for q in qs:print(q, q.vote_count) -
Try a Q filter:
Python01234567qs = Question.objects.filter(Q(category="fun") | Q(category="sports"))print(qs.count())
Tell me what you want next:
- Which “get data” pattern is still confusing? (single object? many? annotation? N+1?)
- Want 15 more real QuerySet examples from different situations?
- Ready to learn .values(), .values_list(), .distinct(), .union()?
- Or finally ready for Django Forms + ModelForm + Voting POST handling?
You’re now thinking like a real Django developer — QuerySets are the engine of almost every view you’ll write.
Keep playing in shell — you’re doing excellent! 🚀🇮🇳
