Repere Django: modele, administrator și valorificarea bazei de date relaționale (partea 3)

Publicat: 2022-03-10
Rezumat rapid ↬ Panoul de administrare este una dintre cele mai puternice și flexibile caracteristici pe care le oferă cadrul web Django, combinând funcționalitatea instantanee de la raft cu personalizarea infinită. Folosind un exemplu de proiect bazat pe un sistem de inventar de bibliotecă, vom folosi panoul de administrare pentru a afla despre crearea modelelor și interacțiunea cu bazele de date relaționale în Django.

Înainte de a începe, vreau să observ că capabilitățile administrative încorporate ale Django, chiar și după personalizare, nu sunt destinate utilizatorilor finali. Panoul de administrare există ca instrument de dezvoltator, operator și administrator pentru crearea și întreținerea software-ului. Nu este destinat să fie folosit pentru a oferi utilizatorilor finali capabilități de moderare sau alte abilități de administrator pe platforma pe care o dezvoltați.

Acest articol se bazează pe o ipoteză în două părți:

  1. Panoul de administrare Django este atât de intuitiv încât practic știți deja cum să îl utilizați.
  2. Panoul de administrare Django este atât de puternic încât îl putem folosi ca instrument pentru a învăța despre reprezentarea datelor într-o bază de date relațională folosind un model Django.

Ofer aceste idei cu avertismentul că va trebui în continuare să scriem un cod de configurare pentru a activa abilitățile mai puternice ale panoului de administrare și va trebui în continuare să folosim ORM (mapping-relațional obiect) al Django pentru a specifica reprezentarea datelor. în sistemul nostru.

Lectură recomandată

„Django Highlights” este o serie care introduce concepte importante de dezvoltare web în Django. Poate doriți să citiți despre furnizarea de fluxuri de autentificare a utilizatorilor sigure și să urmați alături de o demonstrație despre utilizarea șablonului Django pentru a scrie pagini complexe.

Mai multe după săritură! Continuați să citiți mai jos ↓

Configurare

Vom lucra cu un exemplu de proiect în acest articol. Proiectul modelează unele date pe care o bibliotecă le-ar stoca despre cărțile și patronii săi. Exemplul ar trebui să fie destul de aplicabil pentru multe tipuri de sisteme care gestionează utilizatorii și/sau inventarul. Iată o scurtă privire despre cum arată datele:

Modelul de date. (Previzualizare mare)

Vă rugăm să parcurgeți următorii pași pentru ca exemplul de cod să ruleze pe computerul dvs. local.

1. Instalarea pachetelor

Cu Python 3.6 sau o versiune ulterioară instalată, creați un director și un mediu virtual. Apoi, instalați următoarele pachete:

 pip install django django-grappelli

Django este cadrul web cu care lucrăm în acest articol. ( django-grappelli este o temă a panoului de administrare pe care o vom acoperi pe scurt.)

2. Obținerea Proiectului

Cu pachetele anterioare instalate, descărcați exemplul de cod de pe GitHub. Alerga:

 git clone https://github.com/philipkiely/library_records.git cd library_records/library

3. Crearea unui superutilizator

Folosind următoarele comenzi, configurați baza de date și creați un superutilizator. Interfața de linie de comandă vă va ghida prin procesul de creare a unui superutilizator. Contul dvs. de superutilizator va fi modul în care accesați panoul de administrare într-un moment, așa că asigurați-vă că vă amintiți parola pe care ați setat-o. Utilizare:

 python manage.py migrate python manage.py createsuperuser

4. Încărcarea datelor

Pentru explorarea noastră, am creat un set de date numit fixture pe care îl puteți încărca în baza de date (mai multe despre cum să creați un fixture la sfârșitul articolului). Utilizați dispozitivul pentru a vă completa baza de date înainte de a o explora în panoul de administrare. Alerga:

 python manage.py loaddata ../fixture.json

5. Rularea Proiectului Exemplu

