03 β View va URL marshrutlash¶
β¬ οΈ Oldingi: 02 β O'rnatish va birinchi loyiha Β· π README Β· Keyingi: 04 β Template tizimi (DTL) β‘οΈ
Bu bobda: Django ilovasini
startappbilan yaratamiz va uning ichki tuzilishini bo'lakma-bo'lak tushunamiz. Funksiyaga asoslangan view (function-based view) yozamiz vaHttpResponseorqali oddiy matn/HTML,JsonResponseorqali esa JSON qaytarishni o'rganamiz. Keyinurls.pyfaylidapath()bilan marshrut (route) yaratamiz, dinamik segmentlarni (<int:id>,<slug:slug>,<str:>,<path:>,<uuid:>) ishlatamiz, hatto o'z konvertorimizni yozamiz.include()yordamida loyiha URL'larini ilovalarga taqsimlaymiz,app_nameva nomli URL (named URL) orqali manzillarnireverse()va{% url %}bilan quramiz β manzilni hech qachon qo'lda yozmaslik prinsipini o'zlashtiramiz. So'rovning HTTP metodini (GET,POSTva h.k.) tekshirishni varedirect()bilan yo'naltirishni ko'ramiz. Hamma kod Django 6.0.6 da haqiqatan ishga tushirib tekshirilgan.
Marshrutlash nima va MTV oqimida qayerda turadi¶
02-bobda loyiha yaratdik. Endi savol: brauzer http://127.0.0.1:8000/maqola/42/ manziliga so'rov yuborganda, Django qaysi Python kodini ishga tushishini qanday biladi?
Javob β URL marshrutlash (URL routing). Django manzilni (URL) view deb ataladigan funksiyaga bog'laydi. View β bu so'rovni (HttpRequest) qabul qilib, javobni (HttpResponse) qaytaradigan oddiy Python funksiyasi. Bu MTV (Model-Template-View) arxitekturasining "V" qismi.
So'rovning to'liq yo'li quyidagicha:
- Brauzer URL bo'yicha so'rov yuboradi.
- Django URLconf (URL konfiguratsiyasi) ni yuqoridan pastga qarab o'qiydi va birinchi mos kelgan
path()ni topadi. - Mos
path()ga bog'langan view chaqiriladi. URL'dagi dinamik qismlar (masalan42) view'ga argument sifatida uzatiladi. - View
HttpResponse(yoki uning vorisi) qaytaradi. - Django javobni brauzerga jo'natadi.
Python sintaksisi bo'yicha ko'nikmangiz susaygan bo'lsa β funksiyalar, lug'at (dict), f-string β Python qo'llanmasiga qarang. Bu bobda biz faqat Django'ga xos qismlarni chuqur ochamiz.
startapp: ilova yaratish¶
Django loyihasi (project) bir nechta ilova (app) dan tashkil topadi. Ilova β bu mantiqan bir butun bo'lgan funksionallik bo'lagi: blog, do'kon, foydalanuvchilar va h.k. Bitta loyihada bir nechta ilova bo'lishi, bitta ilova bir nechta loyihada qayta ishlatilishi mumkin.
Loyiha ildizida (manage.py yonida) yangi ilova yaratamiz:
Bu blog/ papkasini quyidagi tuzilish bilan yaratadi:
blog/
βββ __init__.py # papka Python paketi ekanini bildiradi
βββ admin.py # admin panel sozlamalari (07-bob)
βββ apps.py # ilova konfiguratsiyasi (BlogConfig)
βββ migrations/ # baza migratsiyalari (05-bob)
β βββ __init__.py
βββ models.py # ma'lumotlar modellari (05-bob)
βββ tests.py # testlar
βββ views.py # VIEW funksiyalari β bu bobning markazi
Bu bobda bizni asosan views.py va o'zimiz yaratadigan urls.py qiziqtiradi.
Ilovani ro'yxatdan o'tkazish¶
Django ilovani avtomatik ko'rmaydi β uni settings.py dagi INSTALLED_APPS ro'yxatiga qo'shish kerak:
# mysite/settings.py
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"blog", # <-- yangi ilovamiz
]
apps.py ichidagi BlogConfig ga to'liq yo'l bilan ham yozish mumkin ("blog.apps.BlogConfig"), lekin oddiy "blog" Django 6.0 da yetarli β u o'zi apps.py dagi yagona AppConfig ni topadi.
Eslatma β
DEFAULT_AUTO_FIELD: Django 6.0 da yangi modellar uchun standart birlamchi kalit turiBigAutoFieldhisoblanadi. Bu Django'ning global standarti (django.conf.global_settings) β eski versiyalardagidekstartprojectsettings.pyga alohidaDEFAULT_AUTO_FIELDqatorini yozmaydi, vaapps.pyga hamdefault_auto_fieldatributi qo'shilmaydi. Kerak bo'lsa unisettings.pydaDEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"bilan oshkora belgilash mumkin (05-bobda muhim bo'ladi).
Birinchi view: HttpResponse¶
View β bu birinchi argumenti request bo'lgan oddiy funksiya. Eng sodda view oddiy matn qaytaradi:
# blog/views.py
from django.http import HttpResponse
def home(request):
return HttpResponse("Salom, Django!")
HttpResponse brauzerga matn (yoki HTML) ni 200 OK holati bilan qaytaradi. Ichiga to'g'ridan-to'g'ri HTML ham yozish mumkin:
# blog/views.py
def about(request):
matn = "<h1>Biz haqimizda</h1><p>Bu oddiy sahifa.</p>"
return HttpResponse(matn)
Bu yerda HTML'ni qo'lda yozyapmiz β bu faqat o'rganish uchun. Haqiqiy loyihada HTML'ni 04-bobdagi template tizimi orqali yozasiz. Hozircha view qanday ishlashini ko'rishimiz uchun shu yetarli.
HttpResponse ning ko'p ishlatiladigan vorislari ham bor:
| Klass | Holat kodi | Qachon |
|---|---|---|
HttpResponse |
200 | Oddiy javob |
HttpResponseNotFound |
404 | Topilmadi |
HttpResponseRedirect |
302 | Boshqa manzilga yo'naltirish |
HttpResponseBadRequest |
400 | Noto'g'ri so'rov |
HttpResponseNotAllowed |
405 | Bu HTTP metod ruxsat etilmagan |
HttpResponseForbidden |
403 | Taqiqlangan |
JsonResponse: JSON qaytarish (API uchun)¶
Hozir ko'p ilovalar JSON qaytaradi β bu API'lar va frontend (React/Vue) bilan ishlashning asosi. JsonResponse Python lug'atini (dict) avtomatik JSON'ga aylantiradi va Content-Type: application/json sarlavhasini qo'yadi:
# blog/views.py
from django.http import JsonResponse
def api_holat(request):
return JsonResponse({
"status": "ok",
"versiya": "6.0.6",
"til": "uz",
})
Brauzerda /api/holat/ ni ochsangiz, natija:
Standart holatda JsonResponse faqat lug'atni (dict) qabul qiladi β bu xavfsizlik chorasi (eski brauzerlardagi JSON massiv zaifligidan himoya). Agar ro'yxat (list) qaytarmoqchi bo'lsangiz, safe=False ni aniq berishingiz kerak:
# blog/views.py
def royxat(request):
sonlar = [1, 2, 3]
return JsonResponse(sonlar, safe=False) # safe=False shart, aks holda TypeError
# β XATO β list bilan safe=False bermasak, TypeError ko'tariladi:
def royxat_xato(request):
return JsonResponse([1, 2, 3]) # TypeError: In order to allow non-dict objects ...
JSON API'larni keyinchalik Django REST Framework (DRF) bilan ancha qulay quramiz.
JsonResponseesa Django'ning o'zidagi eng past darajadagi vosita β uni bilish foydali.
urls.py: marshrutni path() bilan ulash¶
View yozdik, lekin uni biror URL'ga bog'lamaguncha hech qanday so'rov unga yetib bormaydi. Buning uchun ilovamizda urls.py faylini yaratamiz (startapp uni avtomatik yaratmaydi β qo'lda yaratamiz):
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.home, name="home"),
path("about/", views.about, name="about"),
path("api/holat/", views.api_holat, name="api-holat"),
path("api/royxat/", views.royxat, name="api-royxat"),
]
path() uchta narsani oladi:
- Marshrut shabloni (route) β
"about/"kabi URL qismi. Boshida/qo'yilmaydi. - View β chaqiriladigan funksiya (
views.homeβ qavssiz, ya'ni funksiyaning o'zi, uni chaqirmaymiz). nameβ bu marshrutning nomi (keyinroqreverse()uchun zarur).
Django 6.0 idiomi:
path()ishlatiladi. Eskiurl()o'chirib tashlangan. Murakkab regulyar ifoda kerak bo'lsaginare_path()ishlatiladi (pastda ko'ramiz).
Loyiha URLconf'iga ulash: include()¶
blog/urls.py ni yaratdik, lekin Django avval loyiha URLconf'ini (mysite/urls.py) o'qiydi. Ilova URL'larini loyihaga include() orqali ulaymiz:
# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("blog.urls")), # blog ilovasining hamma URL'lari shu yerga ulanadi
]
include("blog.urls") β bu "URL'ni shu nuqtadan boshlab blog/urls.py ga uzat" degani. Agar bu yerda path("blog/", include("blog.urls")) yozsak, blog URL'lari /blog/about/, /blog/api/holat/ ko'rinishini olardi.
Endi serverni ishga tushiramiz:
Brauzerda:
http://127.0.0.1:8000/->Salom, Django!http://127.0.0.1:8000/about/-> "Biz haqimizda" sahifasihttp://127.0.0.1:8000/api/holat/-> JSON javob
include()qanday ishlaydi? Django root URLconf'da mos prefiksni (masalan"blog/") topgach, o'sha qismni URL'dan olib tashlaydi va qolgan qismni ichki URLconf'ga uzatadi. Shuning uchunblog/urls.pyichidagi shablonlar"blog/"siz, faqat o'z qismidan boshlab yoziladi.
Dinamik segmentlar: URL'dan qiymat olish¶
Eng kuchli xususiyat β URL ichidan qiymat ushlab olish. /maqola/42/ so'rovida 42 raqamini view'ga uzatmoqchimiz. Buning uchun <konvertor:nom> sintaksisidan foydalanamiz:
# blog/urls.py
urlpatterns = [
# ... oldingilar ...
path("maqola/<int:id>/", views.maqola_detali, name="maqola-detali"),
path("post/<slug:slug>/", views.maqola_slug, name="maqola-slug"),
path("arxiv/<int:yil>/<int:oy>/", views.yil_oy, name="yil-oy"),
]
View'lar ushlab olingan qiymatni nomli argument sifatida qabul qiladi (nom URL'dagi nom bilan bir xil bo'lishi shart):
# blog/views.py
def maqola_detali(request, id):
return HttpResponse(f"Maqola raqami: {id} (turi: {type(id).__name__})")
def maqola_slug(request, slug):
return HttpResponse(f"Maqola slugi: {slug}")
def yil_oy(request, yil, oy):
return HttpResponse(f"{yil}-yil, {oy}-oy maqolalari")
Natijalar:
/maqola/42/->Maqola raqami: 42 (turi: int)β diqqat:idinteger turida keladi, string emas!/post/django-darslari/->Maqola slugi: django-darslari/arxiv/2026/6/->2026-yil, 6-oy maqolalari/maqola/abc/-> 404 β chunki<int:>faqat raqamga mos keladi,abcmos kelmaydi.
Konvertor turlari (path converters)¶
Django'ning o'rnatilgan konvertorlari:
| Konvertor | Nimaga mos keladi | Python turi |
|---|---|---|
str |
/ dan boshqa har qanday belgi (standart) |
str |
int |
manfiy bo'lmagan butun son | int |
slug |
harf, raqam, -, _ (masalan salom-dunyo) |
str |
uuid |
UUID (masalan 12345678-1234-...) |
uuid.UUID |
path |
har qanday belgi, shu jumladan / |
str |
str standart konvertor β agar konvertor yozmasangiz (<nom>), str ishlatiladi. path konvertori / ni ham qamrab oladi, shuning uchun u butun yo'lni ushlaydi:
# /fayl/rasmlar/2026/logo.png/ -> {"toliq": "rasmlar/2026/logo.png"}
path("fayl/<path:toliq>/", views.fayl_korsat, name="fayl"),
O'z konvertoringizni yozish¶
Agar standart konvertorlar yetmasa, o'zingiznikini yozasiz. Masalan, ikki xonali yilni (26) ushlab, intga aylantiramiz:
# blog/converters.py
class TwoDigitYearConverter:
regex = "[0-9]{2}" # mos keladigan naqsh
def to_python(self, value): # URL -> Python qiymat
return int(value)
def to_url(self, value): # Python qiymat -> URL (reverse uchun)
return "%02d" % value
# blog/urls.py
from django.urls import path, register_converter
from . import converters, views
register_converter(converters.TwoDigitYearConverter, "yy")
urlpatterns = [
path("yil/<yy:yil>/", views.yil_korsat, name="yil"),
# ...
]
Endi /yil/26/ URL'i view'ga yil=26 (integer) uzatadi.
re_path(): regulyar ifoda kerak bo'lganda¶
Standart path() aksariyat holatlarda yetadi. Lekin murakkab naqsh kerak bo'lsa, re_path() ishlatiladi:
# blog/urls.py
from django.urls import re_path
urlpatterns = [
# 4 xonali yil, faqat 2 raqamli oy:
re_path(r"^arxiv/(?P<yil>[0-9]{4})/(?P<oy>[0-9]{2})/$", views.yil_oy, name="re-arxiv"),
]
Imkon qadar
path()ni afzal ko'ring β u o'qishga osonroq va xatosi kamroq.re_path()ni faqat haqiqatan kerak bo'lganda ishlating.
HTTP metodlari: GET, POST va boshqalar¶
Har bir so'rovning HTTP metodi bor: GET (ma'lumot olish), POST (yangi ma'lumot yuborish), PUT, PATCH, DELETE va h.k. View ichida metodni request.method orqali tekshiramiz:
# blog/views.py
from django.http import HttpResponse, HttpResponseNotAllowed
def metod_korsat(request):
if request.method == "GET":
return HttpResponse("Bu GET so'rovi")
elif request.method == "POST":
return HttpResponse("Bu POST so'rovi")
else:
return HttpResponseNotAllowed(["GET", "POST"]) # 405 + Allow sarlavhasi
HttpResponseNotAllowed(["GET", "POST"]) 405 Method Not Allowed holatini va Allow: GET, POST sarlavhasini qaytaradi β bu HTTP standartiga to'g'ri yondashuv.
Muhim β CSRF himoyasi: Haqiqiy brauzerdan (yoki
curl -X POST)POSTyuborsangiz, Django standart holatda403 Forbidden(CSRF verification failed) qaytaradi. Buning sababi βPOSTso'rovlar uchun CSRF token talab qilinadi (xavfsizlik uchun). HTML formalarida{% csrf_token %}(04-bob), API'larda esa boshqa usullar ishlatiladi. Quyidagi test misollarida biz Django'ning test client idan foydalanamiz β u testlarda CSRF tekshiruvini o'chiradi, shuning uchunPOSTmuammosiz ishlaydi.Har bir metod uchun alohida
ifyozish zerikarli. Keyinroq klassga asoslangan view (CBV) va DRF buni ancha soddalashtiradi. Hozircha asosni tushunamiz.
Nomli URL: reverse() va {% url %}¶
Eng muhim prinsip: manzilni hech qachon qo'lda yozmang. Agar kodingizda /maqola/42/ deb yozsangiz va keyin URL'ni /post/42/ ga o'zgartirsangiz β butun loyiha bo'ylab qidirib tuzatishingizga to'g'ri keladi. Buning o'rniga marshrutga nom beramiz va shu nom orqali manzil quramiz.
Biz allaqachon path(..., name="maqola-detali") deb nom berdik. Endi app_name qo'shsak, nom namespace (fazo nomi) bilan to'liq bo'ladi:
# blog/urls.py
app_name = "blog" # namespace
urlpatterns = [
path("", views.home, name="home"),
path("maqola/<int:id>/", views.maqola_detali, name="maqola-detali"),
# ...
]
Endi to'liq nom "blog:maqola-detali" bo'ladi (namespace:name ko'rinishida). Bu turli ilovalarda bir xil nom (detail kabi) ishlatilsa ham to'qnashuv bo'lmasligini ta'minlaydi.
Python kodida: reverse()¶
# blog/views.py
from django.urls import reverse
from django.http import HttpResponse
def boshqa_sahifaga(request):
manzil = reverse("blog:maqola-detali", kwargs={"id": 42})
return HttpResponse(f"Manzil: {manzil}") # Manzil: /maqola/42/
reverse() ni args (pozitsion) bilan ham chaqirish mumkin:
reverse("blog:yil-oy", args=[2026, 6]) # -> /arxiv/2026/6/
reverse("blog:yil-oy", kwargs={"yil": 2026, "oy": 6}) # -> /arxiv/2026/6/ (bir xil)
Yo'naltirish: redirect()¶
redirect() ham nomdan foydalanadi β manzilni qo'lda yozmaymiz:
# blog/views.py
from django.shortcuts import redirect
def yonaltir(request):
return redirect("blog:home") # 302 -> Location: /
Template ichida: {% url %}¶
HTML template ichida ham xuddi shunday β nom orqali (04-bobda chuqurroq):
<a href="{% url 'blog:about' %}">Biz haqimizda</a>
<a href="{% url 'blog:maqola-detali' id=42 %}">42-maqola</a>
Bu render bo'lganda:
Node.js bilan solishtirsangiz (Node.js qo'llanmasi): Express'da
app.get("/maqola/:id", ...)yozasiz, lekin u yerda nomli marshrut vareverse()o'rnatilgan emas. Django'ning nomli URL tizimi β katta loyihalarda manzillarni boshqarishning eng kuchli vositalaridan biri.
Hammasini birga: to'liq misol va test¶
Yuqoridagi hamma narsa Django 6.0.6 da ishlashini tasdiqlash uchun blog/tests.py ga test yozamiz. Django testlar uchun test client beradi β bu so'rov yuborib, javobni tekshiradigan soxta brauzer:
# blog/tests.py
from django.test import TestCase
from django.urls import reverse
class URLTest(TestCase):
def test_home(self):
r = self.client.get(reverse("blog:home"))
self.assertEqual(r.status_code, 200)
self.assertContains(r, "Salom")
def test_dinamik_int(self):
r = self.client.get("/maqola/42/")
self.assertEqual(r.status_code, 200)
self.assertContains(r, "42")
def test_int_404(self):
# <int:> faqat raqamga mos keladi -> abc 404 beradi
self.assertEqual(self.client.get("/maqola/abc/").status_code, 404)
def test_metod_405(self):
# metod_korsat faqat GET/POST qabul qiladi
self.assertEqual(self.client.delete("/metod/").status_code, 405)
def test_json(self):
r = self.client.get("/api/holat/")
self.assertEqual(r["Content-Type"], "application/json")
self.assertEqual(r.json()["status"], "ok")
Testlarni ishga tushiramiz:
Natija:
Found 5 test(s).
Creating test database for alias 'default'...
.....
----------------------------------------------------------------------
Ran 5 tests in 0.01s
OK
assertContainsjavob ichida matn borligini,r.json()esa JSON javobni Python dict'ga aylantirib tekshiradi. Test client'i CSRF tekshiruvini o'chiradi β shuning uchunPOST/DELETEmuammosiz ishlaydi. Testlar β Django'ning eng kuchli tomonlaridan, ularni keyingi boblarda chuqurroq ishlatamiz.
Bobning xulosasi¶
startappilova yaratadi; uniINSTALLED_APPSga qo'shish shart.- View β
requestni olib,HttpResponseqaytaradigan funksiya.JsonResponseJSON qaytaradi (listuchunsafe=False). path()marshrutni view'ga bog'laydi;include()loyiha URL'larini ilovalarga taqsimlaydi.- Dinamik segmentlar (
<int:id>,<slug:slug>,<path:>,<uuid:>) URL'dan qiymat ushlaydi va view'ga nomli argument sifatida uzatadi; konvertor turni avtomatik aylantiradi. app_name+name-> nomli URL (namespace:name);reverse()(Python) va{% url %}(template) manzilni nom orqali quradi β qo'lda yozmang.request.methodHTTP metodini tekshiradi;redirect()yo'naltiradi.
Keyingi bobda HTML'ni qo'lda yozishni tashlab, Django Template Language (DTL) bilan chiroyli sahifalar quramiz.
Mashqlar¶
Oson¶
contactilovasinistartappbilan yarating vaINSTALLED_APPSga qo'shing.python manage.py checkxatosiz o'tishini tekshiring.salom(request)view yozing β uHttpResponse("Assalomu alaykum!")qaytarsin va uni/salom/URL'iga ulang (nom:salom).vaqt(request)view yozing β u joriy yilniJsonResponse({"yil": 2026})ko'rinishida qaytarsin./foydalanuvchi/<int:user_id>/marshrutini yarating; viewuser_idni va uning turini (type().__name__) qaytarsin.path("post/<slug:slug>/", ...)marshrutini yozing va/post/mening-birinchi-maqolam/so'rovidaslugqiymati qaytarilishini tekshiring.- Ikkita view (
home,about) ni nomli qiling vareverse()orqali ularning manzilini Python shell'da (python manage.py shell) chop eting.
O'rta¶
<str:>va<path:>farqini ko'rsating:/yo1/<str:x>/va/yo2/<path:x>/marshrutlarini yarating,/yo1/a/b/va/yo2/a/b/so'rovlarida qaysi biri mos kelishini izohlang.metod_korsatga o'xshash view yozing, lekin u faqatPOSTni qabul qilsin; boshqa metodlardaHttpResponseNotAllowed(["POST"])qaytarsin. Test client bilanGET405 berishini tasdiqlang.blog/urls.pyniapp_name = "blog"bilan jihozlang. So'ngreverse("blog:maqola-detali", kwargs={"id": 7})/maqola/7/berishini test yozib tasdiqlang.JsonResponsebilan ro'yxat (list) qaytarishga harakat qilingsafe=Falsesiz β qanday xato chiqadi? Keyinsafe=Falseqo'shib to'g'rilang.redirect()ishlatuvchi view yozing β/eski/so'rovini/yangi/ga (nom orqali) yo'naltirsin. Test clientfollow=Falsebilan 302 vaLocationsarlavhasini tekshiring.- Loyiha
mysite/urls.pydapath("blog/", include("blog.urls"))qiling. Endi barcha blog URL'lari oldiga/blog/qo'shilishini vareverse("blog:home")/blog/berishini tasdiqlang.
Qiyin¶
- O'z konvertoringizni yozing:
MonthConverterβ faqat01-12orasidagi ikki xonali oyni qabul qilsin (regex = "0[1-9]|1[0-2]"),to_pythondaintqaytarsin./oy/<month:m>/marshrutida/oy/13/404,/oy/06/200 berishini test bilan tasdiqlang. re_path()bilan/yil/2026/(4 xonali yil) marshrutini yozing va/yil/26/404 berishini tasdiqlang. Keyin shu marshrutnipath()+ o'z konvertoringiz bilan qayta yozing.- Bir nechta ilova (
blog,shop) yarating, ikkalasida hamname="detail"bo'lgan marshrut bo'lsin.app_nameorqali namespace bering vareverse("blog:detail", ...)bilanreverse("shop:detail", ...)to'g'ri ajralishini test bilan ko'rsating.
Yechimlar
1-mashq:
2-mashq:
3-mashq:
4-mashq:
# blog/views.py
def foydalanuvchi(request, user_id):
return HttpResponse(f"user_id={user_id}, turi={type(user_id).__name__}")
/foydalanuvchi/5/ -> user_id=5, turi=int.
5-mashq:
/post/mening-birinchi-maqolam/ -> slug=mening-birinchi-maqolam.
6-mashq:
>>> from django.urls import reverse
>>> reverse("blog:home")
'/'
>>> reverse("blog:about")
'/about/'
7-mashq:
# blog/urls.py
path("yo1/<str:x>/", views.yo1, name="yo1"),
path("yo2/<path:x>/", views.yo2, name="yo2"),
# blog/views.py
def yo1(request, x):
return HttpResponse(f"str: {x}")
def yo2(request, x):
return HttpResponse(f"path: {x}")
/yo1/a/b/ -> 404, chunki <str:> / ni qamramaydi, shuning uchun a/b ga mos kelmaydi.
- /yo2/a/b/ -> 200, x = "a/b" β <path:> / ni ham qamraydi.
8-mashq:
# blog/views.py
from django.http import HttpResponseNotAllowed
def faqat_post(request):
if request.method == "POST":
return HttpResponse("POST qabul qilindi")
return HttpResponseNotAllowed(["POST"])
# blog/tests.py
def test_faqat_post(self):
self.assertEqual(self.client.get("/faqat-post/").status_code, 405)
self.assertEqual(self.client.post("/faqat-post/").status_code, 200)
9-mashq:
# blog/urls.py
app_name = "blog"
urlpatterns = [
path("maqola/<int:id>/", views.maqola_detali, name="maqola-detali"),
# ...
]
# blog/tests.py
from django.urls import reverse
def test_reverse(self):
self.assertEqual(reverse("blog:maqola-detali", kwargs={"id": 7}), "/maqola/7/")
10-mashq:
# β Bu xato beradi:
JsonResponse([1, 2, 3])
# TypeError: In order to allow non-dict objects to be serialized
# set the safe parameter to False.
# β
To'g'risi:
JsonResponse([1, 2, 3], safe=False)
11-mashq:
# blog/views.py
from django.shortcuts import redirect
def eski(request):
return redirect("blog:yangi")
def yangi(request):
return HttpResponse("Yangi sahifa")
# blog/tests.py
def test_redirect(self):
r = self.client.get("/eski/")
self.assertEqual(r.status_code, 302)
self.assertEqual(r["Location"], "/yangi/")
12-mashq:
# mysite/urls.py
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
]
# blog/tests.py
def test_prefix(self):
self.assertEqual(reverse("blog:home"), "/blog/")
self.assertEqual(self.client.get("/blog/about/").status_code, 200)
/blog/... bilan boshlanadi; reverse() buni avtomatik hisobga oladi β kodni o'zgartirmaymiz.
13-mashq:
# blog/converters.py
class MonthConverter:
regex = "0[1-9]|1[0-2]"
def to_python(self, value):
return int(value)
def to_url(self, value):
return "%02d" % value
# blog/urls.py
from django.urls import path, register_converter
from . import converters, views
register_converter(converters.MonthConverter, "month")
urlpatterns = [
path("oy/<month:m>/", views.oy_view, name="oy"),
# ...
]
# blog/tests.py
def test_month_converter(self):
self.assertEqual(self.client.get("/oy/13/").status_code, 404)
r = self.client.get("/oy/06/")
self.assertEqual(r.status_code, 200)
self.assertContains(r, "oy=6") # to_python int qaytargani uchun "06" -> 6
14-mashq:
# re_path bilan:
from django.urls import re_path
re_path(r"^yil/(?P<yil>[0-9]{4})/$", views.yil_view, name="yil-re"),
# blog/tests.py
def test_yil4(self):
self.assertEqual(self.client.get("/yil/26/").status_code, 404) # 2 xonali mos emas
self.assertEqual(self.client.get("/yil/2026/").status_code, 200)
path() + konvertor bilan:
# blog/converters.py
class FourDigitYearConverter:
regex = "[0-9]{4}"
def to_python(self, value):
return int(value)
def to_url(self, value):
return "%04d" % value
# blog/urls.py
register_converter(converters.FourDigitYearConverter, "yyyy")
path("yil/<yyyy:yil>/", views.yil_view, name="yil"),
15-mashq:
# blog/urls.py
app_name = "blog"
urlpatterns = [path("maqola/<int:id>/", views.detail, name="detail")]
# shop/urls.py
app_name = "shop"
urlpatterns = [path("mahsulot/<int:id>/", views.detail, name="detail")]
# mysite/urls.py
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
path("shop/", include("shop.urls")),
]
# test
def test_namespace(self):
self.assertEqual(reverse("blog:detail", kwargs={"id": 1}), "/blog/maqola/1/")
self.assertEqual(reverse("shop:detail", kwargs={"id": 1}), "/shop/mahsulot/1/")
app_name (namespace) tufayli bir xil name="detail" ikki ilovada to'qnashmaydi.
β¬ οΈ Oldingi: 02 β O'rnatish va birinchi loyiha Β· π README Β· Keyingi: 04 β Template tizimi (DTL) β‘οΈ