08 β Admin panel¶
β¬ οΈ Oldingi: 07 β Model munosabatlari Β· π README Β· Keyingi: 09 β Ilg'or ORM va optimizatsiya β‘οΈ
Bu bobda: Django'ning eng mashhur "sehri" β admin panel bilan tanishamiz. Bironta HTML, CSS yoki JavaScript yozmasdan, faqat bir necha qator Python kodi bilan ma'lumotlarni qo'shish, ko'rish, tahrirlash va o'chirish uchun to'liq boshqaruv interfeysini olamiz. Avval
createsuperuserbilan admin foydalanuvchi yaratamiz va/admin/ga kiramiz. So'ng modellarniadmin.site.registerbilan ro'yxatdan o'tkazamiz. KeyinModelAdminsinfi orqali panelni sozlaymiz: ro'yxat sahifasidagi ustunlar (list_display), o'ng paneldagi filtrlar (list_filter), qidiruv qutisi (search_fields), tartiblash (ordering), ro'yxatda to'g'ridan-to'g'ri tahrirlash (list_editable). Forma sahifasini chiroyli guruhlarga ajratamiz (fieldsets), ba'zi maydonlarni faqat o'qish uchun qilamiz (readonly_fields). Bog'liq yozuvlarni ota model formasi ichida tahrirlashni o'rganamiz (TabularInlinevaStackedInline). Bir vaqtning o'zida yuzlab yozuvga ta'sir qiladigan admin actions yozamiz. Oxirida@admin.displaydekoratori bilan maxsus ustunlar vaadmin.sitesozlamalari. Hamma kod Django 6.0.6 da haqiqatan ishga tushirib tekshirilgan.
Nega admin panel β Django'ning sovg'asi¶
Tasavvur qiling: blog sayti qildingiz. Maqola qo'shish kerak. Maqola modelingiz bor, lekin uni baza ichiga qanday kiritasiz? Qo'lda SQL INSERT yozasizmi? Yangi maqola formasi, ro'yxat sahifasi, "o'chirish" tugmasi β bularning hammasini noldan yozasizmi?
Aksariyat veb-freymvorklarda aynan shunday β boshqaruv panelini o'zingiz qurasiz. Django esa boshqacha yo'l tutadi: u modellaringizni o'qiydi va ular asosida to'liq ishlaydigan admin interfeysini avtomatik yaratadi. Maydonlaringiz qanday bo'lsa β matn qutisi, sana tanlagich, ochiluvchi ro'yxat (ForeignKey uchun) β Django mosini tanlaydi.
Bu shunchaki demo emas. Django admin paneli β kuchli, xavfsiz va kengaytiriladigan vosita. Ko'p loyihalarda kontent muharrirlari, moderatorlar va menejerlar aynan shu panel bilan ishlaydi. Sayt foydalanuvchilari ko'radigan jamoatchilik yuzini (frontend) siz alohida qurasiz, lekin ichki boshqaruv uchun admin panel ko'pincha yetarli.
Python eslatma. Bu bob to'liq Python sinflari va dekoratorlariga tayanadi. Agar
class, meros (inheritance) yoki@dekoratorsintaksisi yodingizdan ko'tarilgan bo'lsa, Python qo'llanmasiga bir nazar tashlang. Django'ga xos hamma narsani esa shu yerda to'liq tushuntiramiz.
Bu bobda quyidagi modellar bilan ishlaymiz (blog/models.py). Bular 07-bobdagi munosabatlar bilimini davom ettiradi: bitta Muallif ko'p Maqola yozadi, har Maqola ko'p Izohga ega.
# blog/models.py
from django.db import models
class Muallif(models.Model):
ism = models.CharField(max_length=100)
email = models.EmailField(blank=True)
def __str__(self):
return self.ism
class Kategoriya(models.Model):
nom = models.CharField(max_length=50)
class Meta:
verbose_name_plural = "Kategoriyalar"
def __str__(self):
return self.nom
class Maqola(models.Model):
HOLAT = [
("qoralama", "Qoralama"),
("nashr", "Nashr qilingan"),
]
sarlavha = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, blank=True)
muallif = models.ForeignKey(Muallif, on_delete=models.CASCADE, related_name="maqolalar")
kategoriya = models.ForeignKey(Kategoriya, on_delete=models.SET_NULL, null=True, blank=True)
matn = models.TextField()
holat = models.CharField(max_length=10, choices=HOLAT, default="qoralama")
korishlar = models.IntegerField(default=0)
yaratilgan = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.sarlavha
class Izoh(models.Model):
maqola = models.ForeignKey(Maqola, on_delete=models.CASCADE, related_name="izohlar")
muallif_ism = models.CharField(max_length=100)
matn = models.TextField()
tasdiqlangan = models.BooleanField(default=False)
def __str__(self):
return f"{self.muallif_ism}: {self.matn[:30]}"
__str__haqida muhim eslatma. Har modelda__str__metodi bo'lishi admin panel uchun juda muhim. Aks holda admin har bir yozuvniMaqola object (1)ko'rinishida ko'rsatadi β foydasiz.__str__qaytargan matn esa odam o'qiy oladigan nom bo'ladi. Bu kichik metod butun panel ko'rinishini yaxshilaydi.
1-qadam: superuser yaratish va /admin/ ga kirish¶
Admin panelga kirish uchun avval superuser (cheksiz huquqli foydalanuvchi) kerak. Buni createsuperuser buyrug'i yaratadi. Migratsiyalar bajarilgan bo'lishi shart (admin va auth jadvallari kerak):
Buyruq sizdan foydalanuvchi nomi, email (ixtiyoriy) va parol so'raydi. Parolni ikki marta kiritasiz (ekranda ko'rinmaydi β bu normal):
Username: admin
Email address: admin@example.uz
Password:
Password (again):
Superuser created successfully.
So'ng serverni ishga tushirib, brauzerda http://127.0.0.1:8000/admin/ ga kiring:
Eslatma.
runserverβ bu ishlab chiqish (development) serveri, ishlab chiqarish (production) uchun emas. Uni deploy bobida git-github va keyingi boblarda gunicorn/uvicorn bilan almashtiramiz. Hozircha mahalliy ishlash uchun ideal.
admin/ URL'i loyiha yaratilganda urls.py ga avtomatik qo'shilgan bo'ladi:
# mysite/urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path("admin/", admin.site.urls),
]
Skript orqali superuser (CI uchun)¶
Interaktiv so'roq CI/CD yoki Docker'da ishlamaydi. Shunda --noinput bayrog'i va atrof-muhit o'zgaruvchilaridan foydalaning. Parolni DJANGO_SUPERUSER_PASSWORD ga qo'yasiz:
# Linux/macOS
DJANGO_SUPERUSER_PASSWORD=parol12345 python manage.py createsuperuser \
--noinput --username admin --email admin@example.uz
# Windows PowerShell
$env:DJANGO_SUPERUSER_PASSWORD = "parol12345"
python manage.py createsuperuser --noinput --username admin --email admin@example.uz
Bu usul aynan shu bobni tekshirishda ishlatildi va Superuser created successfully. natijasini berdi.
Xavfsizlik. Haqiqiy parolni hech qachon kodga yoki Git'ga yozmang. Atrof-muhit o'zgaruvchisi (
.envfayl, deploy platformasi sirlari) orqali bering. Bu naqsh deploy bosqichida muhim bo'ladi.
2-qadam: modelni ro'yxatdan o'tkazish β admin.site.register¶
Yangi superuser bilan /admin/ ga kirsangiz, faqat Users va Groups ko'rasiz. Sizning Maqola, Muallif modellaringiz yo'q β chunki ularni hali admin'ga e'lon qilmadingiz.
Buni blog/admin.py faylida qilamiz. Eng oddiy ro'yxatdan o'tkazish:
# blog/admin.py
from django.contrib import admin
from .models import Muallif, Kategoriya, Maqola, Izoh
admin.site.register(Muallif)
admin.site.register(Kategoriya)
admin.site.register(Maqola)
admin.site.register(Izoh)
Endi sahifani yangilang β to'rt model ham paydo bo'ldi. Har biriga bosib, yozuv qo'shishingiz, tahrirlashingiz, o'chirishingiz mumkin. Hech qanday HTML yozmadingiz β Django modellardan formani avtomatik qurdi.
Bu qanday ishlaydi? Django ishga tushganda har bir ilovaning
admin.pyfaylini avtomatik import qiladi (django.contrib.adminningautodiscovermexanizmi). Shu import paytidaregisterchaqiruvlari ishga tushadi va modellar globaladmin.siteobyektiga qo'shiladi. Shuning uchun sozlamalaringiz aynanadmin.pyda bo'lishi kerak.
Hozircha hammasi standart. Maqolalar ro'yxati __str__ natijasini bitta ustunda ko'rsatadi, filtrlar va qidiruv yo'q. Buni yaxshilash uchun ModelAdmin kerak.
ModelAdmin β panelni sozlash markazi¶
admin.site.register(Maqola) deganda Django ichki sukut bo'yicha ModelAdmin sinfini ishlatadi. Panelni sozlash uchun shu sinfdan meros olib, o'z sozlamalaringizni yozasiz.
Ikki yozish uslubi bor. Birinchisi β register ga ikkinchi argument sifatida sinfni berish:
# blog/admin.py
from django.contrib import admin
from .models import Maqola
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat", "korishlar"]
admin.site.register(Maqola, MaqolaAdmin)
Ikkinchisi β dekorator uslubi. Bu zamonaviy va toza ko'rinadi, shuning uchun bu bobda asosan shuni ishlatamiz:
# blog/admin.py
from django.contrib import admin
from .models import Maqola
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat", "korishlar"]
Ikkalasi ham bir xil ishlaydi. @admin.register(Maqola) ostidagi sinfni avtomatik Maqola modeli uchun ro'yxatdan o'tkazadi. Endi ModelAdmin ning asosiy sozlamalarini birma-bir ko'rib chiqamiz.
list_display β ro'yxat sahifasidagi ustunlar¶
Sukut bo'yicha ro'yxat (changelist) sahifasi faqat __str__ ni bitta ustunda ko'rsatadi. list_display esa qaysi maydonlar ustun sifatida ko'rinishini belgilaydi:
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "holat", "korishlar", "yaratilgan"]
Endi maqolalar ro'yxati jadval ko'rinishida β har maqola uchun sarlavha, muallif, holat, ko'rishlar soni va yaratilgan sana ustunlari. Ustun sarlavhasiga bosish bilan o'sha ustun bo'yicha tartiblash ham mumkin.
Diqqat.
list_displaygaManyToManyFieldni to'g'ridan-to'g'ri qo'sha olmaysiz (chunki bir yozuv ko'p qiymatga ega) β Django xato beradi. Bunday holatda maxsus metod yozasiz (pastda@admin.displayqismida ko'ramiz).ForeignKeyesa (masalanmuallif) muammosiz ishlaydi β u modelning__str__ini ko'rsatadi.
list_filter β o'ng paneldagi filtrlar¶
Yuzlab maqola orasidan faqat "nashr qilingan"larini ko'rmoqchimisiz? list_filter o'ng tomonda filtr panelini paydo qiladi:
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "holat", "korishlar", "yaratilgan"]
list_filter = ["holat", "kategoriya", "yaratilgan"]
Endi o'ng panelda uch filtr:
- holat β
choicesborligi uchun "Qoralama" / "Nashr qilingan" tugmalari. - kategoriya β
ForeignKeyborligi uchun mavjud kategoriyalar ro'yxati. - yaratilgan β sana maydoni borligi uchun "Bugun", "So'nggi 7 kun", "Shu oy" kabi aqlli vaqt filtrlar.
Django maydon turiga qarab mos filtr widget'ini o'zi tanlaydi. Bu kichik qator katta qulaylik beradi.
search_fields β qidiruv qutisi¶
search_fields ro'yxat sahifasining yuqorisida qidiruv qutisini paydo qiladi va qaysi maydonlar bo'yicha qidirilishini belgilaydi:
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "holat", "korishlar", "yaratilgan"]
list_filter = ["holat", "kategoriya", "yaratilgan"]
search_fields = ["sarlavha", "matn"]
Endi qidiruv qutisiga "Django" yozsangiz, sarlavhasi yoki matni ichida "Django" bor maqolalar chiqadi (katta-kichik harf farqsiz, qisman moslik β ya'ni icontains).
Bog'liq model ichida ham qidirish mumkin β ikki pastki chiziq (__) bilan munosabatga kirasiz. Masalan muallif ismi bo'yicha qidirish:
Texnik nozik.
muallif__ismβ bu 07-bobdagi munosabatlar sintaksisining o'zi. Django bu so'rovni baza darajasidaJOINga aylantiradi. SQL'da bu qanday ishlashini bilmoqchi bo'lsangiz, SQL qo'llanmaga qarang. Ko'p maydonli qidiruv katta jadvallarda sekin bo'lishi mumkin β production'da indekslar yoki to'liq matnli qidiruv (PostgreSQL) kerak bo'ladi.
ordering va list_editable¶
ordering ro'yxat sukut bo'yicha qanday tartibda chiqishini belgilaydi. Minus belgisi (-) teskari tartib (kattadan kichikka):
list_editable esa bir ajoyib imkoniyat: ro'yxat sahifasidan chiqmasdan, to'g'ridan-to'g'ri ustunni tahrirlash. Masalan har maqolaning holatini bittalab ochmasdan o'zgartirish:
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "holat", "korishlar", "yaratilgan"]
list_filter = ["holat", "kategoriya", "yaratilgan"]
search_fields = ["sarlavha", "matn"]
ordering = ["-yaratilgan"]
list_editable = ["holat"]
Endi holat ustuni ochiluvchi ro'yxatga aylanadi β bir necha maqolaning holatini o'zgartirib, pastdagi "Saqlash" tugmasini bossangiz, hammasi bir zumda yangilanadi.
Muhim qoida (xato manbai).
list_editabledagi maydonlist_displayda ham bo'lishi shart. Bundan tashqari,list_displayning birinchi elementilist_editableda bo'lmasligi kerak β birinchi ustun yozuv sahifasiga havola bo'lib qoladi va tahrirlash bilan to'qnashadi. Yuqoridasarlavhabirinchi (havola),holatesa tahrirlanadi β to'g'ri naqsh. Aks holda (birinchi ustunlist_editableda bo'lsa) Django ishga tushishda aniqadmin.E124xatosini beradi.
# β XATO: birinchi ustun list_editable da bo'lishi mumkin emas
@admin.register(Maqola)
class XatoAdmin(admin.ModelAdmin):
list_display = ["holat", "sarlavha"]
list_editable = ["holat"] # birinchi ustun -> admin.E124 xatosi
fieldsets β forma sahifasini guruhlash¶
Hozirgacha ro'yxat sahifasi haqida gaplashdik. Endi bitta yozuvga bosganda ochiladigan forma (change) sahifasini sozlaymiz.
Sukut bo'yicha forma barcha maydonlarni bir uzun ro'yxatda ko'rsatadi. fieldsets esa maydonlarni mantiqiy bo'limlarga ajratadi β bu, ayniqsa, ko'p maydonli modellarda formani o'qishni yengillashtiradi:
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "holat", "korishlar", "yaratilgan"]
fieldsets = [
("Asosiy", {"fields": ["sarlavha", "slug", "muallif", "kategoriya"]}),
("Mazmun", {"fields": ["matn", "holat"]}),
("Statistika", {
"fields": ["korishlar", "yaratilgan"],
"classes": ["collapse"],
}),
]
Endi forma uch bo'limga bo'linadi: "Asosiy", "Mazmun" va "Statistika". Har bir element β (sarlavha, {sozlamalar}) ko'rinishidagi juftlik. "classes": ["collapse"] esa o'sha bo'limni yopiq (yig'ilgan) holda ko'rsatadi β foydalanuvchi kerak bo'lsa ochadi. Kam ishlatiladigan maydonlarni shunday yashirish formani toza tutadi.
Eslatma.
fieldsetsva oddiyfieldsbirga ishlatilmaydi β bittasini tanlaysiz. Agar shunchaki maydonlar tartibini o'zgartirmoqchi bo'lsangiz va guruhlamoqchi bo'lmasangiz,fields = ["sarlavha", "muallif", "matn"]yetarli.
prepopulated_fields β slug'ni avtomatik to'ldirish¶
SlugField (URL'ga mos qisqa nom) ni qo'lda yozish zerikarli. prepopulated_fields boshqa maydondan slug'ni avtomatik hosil qiladi β siz sarlavha yozganingizda JavaScript orqali slug o'zi to'ladi:
"Django Admin Panel" yozsangiz, slug django-admin-panel bo'ladi.
readonly_fields β faqat o'qish uchun maydonlar¶
Ba'zi maydonlarni ko'rsatish kerak, lekin tahrirlash mumkin emas. Masalan yaratilgan (auto_now_add=True bo'lgani uchun avtomatik to'ladi) yoki korishlar (kod o'zi oshiradi). Bularni qo'lda o'zgartirish mantiqsiz:
Endi bu ikki maydon formada ko'rinadi, lekin kulrang va tahrirlab bo'lmaydigan holatda. Bu noto'g'ri tahrirlardan himoya qiladi.
Nozik nuqta.
auto_now_add=Truebo'lganyaratilganmaydoni baribir formada tahrirlanmaydigan bo'lardi, lekin u sukut bo'yicha ko'rinmaydi ham.readonly_fieldsga qo'shsangiz β ko'rinadi, faqat o'zgartirib bo'lmaydi. Bu yozuvning yaratilgan vaqtini admin'da ko'rsatishning to'g'ri yo'li.
Inlines β bog'liq yozuvlarni bir sahifada tahrirlash¶
Hozirgacha har modelni alohida boshqardik. Lekin ko'pincha bog'liq yozuvlarni birga tahrirlash qulay: maqolani ochganda uning izohlarini ham o'sha sahifada ko'rish va qo'shish.
Bu β inline ning kuchi. Izoh modelida Maqolaga ForeignKey bor (ya'ni izoh maqolaga "tegishli"), shuning uchun izohni maqola formasi ichiga joylash mumkin.
Ikki turdagi inline bor:
TabularInlineβ yozuvlarni jadval (qatorlar) ko'rinishida ko'rsatadi. Ixcham, ko'p yozuv uchun qulay.StackedInlineβ har yozuvni alohida blok sifatida ko'rsatadi. Maydon ko'p bo'lganda o'qish osonroq.
# blog/admin.py
from django.contrib import admin
from .models import Maqola, Izoh
class IzohInline(admin.TabularInline):
model = Izoh
extra = 1 # bo'sh forma soni
fields = ["muallif_ism", "matn", "tasdiqlangan"]
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "holat"]
inlines = [IzohInline]
Endi har bir maqola formasining pastida uning izohlari jadval ko'rinishida chiqadi. extra = 1 bitta bo'sh izoh formasini qo'shadi β yangi izoh qo'shish uchun. Maqolani saqlaganda izohlar ham birga saqlanadi.
StackedInline ga o'tish uchun faqat asosiy sinfni almashtiramiz:
# blog/admin.py
class IzohStackedInline(admin.StackedInline):
model = Izoh
extra = 0 # bo'sh forma ko'rsatma
extrahaqida.extraβ necha dona bo'sh forma oldindan ko'rsatilishini belgilaydi.extra = 0esa hech narsa ko'rsatmaydi (mavjudlarini tahrirlaysiz, kerak bo'lsa "Yana qo'shish" tugmasini bosasiz). Katta jadvallardaextra = 0ekranni toza tutadi. Bundan tashqarimax_num(eng ko'p yozuv soni) vamin_num(eng kam) ni ham belgilash mumkin.Talab. Inline bo'lish uchun farzand modelda ota modelga
ForeignKey(yoki bittaOneToOneField) bo'lishi shart.Izoh.maqolaaynan shunday. Agar bir nechtaForeignKeybo'lsa,fk_namebilan qaysi birini ishlatishni aniq ko'rsatasiz.
Admin actions β ommaviy amallar¶
Yuzta maqolani bittalab "nashr qilingan" qilish β azob. Admin action bir vaqtning o'zida tanlangan barcha yozuvlarga amal bajaradi. Ro'yxat tepasidagi "Amal" ochiluvchi ro'yxatida paydo bo'ladi.
Action β bu uchta argument oladigan oddiy funksiya: modeladmin (ModelAdmin obyekti), request (joriy so'rov) va queryset (belgilangan yozuvlar). @admin.action dekoratori esa ro'yxatdagi ko'rinadigan nomni belgilaydi:
# blog/admin.py
from django.contrib import admin
from .models import Maqola
@admin.action(description="Tanlanganlarni nashr qilish")
def nashr_qilish(modeladmin, request, queryset):
yangilangan = queryset.update(holat="nashr")
modeladmin.message_user(request, f"{yangilangan} ta maqola nashr qilindi.")
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat"]
actions = [nashr_qilish]
Endi ro'yxatda bir necha maqolani belgilab, "Amal" dan "Tanlanganlarni nashr qilish" ni tanlab, "Bajarish" tugmasini bossangiz β hammasi bir SQL UPDATE so'rovi bilan yangilanadi. message_user esa foydalanuvchiga yashil xabar ko'rsatadi.
Tezlik nozikligi.
queryset.update(...)β bu bitta SQLUPDATEso'rovi, mingta yozuv uchun ham. Agar har yozuvni alohidaobj.save()qilsangiz, mingta so'rov bo'lardi vasave()dagi maxsus mantiq, signallar ishlamasligi mumkin.update()esa tez, lekin usave()metodini vapre_save/post_savesignallarni chaqirmaydi. Murakkab mantiq kerak bo'lsa, sikl bilansave()ni ishlating (sekinroq, lekin to'liq).
Action sifatida ModelAdmin metodi ham yozish mumkin. Bu, agar self ga murojaat kerak bo'lsa, qulay:
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat"]
actions = ["qoralama_qilish"]
@admin.action(description="Tanlanganlarni qoralamaga qaytarish")
def qoralama_qilish(self, request, queryset):
soni = queryset.update(holat="qoralama")
self.message_user(request, f"{soni} ta maqola qoralamaga qaytarildi.")
Sukut amal. Django'da har modelda "Tanlangan ... larni o'chirish" amali allaqachon bor. Uni o'chirib qo'yish uchun shu
ModelAdmindaactions = None, yoki butun saytda globaladmin.site.disable_action("delete_selected")ishlatiladi. Diqqat:disable_actionβ buAdminSitemetodi (admin.site.disable_action(...)),ModelAdminda yo'q; uniself.disable_action(...)deb chaqirsangizAttributeErrorchiqadi.
Maxsus ustunlar β @admin.display¶
Ba'zan list_display ga model maydoni emas, hisoblangan qiymat chiqarmoqchi bo'lasiz: matn qismini, rangli belgini yoki ikki maydon birikmasini. Buning uchun ModelAdmin ichida metod yozasiz va uni list_display ga qo'shasiz.
@admin.display dekoratori ustun sarlavhasini (description) va tartiblash uchun maydonni (ordering) belgilaydi:
# blog/admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Maqola
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "qisqa_matn", "holat_belgisi"]
@admin.display(description="Qisqacha")
def qisqa_matn(self, obj):
return obj.matn[:20]
@admin.display(description="Holat", ordering="holat")
def holat_belgisi(self, obj):
rang = "green" if obj.holat == "nashr" else "gray"
return format_html('<b style="color:{}">{}</b>', rang, obj.get_holat_display())
Bu yerda ikki maxsus ustun:
qisqa_matnβ maqola matnining birinchi 20 belgisini ko'rsatadi.holat_belgisiβ holatni rangli qilib ko'rsatadi: nashr β yashil, qoralama β kulrang.ordering="holat"tufayli bu ustun sarlavhasiga bosib tartiblash ham mumkin.
format_htmlβ XSS himoyasi. HTML qaytarayotganda hech qachon oddiyf"<b>{obj.matn}</b>"ishlatmang β bu XSS (zararli skript) teshigi ochadi.format_htmlesa.format()kabi ishlaydi, lekin argumentlarni avtomatik ekran qiladi (xavfsiz belgilarga aylantiradi).get_holat_display()esachoicesli maydonning odam o'qiy oladigan nomini beradi (Django avtomatik beradigan metod).
admin.site ni sozlash va so'rov optimizatsiyasi¶
Sarlavha va nomlar¶
Sukut bo'yicha panel "Django administration" deb yoziladi. Buni o'zgartirish uchun admin.site atributlarini sozlaysiz (odatda admin.py oxirida):
# blog/admin.py
from django.contrib import admin
admin.site.site_header = "Blog boshqaruvi" # tepadagi katta sarlavha
admin.site.site_title = "Blog admin" # brauzer tab nomi
admin.site.index_title = "Bosh sahifa" # bosh sahifa sarlavhasi
list_select_related β N+1 muammosini oldini olish¶
list_display da ForeignKey (masalan muallif) ko'rsatsangiz, Django har bir qator uchun alohida so'rov yuborib muallifni oladi β 100 maqola = 101 so'rov. Bu N+1 muammosi. list_select_related esa hammasini bitta JOIN ga birlashtiradi:
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "kategoriya"]
list_select_related = ["muallif", "kategoriya"] # bitta JOIN
Bu sozlama 09-bobdagi ilg'or ORM da chuqurroq ochiladigan select_related() ning admin'dagi ko'rinishidir. Katta jadvallarda admin sahifasini sezilarli tezlashtiradi.
Node.js bilan solishtirish. Node.js qo'llanmasidagi ko'p ekotizimda admin panelni qo'lda qurishingiz yoki uchinchi tomon kutubxonalarini ulashingiz kerak bo'ladi. Django esa buni "batareya bilan" beradi β bu uning eng kuchli tomonlaridan biri. Bir necha qator
ModelAdminto'liq CRUD interfeysini, autentifikatsiya bilan birga, beradi.
To'liq admin.py β hammasi birga¶
Mana bobda o'rgangan hamma narsani jamlaydigan, ishga tushirib tekshirilgan to'liq admin.py:
# blog/admin.py
from django.contrib import admin
from .models import Muallif, Kategoriya, Maqola, Izoh
class IzohInline(admin.TabularInline):
model = Izoh
extra = 1
fields = ["muallif_ism", "matn", "tasdiqlangan"]
@admin.action(description="Tanlanganlarni nashr qilish")
def nashr_qilish(modeladmin, request, queryset):
yangilangan = queryset.update(holat="nashr")
modeladmin.message_user(request, f"{yangilangan} ta maqola nashr qilindi.")
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "holat", "korishlar", "yaratilgan"]
list_filter = ["holat", "kategoriya", "yaratilgan"]
search_fields = ["sarlavha", "matn"]
ordering = ["-yaratilgan"]
list_editable = ["holat"]
prepopulated_fields = {"slug": ("sarlavha",)}
readonly_fields = ["yaratilgan", "korishlar"]
inlines = [IzohInline]
actions = [nashr_qilish]
fieldsets = [
("Asosiy", {"fields": ["sarlavha", "slug", "muallif", "kategoriya"]}),
("Mazmun", {"fields": ["matn", "holat"]}),
("Statistika", {"fields": ["korishlar", "yaratilgan"], "classes": ["collapse"]}),
]
@admin.register(Muallif)
class MuallifAdmin(admin.ModelAdmin):
list_display = ["ism", "email"]
search_fields = ["ism"]
admin.site.register(Kategoriya)
Bu fayl python manage.py check ni xatosiz o'tadi va admin paneli to'liq ishlaydi: ro'yxat ustunlari, filtrlar, qidiruv, ro'yxatda holat tahrirlash, slug avtomatik to'ldirish, faqat o'qish maydonlari, izoh inline'lari, nashr action'i va guruhlangan forma.
Mashqlar¶
Quyidagi mashqlar yuqoridagi blog modellariga asoslanadi. Har birini temp loyihada python manage.py check va python manage.py test bilan sinab ko'ring.
Oson¶
Muallifmodelini admin'daadmin.site.register(sodda usul) bilan ro'yxatdan o'tkazing.Maqolaadmin'idalist_displaygasarlavha,holatvayaratilganustunlarini qo'shing.Maqolaadmin'idaholatvakategoriyabo'yicha filtr (list_filter) qo'shing.Maqolaadmin'idasarlavhabo'yicha qidiruv (search_fields) yoqing.Maqolaro'yxatini eng yangi maqola tepada turadigan qilib tartiblang (ordering).Kategoriyaadmin'ini@admin.registerdekorator uslubida yozing.
O'rta¶
Maqolaadmin'idaholatni ro'yxatdan to'g'ridan-to'g'ri tahrirlanadigan qiling (list_editable), lekinsarlavhahavola bo'lib qolsin.Maqolaformasini uchfieldsetsga ajrating: "Asosiy" (sarlavha, muallif), "Mazmun" (matn, holat), "Texnik" (slug, kategoriya) β oxirgisicollapsebo'lsin.IzohniMaqolaformasi ichidaTabularInlinesifatida ko'rsating,extra=1bilan.slugmaydoninisarlavhadanprepopulated_fieldsorqali avtomatik to'ldiradigan qiling.yaratilganvakorishlarnireadonly_fieldsqiling va formada ko'rinadigan qiling.- Muallif ismi bo'yicha qidiruv qo'shing (
muallif__ismishlating).
Qiyin¶
- "Tanlangan maqolalarni qoralamaga qaytarish" admin action'ini yozing va foydalanuvchiga necha yozuv o'zgargani haqida xabar (
message_user) ko'rsating. list_displayga@admin.displaydekoratorliholat_belgisiustunini qo'shing: nashr β yashil, qoralama β qizil rangda (format_htmlbilan, XSS xavfsiz).Maqolaro'yxatida har maqolaning izohlari sonini ko'rsatadigan@admin.displayustunini yozing (ko'rsatma:obj.izohlar.count()), va N+1 nilist_select_relatedbilan kamaytiring.
Yechimlar
Oson¶
1. Muallif ni sodda ro'yxatdan o'tkazish
# blog/admin.py
from django.contrib import admin
from .models import Muallif
admin.site.register(Muallif)
2. list_display
# blog/admin.py
from django.contrib import admin
from .models import Maqola
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat", "yaratilgan"]
3. list_filter
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat", "yaratilgan"]
list_filter = ["holat", "kategoriya"]
4. search_fields
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat"]
search_fields = ["sarlavha"]
5. ordering
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "yaratilgan"]
ordering = ["-yaratilgan"] # minus = teskari (yangidan eskiga)
6. Dekorator uslubi
# blog/admin.py
from django.contrib import admin
from .models import Kategoriya
@admin.register(Kategoriya)
class KategoriyaAdmin(admin.ModelAdmin):
list_display = ["nom"]
O'rta¶
7. list_editable (birinchi ustun havola bo'lib qoladi)
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat"] # sarlavha birinchi -> havola
list_editable = ["holat"] # holat tahrirlanadi
8. fieldsets
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
fieldsets = [
("Asosiy", {"fields": ["sarlavha", "muallif"]}),
("Mazmun", {"fields": ["matn", "holat"]}),
("Texnik", {"fields": ["slug", "kategoriya"], "classes": ["collapse"]}),
]
9. TabularInline
# blog/admin.py
from django.contrib import admin
from .models import Maqola, Izoh
class IzohInline(admin.TabularInline):
model = Izoh
extra = 1
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
inlines = [IzohInline]
10. prepopulated_fields
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("sarlavha",)}
11. readonly_fields
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
readonly_fields = ["yaratilgan", "korishlar"]
# readonly maydon formada ko'rinadi, lekin tahrirlanmaydi.
# fields/fieldsets ishlatsangiz, ularni o'sha yerga ham qo'shing.
fields = ["sarlavha", "matn", "holat", "yaratilgan", "korishlar"]
12. Bog'liq model bo'yicha qidiruv
# blog/admin.py
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif"]
search_fields = ["sarlavha", "muallif__ism"] # __ orqali FK ichiga kirish
Qiyin¶
13. Action: qoralamaga qaytarish
# blog/admin.py
from django.contrib import admin
from .models import Maqola
@admin.action(description="Tanlanganlarni qoralamaga qaytarish")
def qoralama_qilish(modeladmin, request, queryset):
soni = queryset.update(holat="qoralama")
modeladmin.message_user(request, f"{soni} ta maqola qoralamaga qaytarildi.")
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat"]
actions = [qoralama_qilish]
14. Rangli holat ustuni (@admin.display + format_html)
# blog/admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Maqola
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "holat_belgisi"]
@admin.display(description="Holat", ordering="holat")
def holat_belgisi(self, obj):
rang = "green" if obj.holat == "nashr" else "red"
return format_html('<b style="color:{}">{}</b>', rang, obj.get_holat_display())
format_html argumentlarni avtomatik ekran qiladi β XSS xavfsiz. get_holat_display() esa choices ning odam o'qiy oladigan nomini beradi.
15. Izohlar sonini ko'rsatish + N+1 optimizatsiya
# blog/admin.py
from django.contrib import admin
from .models import Maqola
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "muallif", "izohlar_soni"]
list_select_related = ["muallif", "kategoriya"] # FK uchun N+1 ni kamaytiradi
@admin.display(description="Izohlar soni")
def izohlar_soni(self, obj):
return obj.izohlar.count()
Eslatma: izohlar.count() har qator uchun bitta COUNT so'rovi yuboradi β ya'ni bu metod o'zi N+1 qiladi. Buni butunlay yo'qotish uchun get_queryset ni qayta yozib, annotate(Count("izohlar")) ishlatish kerak (bu 09-bobda ko'riladi):
# blog/admin.py β to'liq optimallashtirilgan variant
from django.contrib import admin
from django.db.models import Count
from .models import Maqola
@admin.register(Maqola)
class MaqolaAdmin(admin.ModelAdmin):
list_display = ["sarlavha", "izohlar_soni"]
list_select_related = ["muallif", "kategoriya"]
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.annotate(_izoh=Count("izohlar"))
@admin.display(description="Izohlar soni", ordering="_izoh")
def izohlar_soni(self, obj):
return obj._izoh
annotate bilan izohlar soni asosiy so'rov ichida hisoblanadi β bitta so'rov, va ordering="_izoh" tufayli ustun bo'yicha tartiblash ham mumkin.
β¬ οΈ Oldingi: 07 β Model munosabatlari Β· π README Β· Keyingi: 09 β Ilg'or ORM va optimizatsiya β‘οΈ