În cele din urmă, sunteți gata să rulați exemplul de cod. Pentru a rula serverul, utilizați următoarea comandă:

 python manage.py runserver

Deschideți browserul la https://127.0.0.1:8000 pentru a vizualiza proiectul. Rețineți că sunteți redirecționat automat către panoul de administrare la /admin/ . Am realizat asta cu următoarea configurație în library/urls.py :

 from django.contrib import admin from django.urls import path from records import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index), ]

combinat cu următoarea redirecționare simplă în records/views.py :

 from django.http import HttpResponseRedirect def index(request): return HttpResponseRedirect('/admin/')

Utilizarea panoului de administrare

Am reușit! Când încărcați pagina, ar trebui să vedeți ceva de genul următor:

Pagina principală a panoului de administrare Django. (Previzualizare mare)

Această vizualizare este realizată cu următorul cod standard în records/admin.py :

 from django.contrib import admin from .models import Book, Patron, Copy admin.site.register(Book) admin.site.register(Copy) admin.site.register(Patron)

Această vizualizare ar trebui să vă ofere o înțelegere inițială a datelor pe care sistemul le stochează. Voi elimina o parte din mister: Groups și Users sunt definiți de Django și stochează informații și permisiuni pentru conturile din sistem. Puteți citi mai multe despre modelul User într-un articol anterior din această serie. Books , Copys și Patrons sunt tabele din baza de date pe care le-am creat atunci când rulăm migrarea și le-am populat prin încărcarea fixturei. Rețineți că Django pluralizează naiv numele modelelor adăugând un „s”, chiar și în cazuri precum „copii” în care este ortografia incorectă.

Modelul de date. (Previzualizare mare)

În proiectul nostru, o Book este o înregistrare cu un titlu, autor, data publicării și ISBN (International Standard Book Number). Biblioteca păstrează o Copy a fiecărei Book sau, eventual, mai multe. Fiecare Copy poate fi extrasă de către un Patron sau poate fi înregistrată în prezent. Un Patron este o extensie a User care înregistrează adresa și data nașterii acestuia.

Creați, citiți, actualizați, distrugeți

O capacitate standard a panoului de administrare este adăugarea de instanțe ale fiecărui model. Faceți clic pe „cărți” pentru a ajunge la pagina modelului și faceți clic pe butonul „Adăugați carte” din colțul din dreapta sus. Procedând astfel, va apărea un formular, pe care îl puteți completa și salva pentru a crea o carte.

Creați o carte (previzualizare mare)

Crearea unui Patron dezvăluie o altă capacitate încorporată a formularului de creare a administratorului: puteți crea modelul conectat direct din același formular. Captura de ecran de mai jos arată fereastra pop-up care este declanșată de semnul verde plus din dreapta meniului derulant User . Astfel, puteți crea ambele modele pe aceeași pagină de administrare.

Creați un patron. (Previzualizare mare)

Puteți crea o COPY prin același mecanism.

Pentru fiecare înregistrare, puteți face clic pe rând pentru a o edita folosind același formular. De asemenea, puteți șterge înregistrări folosind o acțiune de administrator.

Acțiuni de administrator

În timp ce capacitățile încorporate ale panoului de administrare sunt foarte utile, vă puteți crea propriile instrumente folosind acțiunile de administrare. Vom crea două: una pentru crearea de copii ale cărților și una pentru înregistrarea cărților care au fost returnate la bibliotecă.

Pentru a crea o Copy a unei Book , accesați adresa URL /admin/records/book/ și utilizați meniul drop-down „Acțiune” pentru a selecta „Adăugați o copie a cărții” și apoi utilizați casetele de selectare din coloana din stânga din tabel pentru a selecta cartea sau cărțile din care să adăugați o copie la inventar.

Creați o acțiune de copiere. (Previzualizare mare)

