Pytania rekrutacyjne Django: ORM, Middleware i DRF, szczegółowa analiza
Pytania rekrutacyjne Django obejmujące optymalizację ORM z select_related, prefetch_related i trybem FETCH_PEERS w Django 6.1, architekturę middleware oraz wydajność serializerów Django REST Framework, uprawnienia i paginację.

Pytania rekrutacyjne Django dotyczące ORM, middleware i Django REST Framework stanowią fundament każdej rozmowy kwalifikacyjnej na stanowisko Python-developera w 2026 roku. Niniejszy artykuł omawia zaawansowane zagadnienia, od optymalizacji zapytań przez select_related i prefetch_related, nowy tryb FETCH_PEERS w Django 6.1, przez architekturę potoku middleware, aż po serializery, uprawnienia i throttling w DRF. Każda sekcja zawiera praktyczne przykłady kodu gotowe do zastosowania w realnych projektach opartych na Django 6.1 i DRF 3.18.
Rekruterzy szczególnie cenią kandydatów, którzy potrafią wyjaśnić różnice między select_related a prefetch_related na konkretnych przykładach SQL. Umiejętność uzasadnienia wyboru jednej metody nad drugą w kontekście wydajności daje wyraźną przewagę podczas rozmowy technicznej.
Optymalizacja ORM: select_related i prefetch_related
Jednym z najczęściej zadawanych pytań na rozmowach jest problem N+1 i sposoby jego rozwiązywania w Django ORM. Aby zrozumieć to zagadnienie, warto zacząć od modeli danych ilustrujących typowe relacje: klucz obcy (ForeignKey) oraz relacje wiele-do-wielu (ManyToManyField).
# models.py
from django.db import models
class Company(models.Model):
name = models.CharField(max_length=200)
founded_year = models.IntegerField()
class Developer(models.Model):
name = models.CharField(max_length=200)
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="developers")
skills = models.ManyToManyField("Skill", related_name="developers")
class Skill(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)Model Developer posiada relację ForeignKey do Company oraz ManyToMany do Skill. Bez odpowiedniej optymalizacji iteracja po developerach i odwoływanie się do powiązanych obiektów generuje oddzielne zapytanie SQL dla każdego wiersza, to właśnie problem N+1.
Rozwiązaniem jest zastosowanie odpowiedniego mechanizmu prefetchowania w zależności od typu relacji:
# queries.py — Correct approach for ForeignKey (single object)
developers = Developer.objects.select_related("company").all()
# Generates ONE SQL query with JOIN
# SELECT developer.*, company.* FROM developer INNER JOIN company ...
# Correct approach for ManyToMany (multiple objects)
developers = Developer.objects.prefetch_related("skills").all()
# Generates TWO SQL queries:
# 1. SELECT * FROM developer
# 2. SELECT * FROM skill INNER JOIN developer_skills WHERE developer_id IN (...)Zasada jest prosta: select_related stosuje się do relacji ForeignKey i OneToOneField, ponieważ generuje pojedyncze zapytanie z JOIN. Natomiast prefetch_related obsługuje relacje ManyToMany i odwrotne klucze obce, wykonując dwa oddzielne zapytania i łącząc wyniki w Pythonie. Znajomość tej różnicy eliminuje większość problemów wydajnościowych w aplikacjach Django.
Django 6.1: tryb FETCH_PEERS dla automatycznego zapobiegania N+1
Django 6.1 wprowadza nowy system trybów pobierania (fetch mode), który rozwiązuje problem N+1 na poziomie ORM. Podczas iteracji po queryset i dostępu do powiązanego pola tryb FETCH_PEERS pobiera to pole dla wszystkich instancji w queryset za pomocą jednego dodatkowego zapytania.
# views.py — Django 6.1 FETCH_PEERS mode
from django.db import models
from .models import Book
# Traditional approach: requires explicit prefetch_related
books = Book.objects.prefetch_related("author").all()
# Django 6.1 approach: automatic peer fetching
books = Book.objects.fetch_mode(models.FETCH_PEERS)
for book in books:
print(book.author.name) # Only 2 queries total: books + all authorsDostępne są trzy tryby pobierania:
FETCH_ONE(domyślny): pobiera brakujące pole tylko dla bieżącej instancjiFETCH_PEERS: pobiera pole dla wszystkich instancji z tego samego QuerySetFETCH_RAISE: rzuca wyjątekFieldFetchBlockedw kodzie krytycznym dla wydajności, gdzie lazy loading musi być wyłączony
Na potrzeby rozmowy rekrutacyjnej kandydat powinien znać oba podejścia: jawne select_related/prefetch_related dla precyzyjnej kontroli oraz FETCH_PEERS dla automatycznej optymalizacji podczas iteracji po queryset.
Niestandardowe Managery i łączenie QuerySetów
Podczas rozmów rekrutacyjnych często pada pytanie o Custom Managery, mechanizm enkapsulacji często używanych zapytań na poziomie modelu. Zamiast powtarzać logikę filtrowania w wielu widokach, można zdefiniować niestandardowy QuerySet i Manager.
# managers.py
from django.db import models
from django.utils import timezone
class ActiveDeveloperQuerySet(models.QuerySet):
def active(self):
"""Filter developers who logged in within the last 30 days."""
cutoff = timezone.now() - timezone.timedelta(days=30)
return self.filter(last_login__gte=cutoff)
def senior(self):
"""Filter developers with 5+ years of experience."""
return self.filter(years_experience__gte=5)
def by_skill(self, skill_name):
"""Filter developers by a specific skill."""
return self.filter(skills__name=skill_name)
class ActiveDeveloperManager(models.Manager):
def get_queryset(self):
return ActiveDeveloperQuerySet(self.model, using=self._db)
def active(self):
return self.get_queryset().active()Kluczowa zaleta tego podejścia polega na możliwości łączenia metod QuerySet w łańcuch (chaining). Każda metoda zwraca nowy QuerySet, co pozwala budować złożone zapytania w czytelny, deklaratywny sposób:
# usage.py — QuerySet chaining in action
# This works because each method returns a QuerySet
senior_python_devs = (
Developer.active_objects # custom manager
.active() # ActiveDeveloperQuerySet method
.senior() # chains another QuerySet method
.by_skill("Python") # chains a third method
.select_related("company") # standard QuerySet method still works
)Standardowe metody QuerySet, takie jak select_related, pozostają dostępne nawet przy użyciu niestandardowego Managera. To podejście jest zgodne z zasadą DRY i centralizuje logikę zapytań w jednym miejscu.
Django 6.0 wprowadziło CompositePrimaryKey dla modeli wymagających wielokolumnowych kluczy głównych oraz wbudowany framework do zadań w tle. Pytania rekrutacyjne o te funkcje stają się coraz częstsze, szczególnie dla kandydatów pracujących z bazami legacy lub wymagających przetwarzania asynchronicznego.
Potok middleware: architektura i kolejność wykonywania
Middleware w Django to łańcuch warstw opakowujących (wrapper layers), przez które przechodzi każde żądanie HTTP. Każdy middleware otrzymuje referencję do następnego elementu w łańcuchu poprzez get_response. Wywołanie self.get_response(request) przekazuje sterowanie dalej, do następnego middleware lub do widoku.
Poniższy przykład ilustruje middleware mierzący czas przetwarzania każdego żądania:
# middleware.py
import time
import logging
from django.http import JsonResponse
logger = logging.getLogger(__name__)
class RequestTimingMiddleware:
"""Logs the time taken to process each request."""
def __init__(self, get_response):
self.get_response = get_response # next middleware or view
def __call__(self, request):
start_time = time.monotonic()
response = self.get_response(request) # passes to next layer
duration_ms = (time.monotonic() - start_time) * 1000
logger.info(
"method=%s path=%s status=%d duration=%.2fms",
request.method,
request.path,
response.status_code,
duration_ms,
)
return responseKod wykonany przed wywołaniem get_response działa w fazie żądania (request phase), a kod po nim w fazie odpowiedzi (response phase). To fundamentalny wzorzec, który rekruterzy oczekują od kandydatów.
Middleware może również skrócić łańcuch przetwarzania, zwracając odpowiedź bez przekazywania żądania dalej. Typowy przykład to rate limiting:
# middleware.py — Rate limiting with short-circuit
from django.core.cache import cache
class RateLimitMiddleware:
"""Blocks requests exceeding 100 per minute per IP."""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
ip = request.META.get("REMOTE_ADDR")
cache_key = f"rate_limit:{ip}"
request_count = cache.get(cache_key, 0)
if request_count >= 100:
return JsonResponse( # short-circuits — view never executes
{"error": "Rate limit exceeded. Try again in 60 seconds."},
status=429,
)
cache.set(cache_key, request_count + 1, timeout=60)
return self.get_response(request)Gdy RateLimitMiddleware zwraca JsonResponse bezpośrednio, widok nigdy nie zostaje wywołany. To skrócenie (short-circuit) jest kluczowym mechanizmem ochrony przed nadmiernym obciążeniem serwera.
Hooki middleware: process_view, process_exception i process_template_response
Oprócz standardowej metody __call__, middleware w Django może implementować dodatkowe hooki pozwalające na interwencję w różnych fazach cyklu przetwarzania. Pytanie o te hooki pojawia się regularnie na rozmowach o zaawansowanych zagadnieniach Django.
# middleware.py — Full middleware with all hooks
class AuditMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_view(self, request, view_func, view_args, view_kwargs):
"""Called after URL resolution, before the view executes."""
logger.info("Calling view: %s", view_func.__name__)
return None # returning None continues normal processing
def process_exception(self, request, exception):
"""Called only if the view raises an exception."""
logger.error("View exception: %s", exception, exc_info=True)
return None # returning None lets Django's default handling proceed
def process_template_response(self, request, response):
"""Called if the response has a render() method (TemplateResponse)."""
response.context_data["audit_timestamp"] = time.time()
return response # must return a response with render()Trzy kluczowe hooki działają w następujący sposób:
- process_view: wywoływany po rozwiązaniu URL, ale przed wykonaniem widoku. Zwrócenie
Nonekontynuuje normalne przetwarzanie; zwrócenieHttpResponsepomija widok. - process_exception: wywoływany wyłącznie gdy widok rzuci wyjątek. Służy do logowania, alertów lub niestandardowej obsługi błędów.
- process_template_response: wywoływany gdy odpowiedź posiada metodę
render()(czyli jest instancjąTemplateResponse). Umożliwia modyfikację kontekstu szablonu przed renderowaniem.
Kolejność middleware w ustawieniu MIDDLEWARE jest istotna: żądanie przechodzi przez middleware od góry do dołu, a odpowiedź od dołu do góry.
Gotowy na rozmowy o Django?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Serializery DRF: zagnieżdżone relacje i wydajność
Django REST Framework wykorzystuje serializery do konwersji między obiektami Python a formatami takimi jak JSON. Na rozmowach kwalifikacyjnych częstym pytaniem jest obsługa zagnieżdżonych relacji bez generowania problemu N+1.
# serializers.py
from rest_framework import serializers
from .models import Developer, Company, Skill
class SkillSerializer(serializers.ModelSerializer):
class Meta:
model = Skill
fields = ["name", "category"]
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ["name", "founded_year"]
class DeveloperSerializer(serializers.ModelSerializer):
company = CompanySerializer(read_only=True) # nested FK
skills = SkillSerializer(many=True, read_only=True) # nested M2M
class Meta:
model = Developer
fields = ["id", "name", "company", "skills"]Parametr read_only=True oznacza, że zagnieżdżone dane są dostępne wyłącznie przy odczycie (GET). Aby uniknąć problemu N+1 w kontekście DRF, ViewSet musi odpowiednio optymalizować QuerySet:
# views.py
from rest_framework import viewsets
from .models import Developer
from .serializers import DeveloperSerializer
class DeveloperViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = DeveloperSerializer
def get_queryset(self):
return (
Developer.objects
.select_related("company") # JOIN for FK
.prefetch_related("skills") # separate query for M2M
.order_by("-id")
)Bez select_related i prefetch_related w ViewSecie każdy serializowany developer generuje oddzielne zapytania dla company i skills. Dla listy 50 developerów oznacza to 1 + 50 + 50 = 101 zapytań zamiast 3.
Uprawnienia w DRF: kontrola dostępu na poziomie obiektu
System uprawnień w DRF działa na dwóch poziomach: globalnym (view-level) i obiektowym (object-level). Na rozmowach rekrutacyjnych często pada pytanie o implementację niestandardowych klas uprawnień.
# permissions.py
from rest_framework.permissions import BasePermission
class IsOwnerOrAdmin(BasePermission):
"""
Object-level permission:
- Admin users can access any object
- Regular users can only access objects they own
"""
message = "Access restricted to the object owner or admin users."
def has_object_permission(self, request, view, obj):
if request.user.is_staff:
return True
# Assumes the model has an 'owner' field pointing to User
return obj.owner == request.userMetoda has_object_permission jest wywoływana przez DRF automatycznie przy operacjach retrieve, update, partial_update i destroy. Zastosowanie niestandardowych uprawnień w ViewSecie wygląda następująco:
# views.py — Applying custom permissions
from rest_framework import viewsets, permissions
from .permissions import IsOwnerOrAdmin
class ProjectViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated, IsOwnerOrAdmin]
def get_queryset(self):
# Non-admin users only see their own projects
if self.request.user.is_staff:
return Project.objects.all()
return Project.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user) # auto-assign ownerKluczowy niuans, na który zwracają uwagę rekruterzy: has_permission działa przy każdym żądaniu (poziom listy), natomiast has_object_permission tylko przy wywołaniu get_object() (poziom szczegółów). Brak nadpisania get_queryset dla widoków listowych tworzy lukę bezpieczeństwa.
Poleganie wyłącznie na has_object_permission bez filtrowania queryset pozostawia endpoint listowy bez ochrony. Zawsze należy łączyć uprawnienia na poziomie obiektu z filtrowanym get_queryset, aby wymusić kontrolę dostępu zarówno na widokach listowych, jak i szczegółowych.
Throttling i paginacja w DRF: konfiguracja produkcyjna
Throttling (ograniczanie liczby żądań) i paginacja to dwa mechanizmy, których poprawna konfiguracja jest warunkiem wdrożenia API na produkcję. Rekruterzy często pytają o różnice między typami paginacji i strategie throttlingu.
# settings.py — Production DRF configuration
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"anon": "20/minute", # unauthenticated users
"user": "200/minute", # authenticated users
},
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.CursorPagination",
"PAGE_SIZE": 25,
}Oddzielne limity dla użytkowników anonimowych i uwierzytelnionych są standardową praktyką. W kontekście paginacji, CursorPagination oferuje najlepszą wydajność dla dużych zbiorów danych, ponieważ nie używa OFFSET:
# pagination.py — Custom cursor pagination for consistent ordering
from rest_framework.pagination import CursorPagination
class CreatedAtCursorPagination(CursorPagination):
page_size = 25
ordering = "-created_at" # must be a unique, sequential field
cursor_query_param = "cursor" # ?cursor=abc123Pole ordering musi wskazywać na kolumnę z indeksem i unikalnymi, sekwencyjnymi wartościami. Cursor pagination koduje pozycję w ciągu znaków (cursor), co uniemożliwia przeskakiwanie do dowolnej strony, ale zapewnia stałą wydajność niezależnie od numeru strony.
Źródła
- Django 6.1 Release Notes — tryb FETCH_PEERS dla automatycznego zapobiegania N+1
- Django 6.0 Release Notes — CompositePrimaryKey i framework Background Tasks
- Django REST Framework Documentation — wzorce serializerów i ViewSetów
Kluczowe wnioski z przygotowań do rozmowy Django
- Optymalizacja ORM: metoda zapytania powinna odpowiadać typowi relacji.
select_relateddla ForeignKey/OneToOne,prefetch_relateddla ManyToMany i odwrotnych FK. TrybFETCH_PEERSw Django 6.1 zapewnia automatyczne zapobieganie N+1 podczas iteracji po queryset. - Niestandardowe QuerySety: umożliwiają czytelne łączenie metod (chaining) i wielokrotne wykorzystanie logiki biznesowej. Metody Manager nie mogą być łączone po pierwszym wywołaniu, ale metody QuerySet tak.
- Architektura middleware: żądanie przepływa od góry do dołu, odpowiedź od dołu do góry. Skrócenie łańcucha (short-circuit) przez zwrócenie odpowiedzi przed
get_response()to fundamentalny wzorzec dla rate limitingu, walidacji i kontroli dostępu. - Wydajność serializerów DRF: zagnieżdżone serializery wymagają optymalizacji QuerySet w ViewSecie. Bez
select_related/prefetch_relatedserializacja generuje N+1 zapytań na skali. - Uprawnienia DRF:
has_object_permissionnależy łączyć z filtrowanymget_queryset, aby wymusić kontrolę dostępu na obu poziomach. Same uprawnienia obiektowe pozostawiają widoki listowe bez ochrony. - Throttling i paginacja: cursor pagination skaluje się lepiej niż offset na dużych tabelach. Limity żądań należy konfigurować oddzielnie dla użytkowników anonimowych i uwierzytelnionych.
Aby przećwiczyć te wzorce, warto skorzystać z pytań rekrutacyjnych z Django ORM oraz pytań o Django middleware na platformie SharpSkill.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Znajdziesz błąd w Django?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 13 września 2026
Tagi
Udostępnij
Powiązane artykuły

Django 5.2: Wlasne Middleware i Obsluga Sygnalow -- Przygotowanie do Rozmow Rekrutacyjnych
Praktyczny przewodnik po tworzeniu wlasnych middleware i obsludze sygnalow w Django 5.2. Struktura middleware, asynchroniczne middleware, sygnaly post_save i pre_save, wlasne sygnaly domenowe oraz najczesciej zadawane pytania rekrutacyjne.

Zaawansowane Middleware w Django 2026: Tworzenie Własnych Middleware, Logowanie i Pytania Rekrutacyjne
Kompleksowy przewodnik po zaawansowanych technikach middleware w Django 2026. Dowiedz się, jak tworzyć własne middleware, implementować logowanie i przygotować się do rozmów kwalifikacyjnych.

Pytania rekrutacyjne Django i Python: Top 25 w 2026
25 najczesciej zadawanych pytan rekrutacyjnych z Django i Pythona. ORM, widoki, middleware, DRF, sygnaly i optymalizacja z szczegolowymi odpowiedziami i przykladami kodu.