Nowości w Django 5.2 Beta 1 – przegląd zmian i praktyczne przykłady

Django 5.2 (wersja beta 1) wprowadza szereg usprawnień i nowych funkcjonalności, na które czekali deweloperzy. W poniższym wpisie szczegółowo omówimy najważniejsze zmiany, zilustrujemy je praktycznymi fragmentami kodu oraz porównamy z wersją Django 5.1. Skupimy się na tym, jak wykorzystać nowe funkcje w codziennej pracy z Django, a także jak przygotować istniejące projekty do migracji.
1. Przegląd zmian – Co nowego w Django 5.2?
Najważniejsze nowości i zmiany w Django 5.2 Beta 1:
- Obsługa złożonych kluczy głównych (Composite Primary Keys) – możliwość definiowania klucza głównego składającego się z wielu pól modelu (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
- Automatyczny import modeli w powłoce Django (
manage.py shell
) – uruchamiając shell Django, modele z zainstalowanych aplikacji są importowane automatycznie dla wygody (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Uproszczone dostosowywanie pól formularzy (BoundField) – nowy, prostszy mechanizm nadpisywania sposobu renderowania pól formularza bez potrzeby modyfikacji wewnętrznych metod (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
- Nowe API asynchroniczne w systemie uwierzytelniania – dodano asynchroniczne odpowiedniki metod tworzenia użytkowników i uprawnień (
acreate_user
,ahas_perm
itp.) oraz wsparcie dla asynchronicznych backendów auth (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Rozszerzenia w systemie formularzy – nowe widżety HTML5 (
ColorInput
,SearchInput
,TelInput
) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), usprawnienia w wyświetlaniu błędów (atrybuty ARIA) oraz nowy obiektScript
do dodawania atrybutów do tagów<script>
w formularzach (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Nowe funkcje w routingu i odpowiedziach HTTP – funkcja
reverse()
obsługuje teraz parametry zapytania i fragment URL (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django); odpowiedzi HTTP mają właściwość.text
ułatwiającą dostęp do treści (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django); wprowadzono opcjępreserve_request
dla przekierowań, aby zachować metodę HTTP (kody 307/308) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Usprawnienia w ORM i migracjach – obsługa JSON array przez nową funkcję
JSONArray
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django); operacja migracjiAlterConstraint
umożliwiająca modyfikację ograniczeń bez ich przeładowania (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django); ulepszona kolejność pól wQuerySet.values()
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django); wsparcie kluczy złożonych w niektórych wyrażeniach ORM. - Nowy dekorator szablonowy
simple_block_tag
– ułatwia tworzenie własnych znaczników szablonów, które przetwarzają blok zawartości (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Zmiany w narzędziach deweloperskich i bezpieczeństwie – np. nowy komunikat ostrzegawczy przy uruchamianiu serwera deweloperskiego, zwiększona domyślna iteracja haszowania haseł (PBKDF2) do 1 miliona (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), drobne poprawki w filtracji danych w raportach błędów itp.
W kolejnych sekcjach przyjrzymy się szczegółowo tym zmianom, prezentując konkretne przykłady użycia i porównując zachowanie z Django 5.1.
2. Szczegółowe omówienie nowości
Automatyczny import modeli w manage.py shell
Polecenie Django manage.py shell
stało się wygodniejsze – teraz automatycznie importuje modele ze wszystkich zainstalowanych aplikacji. Oznacza to, że po wejściu do shella nie musimy już ręcznie pisać from myapp.models import MyModel
dla każdego modelu; będą one dostępne od razu. Na przykład, uruchomienie shella z wyższą szczegółowością (--verbosity=2
) wyświetli komunikat o zaimportowanych obiektach:
$ python manage.py shell --verbosity=2
6 objects imported automatically, including:
from django.contrib.auth.models import Group, Permission, User
from django.contrib.contenttypes.models import ContentType
...
Jak widać, m.in. modele użytkowników (User
, Group
) są już dostępne (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). To ułatwienie przyspiesza interaktywne testowanie. Jeśli jednak chcemy zmodyfikować tę listę automatycznych importów (np. dodać własne moduły lub pominąć niektóre modele), Django 5.2 na to pozwala. Możemy nadpisać metodę get_auto_imports()
we własnym poleceniu shell, aby dostosować importy do potrzeb projektu (How to customize the shell command | Django documentation | Django) (How to customize the shell command | Django documentation | Django).
Przykład – dodanie własnych importów w shellu:
# polls/management/commands/shell.py
from django.core.management.commands import shell
class Command(shell.Command):
def get_auto_imports(self):
# Dodajemy funkcje reverse i resolve do automatycznych importów:
return super().get_auto_imports() + [
"django.urls.reverse",
"django.urls.resolve",
]
Dzięki temu nasze polecenie shell dodatkowo importuje funkcje reverse
i resolve
z django.urls
(How to customize the shell command | Django documentation | Django). Możemy także całkowicie wyłączyć automatyczne importy (np. do debugowania) flagą --no-imports
lub ustawiając get_auto_imports()
aby zwracało None
(How to customize the shell command | Django documentation | Django). Domyślnie jednak funkcja ta jest bardzo przydatna i nie wpływa na istniejący kod – to wyłącznie wygoda dla programistów.
Obsługa złożonych kluczy głównych (Composite Primary Keys)
Jedną z najważniejszych nowości Django 5.2 jest natywne wsparcie dla złożonych kluczy głównych, czyli klucza głównego opartego na więcej niż jednym polu tabeli (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Dotychczas Django zakładało istnienie pojedynczego klucza głównego (np. domyślnego pola id
). Teraz możemy zdefiniować w modelu pole pk
jako instancję klasy CompositePrimaryKey
, wskazując nazwy pól składających się na złożony klucz:
from django.db import models
class Order(models.Model):
reference = models.CharField(max_length=20, primary_key=True)
class Product(models.Model):
name = models.CharField(max_length=100)
class OrderLineItem(models.Model):
pk = models.CompositePrimaryKey("product_id", "order_id")
product = models.ForeignKey(Product, on_delete=models.CASCADE)
order = models.ForeignKey(Order, on_delete=models.CASCADE)
quantity = models.IntegerField()
W powyższym przykładzie klasa OrderLineItem
używa CompositePrimaryKey
, definiując klucz główny złożony z pola product_id
i order_id
(Composite primary keys | Django documentation | Django). W efekcie w bazie danych powstanie klucz główny PRIMARY KEY(product_id, order_id)
(Composite primary keys | Django documentation | Django).
Jak to działa w praktyce? Atrybut pk
obiektu takiego modelu jest krotką zawierającą wartości obu pól. Dla stworzonych obiektów możemy to zobaczyć:
>>> product = Product.objects.create(name="Apple")
>>> order = Order.objects.create(reference="A755H")
>>> item = OrderLineItem.objects.create(product=product, order=order, quantity=5)
>>> item.pk
(1, "A755H")
Tutaj item.pk
zwraca krotkę (1, "A755H")
, odpowiadającą kluczowi złożonemu (Composite primary keys | Django documentation | Django). Możemy również filtrować i wyszukiwać po złożonym kluczu używając krotki, np. OrderLineItem.objects.get(pk=(1, "A755H"))
(Composite primary keys | Django documentation | Django).
Wskazówka: Nie można zmieniać zdefiniowanego klucza złożonego w trakcie migracji. Django 5.2 nie wspiera migracji istniejącej tabeli z pojedynczego klucza na klucz złożony (ani odwrotnie) – takie operacje należy przeprowadzić ręcznie na bazie danych (Composite primary keys | Django documentation | Django). Najlepiej zaprojektować klucz złożony od razu przy tworzeniu modelu. Ponadto aktualnie Django admin nie obsługuje modeli ze złożonym kluczem głównym – nie można ich zarejestrować w panelu admina (to ograniczenie ma zostać usunięte w przyszłości) (Composite primary keys | Django documentation | Django). Warto też wiedzieć, że relacje typu ForeignKey nie mogą wskazywać na model ze złożonym kluczem (np. nie stworzymy prostego ForeignKey(OrderLineItem)
), choć można to obejść używając niższego poziomu API (ForeignObject
) kosztem utraty pewnych udogodnień (Composite primary keys | Django documentation | Django) (Composite primary keys | Django documentation | Django). Mimo tych ograniczeń, dodanie Composite Primary Key to duży krok naprzód dla projektów wymagających takiej struktury danych.

Uproszczone nadpisywanie BoundField (renderowanie pól formularzy)
Kolejną nowością jest ułatwienie w dostosowywaniu sposobu renderowania pól formularzy. Do tej pory, aby zmodyfikować zachowanie obiektu BoundField
(reprezentującego związane z danymi pole formularza), należało nadpisać metodę Field.get_bound_field()
– była to mało intuicyjna i globalna zmiana. Django 5.2 wprowadza prostszy mechanizm: możemy określić własną klasę BoundField na poziomie całego projektu, formularza lub pojedynczego pola poprzez atrybut bound_field_class
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
Dostępne są trzy poziomy:
- Globalnie: ustawienie
bound_field_class
w globalnym rendererze formularzy (BaseRenderer.bound_field_class
). - Na poziomie formularza: atrybut klasy formularza
Form.bound_field_class
. - Na poziomie pola: atrybut pola formularza
Field.bound_field_class
.
Ustawienia bardziej szczegółowe mają pierwszeństwo (pole > formularz > globalne) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
Rozważmy przykład: chcemy, by każde pole formularza renderowało się w otoczeniu diva z dodatkową klasą CSS. Możemy stworzyć własną klasę BoundField:
from django import forms
class CustomBoundField(forms.BoundField):
custom_class = "custom"
def css_classes(self, extra_classes=None):
result = super().css_classes(extra_classes)
# Dodajemy naszą klasę CSS jeśli jeszcze jej nie ma:
if self.custom_class not in result:
result += f" {self.custom_class}"
return result.strip()
class CustomForm(forms.Form):
bound_field_class = CustomBoundField # użyj naszej klasy BoundField
name = forms.CharField(label="Your Name", max_length=100, required=False,
widget=forms.TextInput(attrs={"class": "name-input"}))
email = forms.EmailField(label="Your Email")
W powyższym kodzie definiujemy CustomBoundField
dodający klasę "custom"
do atrybutów CSS każdego pola podczas renderowania (poprzez nadpisanie metody css_classes
) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Następnie przypisujemy tę klasę do formularza CustomForm
za pomocą bound_field_class
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Gdy teraz wygenerujemy HTML takiego formularza, każdy element pola będzie zawierał naszą dodatkową klasę CSS:
<div class="custom">
<label for="id_name">Your Name:</label>
<input type="text" name="name" class="name-input" id="id_name" maxlength="100">
</div>
<div class="custom">
<label for="id_email">Your Email:</label>
<input type="email" name="email" id="id_email" required maxlength="320">
</div>
Wygenerowany kod HTML pokazuje <div class="custom">
otaczający każde pole wraz z etykietą (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Udało się to osiągnąć bez grzebania w metodach wewnętrznych pól – wystarczyło użyć nowego mechanizmu bound_field_class
. Ten sam efekt można też osiągnąć ustawiając Field.bound_field_class
dla konkretnego pola, jeśli chcemy aby tylko wybrane pola korzystały z niestandardowego renderowania.
Nowy sposób jest wstecznie zgodny – jeśli ktoś nadpisywał get_bound_field()
w starym kodzie, będzie to nadal działać. Jednak korzystanie z bound_field_class
jest znacznie prostsze i bardziej czytelne. Więcej szczegółów i możliwości (np. hierarchię priorytetów) znajdziemy w dokumentacji (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
Asynchroniczne metody w uwierzytelnianiu (django.contrib.auth)
Django od wersji 3.1 stopniowo rozszerza wsparcie dla asynchronicznych widoków i operacji. W Django 5.2 zrobiono kolejny krok – dodano szereg asynchronicznych metod w ramach modułu uwierzytelniania (django.contrib.auth
). Dotyczy to zarówno menedżerów użytkowników, jak i obiektów użytkownika oraz backendów autoryzacji.
Przykłady nowych metod (asynchroniczne odpowiedniki istniejących metod, z prefiksem a
):
UserManager.acreate_user()
– asynchroniczna wersjacreate_user
(tworzenie użytkownika) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).UserManager.acreate_superuser()
– wersja dla tworzenia superużytkownika (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).BaseUserManager.aget_by_natural_key()
– asynchroniczne wyszukanie użytkownika po kluczu naturalnym (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).User.aget_user_permissions()
,User.aget_group_permissions()
– pobieranie uprawnień użytkownika/asynchronicznie (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).User.ahas_perm()
,User.ahas_perms()
,User.ahas_module_perms()
– sprawdzanie uprawnień (pojedynczych, wielu, modułowych) w trybie async (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).- Metody asynchroniczne pojawiły się także w klasach backendów uwierzytelniania:
ModelBackend.aauthenticate()
,ModelBackend.aget_user_permissions()
i analogiczne dla grup i wszystkich uprawnień,ModelBackend.ahas_perm()
,ahas_module_perms()
, a wRemoteUserBackend
dodanoaconfigure_user()
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
Dzięki temu, jeżeli piszemy widoki asynchroniczne (async def) i musimy np. utworzyć użytkownika lub sprawdzić uprawnienia, możemy to zrobić nie blokując pętli zdarzeń. Przykładowo, asynchroniczny widok rejestracji użytkownika może teraz wyglądać tak:
from django.contrib.auth import get_user_model
User = get_user_model()
async def register(request):
# ... (walidacja danych)
user = await User.objects.acreate_user(username="jan", email="jan@example.com", password="p@ssword123")
# user utworzony asynchronicznie, bez blokowania
# ... (dalsza logika)
return JsonResponse({"status": "ok"})
Metoda acreate_user
utworzy użytkownika korzystając z nowo dodanej asynchronicznej implementacji i zwróci obiekt User
– możemy na niego poczekać słowem kluczowym await
. Analogicznie skorzystamy z await user.ahas_perm('app.label_permission')
aby sprawdzić uprawnienie w sposób nieblokujący.
Ważne jest, że customowe backendy autoryzacji również mogą teraz definiować swoje asynchroniczne odpowiedniki metod. Jeśli podamy je w klasie backendu, to Django użyje ich automatycznie w kontekście asynchronicznym (co redukuje liczbę przełączeń kontekstu i poprawia wydajność) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Innymi słowy, możemy dodać np. async def aauthenticate()
do własnego backendu. Jeśli natomiast backend nie ma zaimplementowanych wersji asynchronicznych, Django i tak zadziała (będzie wykonywać operacje w wątku synchronizacyjnym), ale implementacja własnych async może dać zysk wydajności.
Na koniec, drobna zmiana w walidatorach haseł: klasa bazowa walidatora (UserAttributeSimilarityValidator
, MinimumLengthValidator
itd.) zyskała nową metodę get_error_message()
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Można ją nadpisać, aby w prosty sposób zdefiniować własną treść komunikatu błędu walidacji hasła (zamiast nadpisywać np. atrybuty czy całe validate
). To ułatwia spójne dostosowanie komunikatów błędów podczas sprawdzania złożoności hasła.
Nowe widżety formularzy: ColorInput, SearchInput, TelInput
W HTML5 dostępnych jest kilka specyficznych typów pól <input>
, które do tej pory wymagały ręcznego tworzenia atrybutu input_type
lub użycia widgetu TextInput z odpowiednim parametrem. Django 5.2 dodaje dedykowane klasy widżetów formularza dla trzech popularnych typów:
- ColorInput – widżet generujący
<input type="color">
, umożliwiający wybór koloru (przeglądarki zazwyczaj pokażą selektor koloru) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - SearchInput – widżet dla pola wyszukiwania (
<input type="search">
), które może mieć np. zaokrąglone rogi lub inne domyślne style zależnie od przeglądarki (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - TelInput – widżet dla numeru telefonu (
<input type="tel">
), pozwalający np. na włączenie klawiatury numerycznej na urządzeniach mobilnych (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
Użycie tych widżetów jest proste. Możemy przypisać je do pola formularza tak jak inne widgety:
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(label="Imię")
email = forms.EmailField(label="Email")
phone = forms.CharField(label="Telefon", widget=forms.TelInput)
query = forms.CharField(label="Szukaj", required=False, widget=forms.SearchInput)
favorite_color = forms.CharField(label="Ulubiony kolor", required=False, widget=forms.ColorInput)
W powyższym formularzu pole phone
będzie renderowane jako <input type="tel" ...>
, query
jako <input type="search">
, a favorite_color
jako <input type="color">
. Nie musimy ustawiać ręcznie żadnych atrybutów – wystarczy użyć odpowiedniego widżetu. Oczywiście można dalej dodawać atrybuty attrs
(np. klasę CSS) w razie potrzeby.
To drobna zmiana, ale pomocna – kod staje się bardziej semantyczny i zgodny ze standardami, a my piszemy mniej własnego kodu.
Usprawnienia dostępności (ARIA): W temacie formularzy warto wspomnieć o zmianach związanych z dostępnością. Django 5.2 automatycznie łączy komunikaty błędów pól formularza z polami poprzez atrybut aria-describedby
. Każdy BoundField
ma teraz właściwość aria_describedby
, która generuje odpowiednie ID elementu błędu (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Jeśli korzystamy z standardowego renderowania błędów przez ErrorList
, to Django zadba o ustawienie aria-describedby
na polu, wskazującym na element <ul class="errorlist">
z komunikatem błędu. Co więcej, klasa ErrorList
posiada nowy parametr field_id
, który pozwala nadać własne id
wygenerowanej liście błędów (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Dzięki temu poprawiono dostępność formularzy dla czytników ekranu i ułatwiono deweloperom tworzenie formularzy zgodnych ze standardami WCAG (powiązanie komunikatu błędu z polem jest automatyczne). W codziennej pracy nie musimy nic zmieniać – po prostu otrzymujemy bardziej accessible formularze za darmo.

Dekorator method_decorator
dla widoków asynchronicznych
Django od dawna posiada dekorator django.utils.decorators.method_decorator
ułatwiający stosowanie funkcji-dekoratorów (np. login_required
) do metod klasy (np. metody dispatch
czy get
w Class-Based Views). W Django 5.2 rozszerzono jego możliwości – teraz obsługuje on również asynchroniczne metody widoków (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
Co to oznacza? Jeśli tworzymy klasę widoku opartą o View
lub inne klasy CBV i definiujemy w niej metody asynchroniczne (async def
), to możemy je dekorować tak samo jak metody synchroniczne. Przykład:
from django.views import View
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
class UserDataView(View):
@method_decorator(login_required)
async def get(self, request):
# ta metoda jest asynchroniczna, ale udekorowana login_required
data = await fetch_user_data_async(request.user) # przykładowe async I/O
return JsonResponse({"data": data})
W powyższym kodzie metoda get
jest asynchroniczna, ale dzięki @method_decorator(login_required)
nadal wymagamy, by użytkownik był zalogowany. We wcześniejszych wersjach Django mogły być z tym problemy (dekorator mógł nie rozpoznawać coroutiny poprawnie). Teraz method_decorator
wykrywa, że dekorowana metoda jest asynchroniczna i odpowiednio dostosuje dekorator (o ile sam dekorator potrafi obsłużyć async, a login_required
potrafi). Ta zmiana jest wewnętrzna, dla nas niewidoczna, ale umożliwia szersze wykorzystanie asynchronicznych CBV bez utraty funkcjonalności dekoratorów.
Funkcja reverse()
z parametrami query i fragment
Generowanie URL-i na podstawie nazw widoków (funkcja django.urls.reverse
) zostało ulepszone. Często potrzebujemy nie tylko odtworzyć główny URL, ale również dodać parametry zapytania (query string) lub fragment URL (część za #
). Do tej pory robiliśmy to ręcznie, np. reverse('search') + '?q=abc#section'
. Django 5.2 pozwala zrobić to wprost, przekazując do reverse()
dodatkowe argumenty query
i fragment
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
Przykład użycia reverse
z nowymi parametrami:
from django.urls import reverse
# Przypuśćmy, że mamy route o nazwie "search-results"
url = reverse("search-results", kwargs={"term": "django"}, query={"page": 2, "lang": "pl"}, fragment="top")
print(url)
# Wynik: "/search/django/?page=2&lang=pl#top"
Powyżej reverse()
wygeneruje URL do widoku "search-results" (np. /search/django/
), a następnie doda do niego automatycznie ?page=2&lang=pl
oraz fragment #top
. Nie musimy martwić się samodzielnym kodowaniem tych elementów ani ich poprawnym escapingiem.
Parametr query
przyjmuje słownik (lub listę krotek) z parametrami, a fragment
to string (bez znaku #
). Uwaga: parametr query
nie zastępuje przekazywania args/kwargs
do reverse – nadal używamy args
lub kwargs
do dynamicznych części URL (np. term: "django"
w powyższym przykładze), a query
wyłącznie do części zapytania. Ta funkcjonalność poprawia czytelność kodu i zmniejsza ryzyko błędów przy doklejaniu query stringów ręcznie (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
Nowe właściwości obiektów HttpResponse i HttpRequest
W odpowiedziach HTTP i obiektach requestu pojawiło się kilka nowych udogodnień:
Przekierowania z zachowaniem metody (307/308) – Wprowadzono nowy argument preserve_request
dla klas HttpResponseRedirect
i HttpResponsePermanentRedirect
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) oraz analogiczną opcję dla skrótu redirect()
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Normalnie przekierowanie HTTP (302 lub 301) powoduje zmianę metody żądania na GET. Jednak kody 307 (Temporary Redirect) i 308 (Permanent Redirect) pozwalają zachować metodę i body oryginalnego żądania. Jeśli więc chcemy przekierować np. metodę POST na inny adres bez zmiany na GET (co by porzuciło dane formularza), możemy teraz zrobić:
return redirect('new-endpoint', preserve_request=True)
lub równoważnie:
return HttpResponseRedirect("/new-url", preserve_request=True)
Ustawienie preserve_request=True
spowoduje wysłanie kodu 307 (lub 308 w przypadku permanentnego przekierowania) zamiast 302/301 (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). To sygnał dla klienta, by ponowił żądanie pod nowym adresem używając tej samej metody i danych. Jest to szczególnie przydatne przy przekierowaniach w API REST (gdzie np. chcemy przekierować POST na inny endpoint), lub przy wymuszaniu HTTPS/WWW gdzie chcemy przekierować również nietypowe metody bez utraty ich payloadu. Z punktu widzenia programisty Django zmiana jest prosta – dodatkowy argument – ale warto być świadomym różnicy. Wcześniej osiągnięcie tego wymagało ręcznego tworzenia odpowiedzi z kodem 307.
HttpRequest.get_preferred_type(media_types) – Nowa metoda obiektu request, która pomaga w negocjacji typu odpowiedzi na podstawie nagłówka Accept wysyłanego przez klienta (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Przydaje się to w API lub widokach obsługujących różne formaty (HTML, JSON, XML). Metoda przyjmuje listę typów mediów (MIME), które nasz widok może zwrócić, i zwraca ten z nich, który jest najwyżej preferowany przez klienta lub None
, jeśli żaden nie jest akceptowany (Request and response objects — Django 5.2.dev20241127161328 documentation) (Request and response objects — Django 5.2.dev20241127161328 documentation).Przykład użycia:
def my_view(request):
best_type = request.get_preferred_type(["text/html", "application/json"])
if best_type == "application/json":
return JsonResponse({"msg": "Hello"})
else:
return HttpResponse("<p>Hello</p>", content_type="text/html")
Jeśli przeglądarka (klient) w nagłówku Accept
faworyzuje HTML, get_preferred_type
zwróci "text/html"
; jeśli np. klientem jest skrypt preferujący JSON, zwróci "application/json"
. To wygodny sposób na obsługę content negotiation – Django wykonuje za nas analizę skomplikowanego nagłówka Accept. Wcześniej deweloper musiał to robić ręcznie lub użyć bibliotek zewnętrznych. Teraz wbudowana metoda upraszcza ten kod. Pamiętajmy tylko, by przekazać listę obsługiwanych typów w odpowiedniej kolejności priorytetów.
HttpResponse.text – W obiekcie odpowiedzi HTTP (HttpResponse
i jego pochodnych) dodano właściwość (property) .text
, która zwraca zawartość odpowiedzi jako ciąg tekstowy (string) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Dotychczas, aby odczytać treść odpowiedzi, korzystaliśmy z response.content
(co zwraca bajty) i ewentualnie dekodowaliśmy do tekstu. Teraz response.text
zrobi to za nas, zakładając poprawne kodowanie UTF-8 (lub inne ustawione). Przykład:
response = HttpResponse("Witaj Django")
response.content # b"Witaj Django"
response.text # "Witaj Django"
Jest to bardziej Pythonowe i wygodne, zwłaszcza przy pisaniu testów lub debugowaniu odpowiedzi. Jeśli odpowiedź jest binarna lub nie da się zdekodować, property to może oczywiście rzucić wyjątek lub zwrócić nieczytelne dane – więc używamy go tam, gdzie spodziewamy się tekstu.
Nowy dekorator simple_block_tag
dla szablonów
Django 5.2 wprowadza nowy dekorator ułatwiający tworzenie własnych znaczników szablonowych (template tags), które otaczają blok kodu. Do tej pory istniał dekorator simple_tag
do szybkiego tworzenia tagów prostych (zwracających wartość), ale tagi blokowe (takie jak np. {% cache %}...{% endcache %}
czy {% if %}...{% endif %}
) wymagały bardziej złożonej implementacji. Teraz możemy skorzystać z @register.simple_block_tag
.
Dekorator simple_block_tag
pozwala łatwo zdefiniować tag przyjmujący fragment renderowanego już szablonu jako wejście. Funkcja dekorowana otrzyma pierwszy argument o nazwie content
, zawierający treść między tagiem otwierającym a zamykającym, już wyrenderowaną do łańcucha tekstowego (How to create custom template tags and filters — Django 5.2.dev20241127161328 documentation). Możemy następnie przetworzyć lub użyć tej treści wedle potrzeb i zwrócić wynik, który zostanie wstawiony w miejsce oryginalnego tagu wraz z blokiem.
Przykład:
Załóżmy, że chcemy stworzyć tag {% highlight %}...{% endhighlight %}
, który opakowuje dany blok HTML w dodatkowy znacznik (np. <div class="highlight">...</div>
). Z simple_block_tag
zrobimy to tak:
# myapp/templatetags/mytags.py
from django import template
register = template.Library()
@register.simple_block_tag
def highlight(content):
# content to już zrenderowany tekst wewnątrz taga
return f'<div class="highlight">{ content }</div>'
Nasz tag highlight
przyjmuje jeden argument content
i zwraca łańcuch HTML z owiniętą zawartością. Użycie w szablonie:
{% load mytags %}
{% highlight %}
<p>Ten tekst będzie podświetlony.</p>
{% endhighlight %}
Po wyrenderowaniu, w miejscu tego bloku pojawi się:
<div class="highlight">
<p>Ten tekst będzie podświetlony.</p>
</div>
Django zadbało o przekazanie zawartości bloku do naszej funkcji highlight(content)
, a my mogliśmy ją wykorzystać jak zwykły string i zwrócić opakowany w dodatkowy kod. Uwaga: w definicji takiej funkcji pierwszy parametr musi nazywać się content
– to konwencja wymagana przez mechanizm simple_block_tag
(How to create custom template tags and filters — Django 5.2.dev20241127161328 documentation). Możemy definiować też dodatkowe parametry dla tagu (np. atrybuty), jak w normalnej funkcji, oraz użyć opcji takich jak takes_context
czy zdefiniować alternatywną nazwę zamykającego tagu przez argument end_name
dekoratora (How to create custom template tags and filters — Django 5.2.dev20241127161328 documentation). simple_block_tag
dba też o prawidłowe escape'owanie i bezpieczeństwo (analogicznie do simple_tag
) (How to create custom template tags and filters — Django 5.2.dev20241127161328 documentation).
Dzięki temu nowemu dekoratorowi pisanie własnych znaczników szablonowych staje się prostsze i mniej podatne na błędy. Możemy łatwo tworzyć np. niestandardowe kontenery, elementy interfejsu czy integracje z bibliotekami JS (np. tag generujący elementy potrzebne dla biblioteki chart/wykresów, gdzie zawartość tagu to dane lub konfiguracja – już pojawiły się pomysły użycia simple_block_tag
do integracji z HTMX i innymi narzędziami (Tim Schilling: "I'm pretty excited for #Django…" - Fosstodon)). To kolejna funkcjonalność, która nie wpływa na stary kod (stare tagi działają bez zmian), ale daje nowe możliwości.
Usprawnienia w migracjach i ORM
Django 5.2 przynosi też kilka zmian, które choć mniejsze, mogą być istotne dla pracy z bazą danych i migracjami:
- Nowa operacja migracji
AlterConstraint
: Pozwala zmienić istniejące ograniczenie (constraint) bazy danych bez jego usuwania i tworzenia od nowa (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). W poprzednich wersjach zmiana np. warunku (condition
) wUniqueConstraint
czy zmiana flagiDEFERRABLE
wymagała faktycznego usunięcia constraint i dodania nowego, co w przypadku dużych tabel bywa kosztowne.AlterConstraint
to operacja typu no-op na poziomie migratora Django – sygnalizuje zmianę atrybutów ograniczenia, ale stara się ją zastosować bez pełnej przebudowy. To oznacza krótszy czas migracji i mniej blokad tabel, jeśli nasze bazy wspierają dynamiczną zmianę constraint. Przykład: jeśli mamy unikalność z warunkiem na kolumnie, i chcemy zmodyfikować ten warunek, migrator wygenerujeAlterConstraint
zamiastRemoveConstraint
+AddConstraint
. Warto zajrzeć do dokumentacji migracji po szczegóły, bo użycie tej operacji może zależeć od konkretnego typu bazy. - Kolejność pól przy
QuerySet.values()
zgodna z podaną: Metody QuerySetvalues()
ivalues_list()
od teraz zachowują dokładnie kolejność pól/wyrażeń, w jakiej je określimy w kodzie (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Poprzednio istniały pewne nieintuicyjne reguły ustalania kolejności kolumn (np. przy łączeniu zapytań czy dodawaniu adnotacji), co mogło powodować zamieszanie podczas łączenia QS przezunion()
itp. Teraz kolejność SELECT-a jest przewidywalna, co ułatwia pracę np. z wynikami jako krotki. Dla większości deweloperów będzie to niezauważalna zmiana (chyba że ktoś polegał na starej, dziwnej kolejności), ale poprawia spójność API. - Wsparcie set-returning functions (funkcje zwracające tabelę) w ORM: Dodano atrybut
Expression.set_returning = True
, który można ustawić w niestandardowych wyrażeniach ORM jeśli reprezentują one funkcje zwracające wiele wierszy (tzw. set-returning, np. niektóre funkcje PG) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Użycie takiej funkcji zwykle wymaga specjalnego traktowania (np. włączenia jej w subzapytanie). Ten mechanizm jest raczej dla zaawansowanych użytkowników i twórców bibliotek, ale pozwoli łatwiej korzystać z tych funkcji w Django. Przykład: jeśli napisaliśmy custom Expression reprezentującyunnest()
Postgresa, powinniśmy ustawić muset_returning=True
, aby Django ujął go w subselect zamiast w SELECT głównym. CharField.max_length
opcjonalny na SQLite: SQLite nie wymusza limitu długości tekstu w kolumnach VARCHAR, dlatego teraz Django nie będzie wymagać ustawieniamax_length
dla pól tekstowych przy użyciu SQLite (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Możemy teoretycznie zdefiniowaćCharField()
bez podaniamax_length
(będzie traktowany jako nieograniczony na SQLite). Jednak dla przenaszalności kodu nadal warto tenmax_length
podawać – na innych bazach jest on potrzebny. Ta zmiana raczej eliminuje pewną niespójność (wcześniej choć SQLite ignoruje limit, Django wciąż wymuszało jego podanie). Użytkownicy SQLite mogą więc zobaczyć ostrzeżenie tylko jeśli naprawdę brakowałomax_length
.QuerySet.explain()
rozszerzony: Jeśli używamy funkcji.explain()
do uzyskania planu zapytania SQL, to na PostgreSQL 17+ można teraz przekazać dodatkowe opcjeMEMORY
iSERIALIZE
do komendy EXPLAIN (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Pozwala to uzyskać więcej informacji (np. o wykorzystanej pamięci) w planie zapytania. To ukłon w stronę osób optymalizujących zapytania – jeśli nasz projekt działa na najnowszym Postgresie, mamy pełniejszy dostęp do możliwości EXPLAIN.
Nowa funkcja bazodanowa JSONArray
: W module django.db.models.functions
pojawiła się funkcja JSONArray
, która zwraca tablicę JSON z wartości wybranych pól (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Działa podobnie do innych funkcji agregujących JSON dostępnych w PostgreSQL. Możemy jej użyć np. tak:
from django.db.models import JSONArray, F
qs = Order.objects.annotate(items_array=JSONArray('items__name', 'items__quantity'))
Jeśli model Order ma powiązane items
, powyższe zapytanie zwróci pole items_array
będące listą JSON zawierającą nazwy i ilości pozycji. W praktyce Django wygeneruje SQL używający funkcji JSON (PostgreSQL) lub odpowiednika w danej bazie. To przydatne do pobierania zagregowanych danych w formacie JSON bezpośrednio z bazy (np. listy wszystkich powiązanych elementów). Wcześniej trzeba było to robić ręcznie przez Value
i Concat
lub raw SQL. Teraz mamy wygodny wbudowany mechanizm.
Podsumowując, zmiany w warstwie ORM/migracji to głównie rozszerzenie funkcjonalności i drobne korekty poprawiające developer experience. Większość z nich nie wymaga żadnych zmian w kodzie istniejących aplikacji, a jedynie daje nowe narzędzia do wykorzystania w razie potrzeby.

Ulepszenia w systemie e-mail (django.core.mail)
Moduł wysyłania maili otrzymał parę drobnych ulepszeń, które mogą ułatwić obsługę zaawansowanych scenariuszy:
- Załączniki jako namedtuple: W obiektach
EmailMessage
iEmailMultiAlternatives
dotychczas lista załączników (attachments
) była listą krotek (nazwa pliku, zawartość, MIME type). W Django 5.2 elementy tej listy to namedtuple zamiast zwykłych krotek (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Dzięki temu możemy odwoływać się do pól załącznika po nazwie (np.attachment.name
,attachment.content
) zamiast pamiętać indeksy 0,1,2. PodobnieEmailMultiAlternatives.alternatives
(lista alternatywnych treści, np. wersja HTML e-maila) jest teraz listą namedtuple zamiast krotek (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Ta zmiana nie wpływa na wysyłkę, ale jeśli gdzieś w kodzie iterujemy poemail.attachments
i odwołujemy się przez indeks, możemy również użyć czytelniejszych atrybutów. - Dodawanie alternatyw tylko przez metodę: Związana z powyższym jest zmiana backwards-incompatible: właściwość
EmailMultiAlternatives.alternatives
stała się tylko do odczytu – dodawanie pozycji do alternatywnych treści maila jest obsługiwane teraz wyłącznie przez metodęattach_alternative()
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Innymi słowy, nie powinniśmy modyfikować bezpośrednio listyemail.alternatives
. Jeśli ktoś tak robił (np.email.alternatives.append(...)
), powinien zmienić kod na użycie w/w metody. W zamian zyskujemy gwarancję spójności typów (namedtuple) i być może przyszłe rozszerzenia.
Nowa metoda body_contains()
: Klasa EmailMessage
(i jej pochodne) ma teraz wygodną metodę body_contains(substring)
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Zwraca ona True/False
informując, czy dany ciąg znaków występuje w treści e-maila lub w którejkolwiek z alternatywnych części typu text/ (czyli np. w wersji HTML maila). Przykład użycia:
email = EmailMessage(subject="Test", body="Hello **world**")
email.attach_alternative("<p>Hello <strong>world</strong></p>", "text/html")
assert email.body_contains("world") # True, znaleziono w body i w HTML
assert email.body_contains("<strong>") # False, szuka w czystym tekście
Ta funkcja jest przydatna zwłaszcza w testach – możemy łatwo sprawdzić, czy wygenerowany email zawiera spodziewaną treść, bez ręcznego parsowania MIME i części. Wewnątrz sprawdza zarówno email.body
(zakładając że to text/plain
) jak i wszystkie załączone części o typie tekstowym (np. text/html
) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). To drobne ułatwienie poprawia czytelność testów dotyczących wysyłki maili.
Drobne zmiany i usprawnienia w innych obszarach
Na koniec warto wymienić kilka pomniejszych zmian w Django 5.2, które mogą mieć wpływ na specyficzne obszary:
- Admin: Szablon bazowy admina (
admin/base.html
) posiada nowy blockextrabody
umożliwiający wstrzyknięcie własnego kodu tuż przed zamknięciem tagu</body>
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Dzięki temu rozszerzenia admina lub własne szablony mogą łatwiej dodawać np. dodatkowe skrypty JS na końcu strony. Ponadto, pola typuURLField
wyświetlane w adminie będą teraz automatycznie renderowane jako klikalne linki (anchor<a href="...">
) zamiast czystego tekstu (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), co poprawia użyteczność interfejsu administratora. - Admindocs: Dokumentacja modeli generowana przez
django.contrib.admindocs
została ograniczona tylko do użytkowników mających uprawnienia view lub change dla danego modelu (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Wcześniej potencjalnie każdy zalogowany w adminie mógł podejrzeć dokumentację modeli; teraz jest to bardziej restrykcyjne. Dodatkowo, składnia linków w docstringach obsługuje teraz własny tekst linku przez format:role:\
tekst `` (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), umożliwiając ładniejsze formatowanie odnośników. - Bezpieczeństwo haseł: Domyślna liczba iteracji algorytmu PBKDF2 dla hashowania haseł wzrosła z 870 000 do 1 000 000 (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). To zwiększa bezpieczeństwo (utrudnia ataki brute-force), ale nie wymaga od nas żadnej zmiany – Django automatycznie zadba o przepisanie hashy przy najbliższej zmianie hasła przez użytkownika. Wydajność nie powinna ucierpieć znacząco na współczesnych serwerach.
- Filtracja danych w raportach błędów: Obiekt
SafeExceptionReporterFilter
(stosowany np. do wycinania wrażliwych danych w debugu) uznaje teraz każdą ustawienie/zmienną, której nazwa zawieraAUTH
, za wrażliwą (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Czyli np. jeśli mamy jakiśAUTH_TOKEN
w settings, to jego wartość zostanie ukryta w raportach błędów. To drobna zmiana zwiększająca bezpieczeństwo wyświetlanych logów. - Zarządzanie plikami statycznymi: Funkcja
django.contrib.staticfiles.finders.find(path, all=True)
dostała zmienioną sygnaturę – parametrall
jest deprecate (będzie usunięty) i zastąpiony przezfind_all
o tej samej funkcjonalności (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Jeśli w projekcie gdzieś wywołujemyfind(..., all=True)
, warto już teraz zmienić nafind_all=True
aby uniknąć ostrzeżeń. - Ostrzeżenie przy
runserver
: Uruchamiając serwer deweloperski komendąmanage.py runserver
, zobaczymy teraz w konsoli ostrzeżenie, że ten serwer nie nadaje się do środowiska produkcyjnego (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Wielu początkujących błędnie używa go w produkcji, stąd Django postanowiło bardziej stanowczo to komunikować. Jeśli wiemy co robimy i chcemy to wyłączyć (np. w testach integracyjnych), można ustawić zmienną środowiskowąHIDE_PRODUCTION_WARNING=true
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), by ukryć to ostrzeżenie. - Inne zmiany w tle: Warto odnotować, że typy bazodanowe PostGIS 3.0 oraz GDAL 3.0 nie są już wspierane (należy używać nowszych wersji tych bibliotek GIS) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Django 5.2 porzuca wsparcie dla PostgreSQL 13 – minimalna wspierana wersja to 14 (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Nowe projekty Django domyślnie używają dla MySQL zestawu znaków
utf8mb4
zamiastutf8/utf8mb3
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (to ukłon w stronę obsługi emoji i pełnego Unicode – jeśli ktoś bardzo potrzebuje staregoutf8mb3
, musi to teraz wymusić w konfiguracji bazy, ale nie jest to zalecane). - Kontekst szablonu debug: Kontekstowy procesor
debug()
nie jest już domyślnie dodawany do ustawień nowo tworzonych projektów (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). W praktyce oznacza to, że w świeżym projekciesettings.py
w sekcjiTEMPLATES[...]OPTIONS['context_processors']
nie znajdziemy wpisu"django.template.context_processors.debug"
. Dla działających projektów po aktualizacji nic to nie zmienia (zostaje tak jak było), ale jeśli generujemy nowy projekt 5.2 i chcemy używać zmiennych debug w szablonach, musimy dodać ten processor. alters_data=True
dla metod tworzących obiekty: Kilka metod wysokiego poziomu, które zmieniają stan bazy (np.UserManager.create_user()
,QuerySet.create()
,QuerySet.bulk_create()
i ich asynchroniczne odpowiedniki) otrzymało flagęalters_data=True
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Flaga ta jest używana przez mechanizm szablonów, by ostrzec lub zablokować wywołanie takich metod w kontekście renderowania (zapobiega to przypadkowemu tworzeniu danych np. przez wstawienie{{ model.objects.create(...) }}
w szablonie). Dla zwykłego kodu aplikacji nie ma to wpływu, ale zapewnia dodatkową ochronę przed niechcianym efektem ubocznym w warstwie prezentacji.
Lista drobnych zmian jest dłuższa (np. w GIS dodano obsługę typów geometrycznych curved, w syndication feedy mogą teraz deklarować style XSL przez atrybut stylesheets
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), itp.), ale powyżej skupiliśmy się na tych, które mogą dotyczyć większości programistów Django na co dzień.
3. Porównanie z wcześniejszą wersją (Django 5.1) – wpływ zmian na istniejące aplikacje
Większość zmian wprowadzonych w Django 5.2 jest kompatybilna wstecznie lub dodaje nowe możliwości bez naruszania istniejącego kodu. Oznacza to, że aktualizacja z Django 5.1 do 5.2 powinna przebiec dość gładko, o ile nasz projekt nie korzysta z funkcjonalności, które zostały wycofane lub zmienione. Poniżej podsumowujemy, jak nowe funkcje wpływają na projekty napisane pod Django 5.1:
- Nowe funkcjonalności są opcjonalne: Takie dodatki jak composite primary keys, automatyczne importy w shellu, dekorator
simple_block_tag
, nowe widgety formularzy czy asynchroniczne metody w auth nie wymagają od nas żadnych zmian – możemy zacząć z nich korzystać w kodzie, ale stary kod będzie działał jak dotychczas. Np. jeśli nie używamy composite PK, nic się nie zmienia. Jeśli nie wprowadzamy własnego BoundField, domyślne zachowanie jest identyczne jak w 5.1. - Zmiany w ustawieniach baz danych: Jeśli nasz projekt działa na PostgreSQL 13 lub starszym, należy zaplanować aktualizację bazy do wersji 14+, ponieważ Django 5.2 nie wspiera już PG13 (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Próba użycia z niewspieraną bazą może skutkować błędami przy migracjach lub uruchamianiu połączenia. Podobnie w przypadku integracji GIS – upewnijmy się, że korzystamy z PostGIS >= 3.1 i GDAL >= 3.1 (starsze 3.0 zostały porzucone) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
- Zachowanie MySQL: Zmiana domyślnego charsetu na
utf8mb4
jest z reguły zmianą pozytywną (lepsza obsługa Unicode). Jeśli jednak nasz stary projekt zakładałutf8
(alias dlautf8mb3
), warto upewnić się, że migracja schematu bazy przebiegnie poprawnie. W razie potrzeby można wymusić stary charset w ustawieniach DB (opcjaOPTIONS {'charset': 'utf8mb3'}
) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), choć zaleca się migrację danych nautf8mb4
. Dla nowych tabel Django 5.2 i tak użyjeutf8mb4
domyślnie. - Deprecacje i zmiany w API: Sprawdźmy w kodzie, czy nie używamy funkcji/parametrów oznaczonych do usunięcia:
- W funkcji
staticfiles.finders.find()
zamieńmy argumentall=True
nafind_all=True
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Jeśli wywołujemy
django.contrib.auth.login(request, user=None)
(bez przekazanego użytkownika, licząc że weźmierequest.user
), to powinno to zostać zmodyfikowane – w przyszłości takie użycie nie będzie wspierane (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Zawsze przekazujmy konkretnego użytkownika do zalogowania. (Ta zmiana zapobiega przypadkowym logowaniom obecnego użytkownika gdy przekazaliśmy None, co i tak jest rzadkim przypadkiem). - W agregacjach PostgreSQL: jeżeli korzystamy z
ArrayAgg
,JSONBAgg
lubStringAgg
z parametremordering
, zamieńmy go naorder_by
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Stare nazewnictwo zostało oznaczone jako deprecated (było niespójne z resztą API, teraz będzie ujednolicone). - Wysyłka e-mail: jeżeli nasz kod manipulował bezpośrednio listą
EmailMultiAlternatives.alternatives
, powinniśmy przejść na oficjalny sposób dodawania alternatyw – metodęattach_alternative()
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). W przeciwnym razie po aktualizacji do 5.2 takie bezpośrednie modyfikacje mogą nie działać.
- W funkcji
- Potencjalne subtelne różnice: Choć starano się unikać regresji, pewne poprawki mogą zmienić zachowanie, na którym nieświadomie polegał nasz kod:
- Kolejność wyników z
QuerySet.values()
teraz jest deterministyczna i zgodna z kolejnością pól w kodzie (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Jeśli gdzieś zakładaliśmy konkretny porządek kolumn wynikowych w oparciu o poprzednie heurystyki (mało prawdopodobne), możemy dostać dane w innej kolejności – warto to przetestować, np. jeśli budujemy na nich jakieś struktury. - Zmiana flagi
alters_data=True
w pewnych metodach (create(), create_user() itd.) oznacza, że nie można ich już wywoływać w szablonach (wcześniej było to możliwe, choć stanowczo niewskazane). Jeśli z jakiegoś powodu mieliśmy logikę tworzącą obiekty w trakcie renderowania szablonu, po aktualizacji do 5.2 to podejście przestanie działać (co jest zmianą na plus – tego i tak nie powinno się robić). - Domyślny context processor debug jest wyłączony w nowych projektach. W istniejących projektach po prostu pozostanie, dopóki go sami nie usuniemy. Ale jeżeli np. porównujemy ustawienia nowego projektu 5.2 ze starym 5.1, zauważymy tę różnicę. Nie wpływa to jednak na działanie aktualizowanego projektu.
- Jeżeli korzystaliśmy z niestandardowych obejść braku composite PK (np. własne mechanizmy), teraz warto rozważyć użycie natywnej obsługi. Jednak migracja istniejącego modelu na composite PK nie jest wspierana automatycznie (Composite primary keys | Django documentation | Django), więc jeśli planujemy refactoring kluczy, trzeba to zrobić ostrożnie (np. poprzez utworzenie nowej tabeli i przeniesienie danych, albo manualne operacje DB i
--fake
migracje (Composite primary keys | Django documentation | Django)). - Modele ze złożonym kluczem nie zadziałają w adminie – jeśli planujemy je dodać do istniejącego projektu i korzystać z admin, napotkamy ograniczenie (admin wyświetli błąd). Trzeba poczekać na pełne wsparcie lub ewentualnie zaimplementować własne mechanizmy admin dla takich modeli.
- Kolejność wyników z
Ogólnie, aktualizacja z 5.1 do 5.2 nie powinna wymagać dużych zmian w kodzie aplikacji. Zaleca się oczywiście uruchomienie pełnej suite testów po migracji oraz przejrzenie ostrzeżeń (Django przy starcie i w testach wypisuje RemovedInDjango60Warning
dla rzeczy planowanych do usunięcia). Zwróćmy uwagę na ostrzeżenia dotyczące deprecacji wymienionych wyżej elementów i poprawmy je zawczasu, aby mieć święty spokój przed wydaniem Django 6.0.
Warto dodać, że Django 5.2 jest wydaniem Long-Term Support (LTS) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), co oznacza, że po stabilnym wydaniu będzie otrzymywać poprawki bezpieczeństwa przez następne ~3 lata. Dlatego tym bardziej opłaca się z czasem zaktualizować projekty z poprzedniego LTS (4.2) czy 5.1 do wersji 5.2.
4. Nowe API i funkcjonalności – lista i zastosowanie
Dla czytelności, zebraliśmy w jednym miejscu kluczowe nowe elementy API Django 5.2, które zostały opisane powyżej. Ta lista może służyć jako szybkie podsumowanie tego, co doszło w tej wersji:
django.db.models.CompositePrimaryKey(*fields)
– nowa klasa pola modelu pozwalająca zdefiniować klucz główny złożony z wielu pól (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Użycie: w modelu przypisujemypk = models.CompositePrimaryKey('field1', 'field2', ...)
. (Ograniczenia: brak wsparcia w admin, brak migracji z/do single PK).- Automatyczne importy modeli w shellu: Wbudowane
manage.py shell
teraz ładuje modele z INSTALLED_APPS automatycznie (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Można dostosować tę listę, nadpisując metodęget_auto_imports()
we własnym poleceniu shell (How to customize the shell command | Django documentation | Django). BaseRenderer.bound_field_class
/Form.bound_field_class
/Field.bound_field_class
– nowe atrybuty umożliwiające wskazanie własnej klasy BoundField dla wszystkich pól, danego formularza lub konkretnego pola (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Ułatwia to dostosowanie renderowania pól formularza (np. dodawanie wrapperów, dodatkowych atrybutów) bez ingerencji w wewnętrzne metody pola.- Nowe asynchroniczne metody w auth:
UserManager.acreate_user()
,acreate_superuser()
– tworzenie użytkownika w trybie async (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).User.aget_user_permissions()
,ahas_perm()
i inne analogiczne – uzyskiwanie uprawnień użytkownika asynchronicznie (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).ModelBackend.aauthenticate()
i reszta metod z prefiksema
– asynchroniczne implementacje metod backendu autoryzacji (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).- Cel: wsparcie bezproblemowego działania w widokach async, redukcja blokowania i przełączeń kontekstu.
django.utils.decorators.method_decorator
– od teraz poprawnie obsługuje dekorowanie metod asynchronicznych widoków klasowych (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Dzięki temu dekoratory (login_required, permission_required itd.) mogą być stosowane do metodasync def
wewnątrz klas dziedziczących zView
i podobnych.- Widżety formularzy HTML5:
forms.ColorInput
→<input type="color">
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django),forms.SearchInput
→<input type="search">
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django),forms.TelInput
→<input type="tel">
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).- Ułatwiają użycie natywnych pól HTML5. Stosowanie:
widget=forms.ColorInput
itp. przy definicji pola formularza.
- Dostępność formularzy:
ErrorList(field_id=...)
– pozwala nadać customid
liście błędów (używane do powiązania z aria-describedby) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).BoundField.aria_describedby
– właściwość dostarczająca wartość atrybutu aria-describedby, wskazującą na element błędu powiązany z polem (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).- Automatyczne ustawianie aria-describedby w renderowanych polach z błędami (domyślne zachowanie, nie wymaga kodu) poprawiające dostępność.
- URL i przekierowania:
django.urls.reverse(viewname, args..., query=dict, fragment=str)
– teraz obsługuje parametry query i fragment dołączane do wygenerowanego URL (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).redirect(to, ..., preserve_request=True)
– nowa flaga powodująca użycie przekierowania 307/308 zamiast 302/301, aby zachować metodę HTTP (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Analogicznie dla klasHttpResponseRedirect(preserve_request=...)
iHttpResponsePermanentRedirect
.
- Request/Response:
HttpResponse.text
– property zwracająceHttpResponse.content
jako tekst (zdekodowany string) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).HttpRequest.get_preferred_type([list_typów])
– metoda zwracająca preferowany przez klienta typ (z listy) na podstawie nagłówka Accept (Request and response objects — Django 5.2.dev20241127161328 documentation). Ułatwia content negotiation (np. wybór HTML vs JSON w widoku).
- Funkcje i klasy ORM:
migrations.AlterConstraint
– klasa operacji migracji do zmiany istniejącego constraint bez dropt/create (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).QuerySet.values()/values_list()
– zachowuje kolejność pól wg definicji w kodzie (poprawa przewidywalności) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).models.CharField.max_length
– nie jest wymagany na SQLite (pole może być nieograniczone) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).QuerySet.explain(**options)
– na Postgres 17+ obsługuje nowe opcjeMEMORY
,SERIALIZE
dla EXPLAIN (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).django.db.models.functions.JSONArray(*expressions)
– nowa funkcja zwracająca JSON array z podanych wyrażeń (np. pól) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).Expression.set_returning = True
– atrybut dla niestandardowych wyrażeń wskazujący, że zwracają wiele wierszy (set-returning function) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).Expression.allows_composite_expressions = True/False
– atrybut sygnalizujący czy dane wyrażenie może być użyte wewnątrz klucza złożonego (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (istotne przy obsłudze composite PK w wyrażeniach).
- Admin i autoryzacja:
admin/base.html
– nowy blok templateextrabody
przed</body>
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).URLField
w admin – automatycznie renderowany jako link (anchor tag) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).SafeExceptionReporterFilter.hidden_settings
– teraz uznaje nazwy zawierające "AUTH" za wrażliwe (maskuje je w raportach błędów) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).django.contrib.auth.password_validation.PasswordValidator.get_error_message()
– nowa metoda do nadpisania, ułatwiająca personalizację komunikatów walidatora haseł (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
- Email:
EmailMessage.attachments
– elementy jako namedtuple zamiast tuple (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).EmailMultiAlternatives.alternatives
– lista namedtuple zamiast tuple (dodawanie tylko viaattach_alternative()
) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).EmailMessage.body_contains(substring)
– sprawdza, czy ciąg znajduje się w treści maila lub jego tekstowych częściach (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).
- Inne:
- Testy: Ukryto wewnętrzne frame'y asercji Django w raportach błędów testów (czytelniejsze tracebacks) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django); dane z fixture i migracji (z
serialized_rollback=True
) są teraz dostępne już wTransactionTestCase.setUpClass()
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - URLs:
django.urls.reverse()
– nowe arg.query
ifragment
(omówione wyżej) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Utilities:
django.utils.safestring.SafeString.__add__
zwraca terazNotImplemented
gdy łączony z obiektem nie-łańcuchowym (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django) (dzięki temu, np. jeśli ktoś doda SafeString do obiektu datetime, użyje się mechanizmu__radd__
datetime lub rzuci błąd zamiast traktować obiekt jako str – to bardziej poprawne zachowanie zgodne zstr.__add__
). django.utils.html.format_html_join()
– teraz akceptuje iterowalne z mappingami (słownikami), przekazując je jako keyword args do format_html (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Ułatwia to generowanie serii powtarzalnych elementów HTML z różnymi atrybutami.
- Testy: Ukryto wewnętrzne frame'y asercji Django w raportach błędów testów (czytelniejsze tracebacks) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django); dane z fixture i migracji (z
forms.Media
– klasa Script
: Nowy typ obiektu do deklaracji plików JS wewnątrz formularzy z możliwością dodania atrybutów HTML. Przykład użycia:
class MyForm(forms.Form):
class Media:
js = [forms.widgets.Script('myapp/script.js', defer=True)]
Spowoduje to wyrenderowanie tagu <script src="myapp/script.js" defer></script>
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Dzięki temu możemy dodawać defer
, async
, type
czy inne atrybuty do naszych skryptów osadzanych przez formularze, co wcześniej wymagało niestandardowych rozwiązań.
Jak widać, lista jest długa – Django 5.2 dostarcza sporo nowości. W codziennej pracy najpewniej najbardziej odczujemy takie rzeczy jak automatyczne importy w shellu, obsługa composite PK (jeśli mieliśmy taką potrzebę, to przełomowa zmiana), wygodniejsze reverse() z query, czy asynchroniczne ulepszenia jeśli tworzymy async views. Inne zmiany choć mniejsze, sumują się na bardziej dopracowany framework.

5. Możliwe problemy i wskazówki dotyczące migracji do Django 5.2
Przy planowaniu migracji istniejącego projektu do Django 5.2 Beta (a wkrótce stabilnej wersji) warto wziąć pod uwagę kilka kwestii:
- Zgodność wersji bazy danych i bibliotek: Upewnij się, że środowisko spełnia nowe minimalne wymagania. Jeśli używasz PostgreSQL 13 lub starszej – zaplanuj aktualizację do 14+ przed przejściem na Django 5.2 (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). W projektach GIS sprawdź wersje PostGIS (min. 3.1) i GDAL (min. 3.1) (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). W przypadku Oracle – minimalna wersja drivera
oracledb
to teraz 2.3.0 (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Testuj funkcjonalność z composite primary keys przed wdrożeniem: Jeśli planujesz wykorzystać złożone klucze główne w nowym modelu, pamiętaj o ograniczeniach (brak admin, brak powiązań ForeignKey z takich modeli (Composite primary keys | Django documentation | Django)). Jeśli chcesz zmodyfikować istniejący model by używał composite PK, przygotuj się na ręczną migrację bazy (Django nie zrobi tego automatycznie) (Composite primary keys | Django documentation | Django) – najlepiej stworzyć nową tabelę, przenieść dane i przemianować, lub użyć zaawansowanych narzędzi migracyjnych jak
SeparateDatabaseAndState
(Composite primary keys | Django documentation | Django). W innym razie trzymaj się pojedynczych PK w już wydanych modelach. - Sprawdź użycie deprecjonowanych elementów: Włącz w testach i podczas uruchamiania aplikacji wyświetlanie ostrzeżeń (np. ustawiając
PYTHONWARNINGS=d
lub korzystając zWarningsFixture
w pytest). Zwróć uwagę na ostrzeżenia typuRemovedInDjango60Warning
. Typowe rzeczy do wyszukania w kodzie to:find(..., all=True)
w plikach związanych ze statikami – zamienić nafind_all=True
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).login(request, user=None)
– upewnić się, że zawsze przekazujemy użytkownika (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django).- Użycie
ordering=
wArrayAgg
/JSONBAgg
/StringAgg
– zmienić naorder_by=
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Bezpośrednie modyfikacje
EmailMessage.alternatives
– przejść naattach_alternative()
(Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). - Ewentualne inne ostrzeżenia, które Django wypisze po uruchomieniu.
- Kompatybilność wsteczna ustawień: Aktualizacja do 5.2 nie powinna wymagać zmian w ustawieniach, ale jeśli generowałeś nowy projekt 5.2 i porównujesz go ze starym, zauważysz np. brak context processor
debug
– to nie błąd, po prostu w nowych projektach go nie dodano (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Jeśli z jakiegoś powodu polegałeś na nim (np. zmiennasql_queries
w kontekście dla debug Toolbar itp.), upewnij się, że dodałeś go w DEBUG w swoim projekcie. - Zmiany wydajnościowe i bezpieczeństwa: Zwiększenie iteracji PBKDF2 do 1e6 nie powinno mieć zauważalnego wpływu na użytkowników (logowanie/zmiana hasła może być minimalnie wolniejsza, ale to w granicach ułamków sekundy). W zamian hasła są bezpieczniejsze – tu nie ma nic do dostosowania, warto jedynie wiedzieć. Domyślne przełączenie MySQL na utf8mb4 to również krok naprzód – upewnij się tylko, że na etapie migracji schematu wszystkie kolumny tekstowe przejdą na utf8mb4 (Django powinno to obsłużyć automatycznie, może wygenerować migracje zmieniające definicje kolumn).
- Nowy warning przy
runserver
: Jeśli w integracji CI/CD lub w jakichś skryptach uruchamiających dewelopersko aplikację to ostrzeżenie jest problematyczne (np. zaśmieca logi), użyj zmiennejHIDE_PRODUCTION_WARNING="true"
w środowisku uruchomieniowym runserver, by je wyciszyć (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django). Natomiast zdecydowanie nie wyłączaj go w faktycznym środowisku produkcyjnym – lepiej w ogóle nie używać runserver w produkcji. - LTS i wsparcie: Django 5.2 jest wydaniem LTS, co oznacza, że warto rozważyć migrację do niego w perspektywie średnioterminowej, nawet jeśli Twój projekt stoi na Django 4.2 LTS. Masz na to czas, bo Django 4.2 będzie wspierane do 2026 roku (Django 5.2 release notes - UNDER DEVELOPMENT | Django documentation | Django), ale 5.2 oferuje już sporo usprawnień i będzie wspierane dłużej (do ~2028). Migracja z 4.2->5.2 pociąga za sobą również zmiany z wersji pośrednich (5.0, 5.1), więc zaplanuj testy i zapoznanie się z release notes tamtych wersji.
Na koniec, przy aktualizacji do Django 5.2 Beta 1 pamiętajmy, że to nadal wersja testowa – nie zaleca się wrzucania jej na produkcję. Warto jednak już teraz wypróbować nowe funkcje lokalnie czy na branchu testowym i zgłosić ewentualne błędy społeczności Django, aby finalne wydanie w kwietniu 2025 było jak najbardziej dopracowane (Django 5.2 beta 1 released | Weblog | Django) (Django 5.2 beta 1 released | Weblog | Django). Migracja istniejącego projektu powinna być dość prosta, a nowe funkcje – jak widać – mogą zaoszczędzić nam sporo czasu i kodu w przyszłości. Powodzenia w eksplorowaniu Django 5.2!