Crearea acesteia se bazează pe o metodă model pe care o vom trata mai târziu. O putem numi ca o acțiune de administrator prin crearea unei clase ModelAdmin pentru modelul Profile , după cum urmează în records/admin.py :

 from django.contrib import admin from .models import Book, Patron, Copy class BookAdmin(admin.ModelAdmin): list_display = ("title", "author", "published") actions = ["make_copys"] def make_copys(self, request, queryset): for q in queryset: q.make_copy() self.message_user(request, "copy(s) created") make_copys.short_description = "Add a copy of book(s)" admin.site.register(Book, BookAdmin)

Proprietatea list_display denotă ce câmpuri sunt folosite pentru a reprezenta modelul în pagina de prezentare generală a modelului. Proprietatea actions listează acțiunile de administrator. Acțiunea noastră de administrator este definită ca o funcție în BookAdmin și ia trei argumente: obiectul admin în sine, cererea (solicitarea HTTP efectivă trimisă de client) și setul de interogări (lista de obiecte ale căror casete au fost bifate). Efectuăm aceeași acțiune asupra fiecărui element din setul de interogări, apoi informăm utilizatorul că acțiunile au fost finalizate. Fiecare acțiune de administrator necesită o scurtă descriere, astfel încât să poată fi identificată corect în meniul derulant. În cele din urmă, acum adăugăm BookAdmin la înregistrarea modelului.

Scrierea acțiunilor de administrator pentru setarea proprietăților în bloc este destul de repetitivă. Iată codul pentru înregistrarea unei Copy , rețineți că este aproape echivalentă cu acțiunea anterioară.

 from django.contrib import admin from .models import Book, Patron, Copy class CopyAdmin(admin.ModelAdmin): actions = ["check_in_copys"] def check_in_copys(self, request, queryset): for q in queryset: q.check_in() self.message_user(request, "copy(s) checked in") check_in_copys.short_description = "Check in copy(s)" admin.site.register(Copy, CopyAdmin)

Tema de administrator

În mod implicit, Django oferă stiluri destul de simple pentru panoul de administrare. Puteți să vă creați propria temă sau să utilizați o temă terță parte pentru a oferi panoului de administrare un nou aspect. O temă populară cu sursă deschisă este grappelli, pe care am instalat-o mai devreme în articol. Puteți consulta documentația pentru capacitățile sale complete.

Instalarea temei este destul de simplă, necesită doar două linii. Mai întâi, adăugați grappelli la INSTALLED_APPS , după cum urmează, în library/settings.py :

 INSTALLED_APPS = [ 'grappelli', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'records', ]

Apoi, ajustați library/urls.py :

 from django.contrib import admin from django.urls import path, include from records import views urlpatterns = [ path('grappelli/', include('grappelli.urls')), path('admin/', admin.site.urls), path('', views.index), ]

Cu aceste modificări în vigoare, panoul de administrare ar trebui să arate astfel:

Panou de administrare cu temă. (Previzualizare mare)

Există o serie de alte teme și, din nou, le puteți dezvolta pe propria voastră. Voi rămâne cu aspectul implicit pentru restul acestui articol.

Înțelegerea modelelor

Acum că vă simțiți confortabil cu panoul de administrare și îl folosiți pentru a naviga prin date, să aruncăm o privire la modelele care definesc structura bazei de date. Fiecare model reprezintă un tabel dintr-o bază de date relațională.

O bază de date relațională stochează date într-unul sau mai multe tabele. Fiecare dintre aceste tabele are o structură de coloană specificată, inclusiv o cheie primară (un identificator unic pentru fiecare element) și una sau mai multe coloane de valori, care sunt de diferite tipuri, cum ar fi șiruri de caractere, numere întregi și date. Fiecare obiect stocat în baza de date este reprezentat ca un singur rând. Partea „relațională” a numelui provine din ceea ce este probabil cea mai importantă caracteristică a tehnologiei: crearea de relații între tabele. Un obiect (rând) poate avea o mapare unu-la-unu, unu-la-mulți (cheie externă) sau mai multe-la-mulți la rânduri din alte tabele. Vom discuta acest lucru în continuare în exemple.

Django, implicit, folosește SQLite3 pentru dezvoltare. SQLite3 este un simplu motor de baze de date relaționale, iar baza ta de date este creată automat ca db.sqlite3 prima dată când rulezi python manage.py migrate . Vom continua cu SQLite3 pentru acest articol, dar nu este potrivit pentru utilizare în producție, în primul rând pentru că sunt posibile suprascrieri cu utilizatori concurenți. În producție sau când scrieți un sistem pe care intenționați să îl implementați într-o zi, utilizați PostgreSQL sau MySQL.

Django folosește modele pentru a interfața cu baza de date. Folosind o parte a ORM-ului Django, fișierul records/models.py include mai multe modele, ceea ce permite specificarea câmpurilor, proprietăților și metodelor pentru fiecare obiect. Când creăm modele, ne străduim pentru o arhitectură „Fat Model”, în limitele rațiunii. Aceasta înseamnă că cât mai mult posibil din validarea datelor, analizarea, procesarea, logica de afaceri, gestionarea excepțiilor, rezolvarea cazurilor marginale și sarcini similare ar trebui gestionate în specificația modelului în sine. Sub capotă, modelele Django sunt obiecte foarte complexe, caracteristice, cu un comportament implicit foarte util. Acest lucru face ca arhitectura „Fat Model” să fie ușor de realizat chiar și fără a scrie o cantitate substanțială de cod.

Să trecem prin cele trei modele din aplicația noastră exemplu. Nu putem acoperi totul, deoarece acesta ar trebui să fie un articol introductiv, nu documentația completă a framework-ului Django, dar voi evidenția cele mai importante alegeri pe care le-am făcut în construirea acestor modele simple.

Clasa Book este cel mai simplu dintre modele. Aici este de la records/models.py :

 from django.db import models class Book(models.Model): title = models.CharField(max_length=300) author = models.CharField(max_length=150) published = models.DateField() isbn = models.IntegerField(unique=True) def __str__(self): return self.title + " by " + self.author def make_copy(self): Copy.objects.create(book=self)

Toate câmpurile CharField necesită un atribut max_length specificat. Lungimea convențională este de 150 de caractere, pe care le-am dublat pentru title în cazul titlurilor foarte lungi. Desigur, există încă o limită arbitrară, care ar putea fi depășită. Pentru lungimea textului nemărginit, utilizați un TextField . Câmpul published este un DateField . Momentul în care a fost publicată cartea nu contează, dar dacă ar fi, aș folosi un DateTimeField . În cele din urmă, ISBN-ul este un număr întreg (ISBN-urile sunt de 10 sau 13 cifre și, prin urmare, toate se încadrează în valoarea maximă a întregului) și folosim unique=True deoarece nu există două cărți care pot avea același ISBN, care este apoi aplicat la nivelul bazei de date.

Toate obiectele au o metodă __str__(self) care definește reprezentarea lor în șir. Înlocuim implementarea implicită oferită de models.Model Clasa de model și în schimb reprezentăm cărțile ca „titlu după autor” în toate locurile în care modelul ar fi reprezentat ca șir. Amintiți-vă că anterior am folosit list_display în obiectul de administrare al Book pentru a determina ce câmpuri vor fi afișate în lista panoului de administrare. Dacă list_display nu este prezent, lista de administrare arată în schimb reprezentarea în șir a modelului, așa cum o face atât pentru Patron , cât și pentru Copy .

În cele din urmă, avem o metodă pe Book pe care am numit-o în acțiunea de administrare pe care am scris-o mai devreme. Această funcție creează o Copy care este legată de o anumită instanță a unei Book din baza de date.

Trecând la Patron , acest model introduce conceptul de relație unu-la-unu, în acest caz cu modelul User încorporat. Verificați-l de pe records/models.py :

 from django.db import models from django.contrib.auth.models import User class Patron(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) address = models.CharField(max_length=150) dob = models.DateField() def __str__(self): return self.user.username

Câmpul user nu este tocmai o funcție bijectivă. POATE exista o instanță de User fără o instanță de Patron asociată. Cu toate acestea, un User NU POATE fi asociat cu mai mult de o Patron de utilizator, iar un Patron nu poate exista fără o singură relație cu un utilizator. Acest lucru este aplicat la nivelul bazei de date și este garantat de specificația on_delete=models.CASCADE : dacă o instanță de User este ștearsă, un Profile asociat va fi șters.

Celelalte câmpuri și __str__(self) pe care le-am văzut înainte. Este de remarcat faptul că puteți ajunge printr-o relație unu-la-unu pentru a obține atribute, în acest caz user.username , în funcțiile unui model.

Pentru a extinde utilitatea relațiilor de baze de date, să ne îndreptăm atenția către Copy from records/models.py :

 from django.db import models class Copy(models.Model): book = models.ForeignKey(Book, on_delete=models.CASCADE) out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL) def __str__(self): has_copy = "checked in" if self.out_to: has_copy = self.out_to.user.username return self.book.title + " -> " + has_copy def check_out(self, p): self.out_to = p self.save() def check_in(self): self.out_to = None self.save()

Din nou, am văzut cele mai multe dintre acestea înainte, așa că să ne concentrăm asupra lucrurilor noi: models.ForeignKey . O Copy trebuie să fie dintr-o singură Book , dar biblioteca poate avea mai multe Copy ale fiecărei Book . O Book poate exista în baza de date fără ca biblioteca să aibă o Copy în catalogul său, dar o Copy nu poate exista fără o Book subiacentă.

Această relație complexă este exprimată cu următoarea linie:

 book = models.ForeignKey(Book, on_delete=models.CASCADE)

Comportamentul de ștergere este același cu cel al Patron în ceea ce privește User .

Relația dintre o Copy și un Patron este ușor diferită. O Copy poate fi extrasă până la un Patron , dar fiecare Patron poate extrage atâtea Copy câte îi permite biblioteca. Cu toate acestea, aceasta nu este o relație permanentă, Copy nu este uneori verificată. Patron -urile și Copy -urile există independent unul de celălalt în baza de date; ștergerea unei instanțe a uneia nu ar trebui să șterge nicio instanță a celeilalte.

Această relație este încă un caz de utilizare pentru cheia externă, dar cu argumente diferite:

 out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL)

Aici, dacă blank=True permite formularelor să accepte None ca valoare pentru relație și null=True permite ca coloana pentru relația Patron din tabelul Copy din baza de date să accepte null ca valoare. Comportamentul de ștergere, care ar fi declanșat pe o Copy dacă o instanță de Patron a fost ștearsă în timp ce a avut acea Copy verificată, este de a întrerupe relația, lăsând Copy intactă, setând câmpul Patron la nul.

Același tip de câmp, models.ForeignKey , poate exprima relații foarte diferite între obiecte. Singura relație pe care nu am putut-o încadra în mod clar în exemplu este un câmp multi-la-mulți, care este ca un câmp unu-la-unu, cu excepția faptului că, așa cum sugerează numele său, fiecare instanță poate fi legată de multe alte instanțe și toate celelalte și fiecare dintre acestea pot fi legate de multe altele, cum ar fi modul în care o carte ar putea avea mai mulți autori, fiecare dintre care a scris mai multe cărți.

Migrații

S-ar putea să vă întrebați cum baza de date știe ce este exprimat în model. Din experiența mea, migrațiile sunt unul dintre acele lucruri care sunt destul de simple până când nu mai sunt și apoi îți mănâncă fața. Iată cum să vă păstrați cana intactă, pentru începători: aflați despre migrații și cum să interacționați cu acestea, dar încercați să evitați să faceți modificări manuale ale fișierelor de migrare. Dacă știi deja ce faci, sări peste această secțiune și ține pasul cu ceea ce funcționează pentru tine.

În orice caz, consultați documentația oficială pentru o tratare completă a subiectului.

Migrațiile traduc modificările dintr-un model în modificări ale schemei bazei de date. Nu trebuie să le scrieți singur, Django le creează cu comanda python manage.py makemigrations . Ar trebui să rulați această comandă atunci când creați un model nou sau editați câmpurile unui model existent, dar nu este nevoie să faceți acest lucru atunci când creați sau editați metode de model. Este important de reținut că migrațiile există ca un lanț, fiecare face referire la precedentul, astfel încât să poată face modificări fără erori ale schemei bazei de date. Astfel, dacă colaborați la un proiect, este important să păstrați un singur istoric de migrare consistent în controlul versiunilor. Când există migrări neaplicate, rulați python manage.py migrate pentru a le aplica înainte de a rula serverul.

Proiectul exemplu este distribuit cu o singură migrare, records/migrations/0001_initial.py . Din nou, acesta este un cod generat automat pe care nu ar trebui să-l editați, așa că nu îl voi copia aici, dar dacă doriți să vă dați o idee a ceea ce se întâmplă în culise, mergeți mai departe și aruncați o privire la el.

Fixare

Spre deosebire de migrații, fixturile nu sunt un aspect comun al dezvoltării Django. Le folosesc pentru a distribui date mostre cu articole și nu le-am folosit niciodată altfel. Cu toate acestea, pentru că am folosit unul mai devreme, mă simt obligat să introduc subiectul.

Pentru o dată, documentația oficială este puțin subțire pe această temă. În general, ceea ce ar trebui să știți este că dispozitivele sunt o modalitate de a importa și exporta date din baza de date într-o varietate de formate, inclusiv JSON, care este ceea ce folosesc. Această funcție există în principal pentru a ajuta la lucruri precum testarea automată și nu este un sistem de rezervă sau o modalitate de a edita datele într-o bază de date live. Mai mult, fixturile nu sunt actualizate cu migrări și dacă încercați să aplicați un fix unei baze de date cu o schemă incompatibilă, acesta va eșua.

Pentru a genera un fix pentru întreaga bază de date, rulați:

 python manage.py dumpdata --format json > fixture.json

Pentru a încărca un dispozitiv, rulați:

 python manage.py loaddata fixture.json

Concluzie

Scrierea modelelor în Django este un subiect uriaș, iar utilizarea panoului de administrare este un altul. În 3.000 de cuvinte, am reușit doar să le prezint pe fiecare. Sperăm că utilizarea panoului de administrare v-a oferit o interfață mai bună pentru a explora modul în care modelele funcționează și se relaționează între ele, lăsându-vă cu încredere să experimentați și să dezvoltați propriile reprezentări relaționale ale datelor.

Dacă sunteți în căutarea unui loc ușor de început, încercați să adăugați un model de Librarian care moștenește de la User , așa cum face Profile . Pentru mai multă provocare, încercați să implementați un istoric de plată pentru fiecare Copy și/sau Patron (există mai multe moduri de a realiza aceasta).

Django Highlights este o serie care introduce concepte importante de dezvoltare web în Django. Fiecare articol este scris ca un ghid independent pentru o fațetă a dezvoltării Django menită să ajute dezvoltatorii și designerii front-end să ajungă la o înțelegere mai profundă a „cealaltă jumătate” a bazei de cod. Aceste articole sunt în mare parte construite pentru a vă ajuta să înțelegeți teoria și convențiile, dar conțin câteva exemple de cod care sunt scrise în Django 3.0.

Lectură suplimentară

Ați putea fi interesat de următoarele articole și documentație.

  • Repere Django: Modele de utilizator și autentificare (Partea 1)
  • Repere Django: Șabloanele salvează linii (Partea a 2-a)
  • Repere Django: dispute active statice și fișiere media (partea a 4-a)
  • Documentația de administrare Django
  • Modele Django
  • Referință pentru câmpul modelului Django
  • Migrațiile Django