Najważniejsze cechy Django: modele, administracja i wykorzystanie relacyjnej bazy danych (część 3)
Opublikowany: 2022-03-10Zanim zaczniemy, chcę zauważyć, że wbudowane możliwości administracyjne Django, nawet po dostosowaniu, nie są przeznaczone dla użytkowników końcowych. Panel administracyjny istnieje jako narzędzie programisty, operatora i administratora do tworzenia i utrzymywania oprogramowania. Nie jest przeznaczony do udostępniania użytkownikom końcowym możliwości moderowania ani żadnych innych uprawnień administracyjnych na tworzonej przez Ciebie platformie.
Ten artykuł opiera się na hipotezie składającej się z dwóch części:
- Panel administracyjny Django jest tak intuicyjny, że w zasadzie już wiesz, jak z niego korzystać.
- Panel administracyjny Django jest tak potężny, że możemy go wykorzystać jako narzędzie do nauki reprezentowania danych w relacyjnej bazie danych za pomocą modelu Django.
Oferuję te pomysły z zastrzeżeniem, że nadal będziemy musieli napisać kod konfiguracyjny, aby aktywować potężniejsze możliwości panelu administracyjnego, i nadal będziemy musieli używać ORM opartego na modelach Django (mapowanie obiektowo-relacyjne) do określenia reprezentacji danych w naszym systemie.
rekomendowane lektury
„Django Highlights” to seria przedstawiająca ważne koncepcje tworzenia stron internetowych w Django. Możesz przeczytać o zapewnianiu bezpiecznych przepływów uwierzytelniania użytkowników i śledzić demonstrację używania szablonów Django do pisania złożonych stron.
Konfiguracja
W tym artykule będziemy pracować z przykładowym projektem. Projekt modeluje niektóre dane, które biblioteka przechowuje na temat swoich książek i patronów. Przykład powinien mieć odpowiednie zastosowanie do wielu typów systemów, które zarządzają użytkownikami i/lub zapasami. Oto zapowiedź tego, jak wyglądają dane:
Wykonaj następujące kroki, aby uruchomić przykładowy kod na komputerze lokalnym.
1. Instalowanie pakietów
Po zainstalowaniu języka Python 3.6 lub nowszego utwórz katalog i środowisko wirtualne. Następnie zainstaluj następujące pakiety:
pip install django django-grappelli
Django to framework sieciowy, z którym pracujemy w tym artykule. ( django-grappelli
to motyw panelu administracyjnego, który pokrótce omówimy.)
2. Zdobycie projektu
Po zainstalowaniu poprzednich pakietów pobierz przykładowy kod z GitHub. Biegać:
git clone https://github.com/philipkiely/library_records.git cd library_records/library
3. Tworzenie superużytkownika
Korzystając z poniższych poleceń, skonfiguruj bazę danych i utwórz administratora. Interfejs wiersza poleceń przeprowadzi Cię przez proces tworzenia superużytkownika. Twoje konto superużytkownika będzie za chwilę sposobem, w jaki uzyskasz dostęp do panelu administracyjnego, więc pamiętaj, aby zapamiętać ustawione przez siebie hasło. Stosowanie:
python manage.py migrate python manage.py createsuperuser
4. Ładowanie danych
Na potrzeby naszej eksploracji stworzyłem zestaw danych nazwany osprzętem, który można załadować do bazy danych (więcej o tworzeniu osprzętu na końcu artykułu). Użyj urządzenia, aby zapełnić bazę danych przed przeglądaniem jej w panelu administracyjnym. Biegać:
python manage.py loaddata ../fixture.json
5. Uruchamianie przykładowego projektu
Wreszcie jesteś gotowy do uruchomienia przykładowego kodu. Aby uruchomić serwer, użyj następującego polecenia:
python manage.py runserver
Otwórz przeglądarkę na https://127.0.0.1:8000, aby wyświetlić projekt. Zauważ, że zostaniesz automatycznie przekierowany do panelu administracyjnego w /admin/ . Udało mi się to osiągnąć za pomocą następującej konfiguracji w pliku 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), ]
w połączeniu z następującym prostym przekierowaniem w records/views.py :
from django.http import HttpResponseRedirect def index(request): return HttpResponseRedirect('/admin/')
Korzystanie z panelu administracyjnego
Zrobiliśmy to! Po załadowaniu strony powinieneś zobaczyć coś takiego:
Ten widok jest realizowany za pomocą następującego kodu wzorcowego w 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)
Widok ten powinien dać wstępne zrozumienie danych, które przechowuje system. Usunę część tajemnicy: Groups
i Users
są definiowani przez Django i przechowują informacje oraz uprawnienia dla kont w systemie. Więcej o modelu User
można przeczytać we wcześniejszym artykule z tej serii. Books
, Copys
i Patrons
to tabele w bazie danych, które utworzyliśmy podczas uruchamiania migracji i wypełniane przez wczytanie urządzenia. Zwróć uwagę, że Django naiwnie tworzy liczbę mnogą nazw modeli, dodając „s”, nawet w przypadkach takich jak „kopie”, w których jest to nieprawidłowa pisownia.
W naszym projekcie Book
to zapis z tytułem, autorem, datą publikacji oraz ISBN (International Standard Book Number). Biblioteka przechowuje Copy
każdej Book
, a być może wiele. Każda Copy
może być wyrejestrowana przez Patron
lub może być aktualnie odebrana. Patron
jest rozszerzeniem User
, które zapisuje jego adres i datę urodzenia.
Twórz, czytaj, aktualizuj, niszcz
Jedną ze standardowych możliwości panelu administracyjnego jest dodawanie instancji każdego modelu. Kliknij „książki”, aby przejść do strony modela, a następnie kliknij przycisk „Dodaj książkę” w prawym górnym rogu. Spowoduje to wyświetlenie formularza, który możesz wypełnić i zapisać, aby utworzyć książkę.
Utworzenie Patron
ujawnia kolejną wbudowaną możliwość formularza tworzenia przez administratora: możesz utworzyć połączony model bezpośrednio z tego samego formularza. Poniższy zrzut ekranu pokazuje wyskakujące okienko, które jest uruchamiane przez zielony znak plus po prawej stronie listy rozwijanej User
. W ten sposób możesz tworzyć oba modele na tej samej stronie administratora.
Możesz utworzyć COPY
za pomocą tego samego mechanizmu.
Dla każdego rekordu możesz kliknąć wiersz, aby go edytować przy użyciu tego samego formularza. Rekordy można również usuwać za pomocą czynności administratora.
Czynności administracyjne
Chociaż wbudowane możliwości panelu administracyjnego są bardzo przydatne, możesz tworzyć własne narzędzia za pomocą działań administracyjnych. Stworzymy dwa: jeden do tworzenia kopii książek i drugi do sprawdzania książek, które zostały zwrócone do biblioteki.
Aby utworzyć Copy
Book
, przejdź do adresu URL /admin/records/book/
i użyj menu rozwijanego „Akcja”, aby wybrać „Dodaj kopię książki(-y)”, a następnie użyj pól wyboru w kolumnie po lewej stronie tabeli, aby wybrać, do której książki lub książek dodać kopię do ekwipunku.
Tworzenie tego opiera się na metodzie modelu, którą omówimy później. Możemy to nazwać akcją administratora, tworząc klasę ModelAdmin
dla modelu Profile
w następujący sposób w 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)
Właściwość list_display
wskazuje, które pola są używane do reprezentowania modelu na stronie przeglądu modelu. Właściwość actions
zawiera listę działań administracyjnych. Nasza akcja admin jest zdefiniowana jako funkcja w BookAdmin
i przyjmuje trzy argumenty: sam obiekt admin, żądanie (faktyczne żądanie HTTP wysłane przez klienta) oraz queryset (lista obiektów, których pola zostały zaznaczone). Na każdym elemencie w queryset wykonujemy tę samą akcję, a następnie powiadamiamy użytkownika, że akcje zostały zakończone. Każda akcja administratora wymaga krótkiego opisu, aby można ją było poprawnie zidentyfikować w rozwijanym menu. Na koniec dodajemy teraz BookAdmin
podczas rejestracji modelu.
Pisanie akcji administracyjnych w celu zbiorczego ustawiania właściwości jest dość powtarzalne. Oto kod do zaewidencjonowania Copy
, zwróć uwagę na jego równoważność z poprzednią akcją.
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)
Motyw administratora
Domyślnie Django zapewnia dość proste style panelu administracyjnego. Możesz utworzyć własny motyw lub użyć motywu innej firmy, aby nadać panelowi administracyjnemu nowy wygląd. Jednym z popularnych motywów open-source jest grappelli, który zainstalowaliśmy wcześniej w artykule. Możesz sprawdzić dokumentację, aby poznać jej pełne możliwości.
Instalacja motywu jest dość prosta, wymaga tylko dwóch linii. Najpierw dodaj grappelli
do INSTALLED_APPS
w następujący sposób w 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', ]
Następnie dostosuj 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), ]
Po wprowadzeniu tych zmian panel administracyjny powinien wyglądać następująco:
Istnieje wiele innych motywów i znowu możesz rozwijać swój własny. Pozostanę przy domyślnym wyglądzie do końca tego artykułu.
Zrozumienie modeli
Teraz, gdy znasz już panel administracyjny i używasz go do nawigacji po danych, przyjrzyjmy się modelom, które definiują strukturę naszej bazy danych. Każdy model reprezentuje jedną tabelę w relacyjnej bazie danych.
Relacyjna baza danych przechowuje dane w jednej lub kilku tabelach. Każda z tych tabel ma określoną strukturę kolumn, w tym klucz podstawowy (unikalny identyfikator dla każdego elementu) i jedną lub więcej kolumn wartości, które są różnego typu, takie jak ciągi, liczby całkowite i daty. Każdy obiekt przechowywany w bazie danych jest reprezentowany jako pojedynczy wiersz. „Relacyjna” część nazwy pochodzi od prawdopodobnie najważniejszej cechy technologii: tworzenia relacji między tabelami. Obiekt (wiersz) może mieć mapowanie jeden-do-jednego, jeden-do-wielu (klucz obcy) lub wiele-do-wielu na wiersze w innych tabelach. Omówimy to dalej w przykładach.
Django domyślnie używa SQLite3 do programowania. SQLite3 to prosty silnik relacyjnej bazy danych, a Twoja baza danych jest automatycznie tworzona jako db.sqlite3 przy pierwszym uruchomieniu python manage.py migrate
. W tym artykule będziemy kontynuować korzystanie z SQLite3, ale nie nadaje się on do użytku produkcyjnego, głównie dlatego, że nadpisywanie jest możliwe w przypadku równoczesnych użytkowników. W środowisku produkcyjnym lub podczas pisania systemu, który pewnego dnia zamierzasz wdrożyć, użyj PostgreSQL lub MySQL.
Django wykorzystuje modele do komunikacji z bazą danych. Korzystając z części ORM Django, plik records/models.py zawiera wiele modeli, co pozwala na określenie pól, właściwości i metod dla każdego obiektu. Tworząc modele, dążymy do architektury „grubego modelu”, w granicach rozsądku. Oznacza to, że jak najwięcej walidacji danych, analizowania, przetwarzania, logiki biznesowej, obsługi wyjątków, rozwiązywania przypadków brzegowych i podobnych zadań, jak to możliwe, powinno być obsłużonych w specyfikacji samego modelu. Pod maską, modele Django są bardzo złożonymi, funkcjonalnymi obiektami z szeroko użytecznym domyślnym zachowaniem. Dzięki temu architektura „grubego modelu” jest łatwa do osiągnięcia nawet bez pisania znacznej ilości kodu.
Przejdźmy przez trzy modele w naszej przykładowej aplikacji. Nie możemy omówić wszystkiego, ponieważ ma to być artykuł wprowadzający, a nie pełna dokumentacja frameworka Django, ale podkreślę najważniejsze wybory, których dokonałem przy konstruowaniu tych prostych modeli.
Klasa Book
jest najprostszym z modeli. Tutaj jest z 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)
Wszystkie pola CharField
wymagają określonego atrybutu max_length
. Umowna długość to 150 znaków, które podwoiłem dla title
w przypadku bardzo długich tytułów. Oczywiście nadal istnieje arbitralny limit, który można przekroczyć. Aby uzyskać nieograniczoną długość tekstu, użyj TextField
. published
pole to DateField
. Czas publikacji książki nie ma znaczenia, ale gdyby tak było, DateTimeField
. Wreszcie numer ISBN jest liczbą całkowitą (numery ISBN mają 10 lub 13 cyfr, a zatem wszystkie mieszczą się w maksymalnej wartości liczby całkowitej) i używamy unique=True
, ponieważ żadne dwie książki nie mogą mieć tego samego numeru ISBN, który jest następnie wymuszany na poziomie bazy danych.
Wszystkie obiekty mają metodę __str__(self)
, która definiuje ich reprezentację w postaci ciągu. Zastępujemy domyślną implementację dostarczoną przez klasę models.Model
i zamiast tego przedstawiamy książki jako „tytuł autora” we wszystkich miejscach, w których model byłby reprezentowany jako ciąg. Przypomnijmy, że poprzednio używaliśmy list_display
w obiekcie admina Book
, aby określić, jakie pola będą wyświetlane na liście panelu administratora. Jeśli ten list_display
nie jest obecny, lista administratorów zamiast tego pokazuje ciąg reprezentujący model, tak jak ma to miejsce w przypadku Patron
i Copy
.
Na koniec mamy metodę w Book
, którą wywołaliśmy w jej akcji administratora, którą napisaliśmy wcześniej. Ta funkcja tworzy Copy
, która jest powiązana z daną instancją Book
w bazie danych.
Przechodząc do Patron
, model ten wprowadza koncepcję relacji jeden-do-jednego, w tym przypadku z wbudowanym modelem User
. Sprawdź to z 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
Pole user
nie jest dokładnie funkcją bijektywną. MOŻE istnieć instancja User
bez powiązanej instancji Patron
. Jednak User
NIE MOŻE być powiązany z więcej niż jedną instancją Patron
, a Patron
nie może istnieć bez dokładnie jednego związku z użytkownikiem. Jest to wymuszane na poziomie bazy danych i jest gwarantowane przez specyfikację on_delete=models.CASCADE
: jeśli instancja User
zostanie usunięta, powiązany Profile
zostanie usunięty.
Inne pola i __str__(self)
, które widzieliśmy wcześniej. Warto zauważyć, że możesz sięgnąć poprzez relację jeden do jednego, aby uzyskać atrybuty, w tym przypadku user.username
, w funkcjach modelu.
Aby rozwinąć użyteczność relacji bazodanowych, zwróćmy uwagę na 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()
Ponownie, większość z nich widzieliśmy już wcześniej, więc skupmy się na nowych rzeczach: models.ForeignKey
. Copy
musi należeć do jednej Book
, ale biblioteka może mieć wiele Copy
każdej Book
. Book
może istnieć w bazie danych bez biblioteki zawierającej Copy
w swoim katalogu, ale Copy
nie może istnieć bez podstawowej Book
.
Ta złożona zależność jest wyrażona następującym wierszem:
book = models.ForeignKey(Book, on_delete=models.CASCADE)
Sposób usuwania jest taki sam, jak w przypadku Patron
w odniesieniu do User
.
Relacja między Copy
a Patron
jest nieco inna. Copy
może zostać pobrana do maksymalnie jednego Patron
, ale każdy Patron
może pobrać tyle Copy
, na ile pozwala mu biblioteka. Nie jest to jednak relacja trwała, czasami Copy
nie jest wyrejestrowana. Patron
i Copy
istnieją niezależnie od siebie w bazie danych; usunięcie wystąpienia jednego nie powinno usunąć żadnego wystąpienia drugiego.
Ta relacja jest nadal przypadkiem użycia klucza obcego, ale z różnymi argumentami:
out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL)
W tym przypadku ustawienie blank=True
pozwala formularzom akceptować None
jako wartość relacji, a null=True
umożliwia, aby kolumna dla relacji Patron
w tabeli Copy
w bazie danych przyjmowała null
jako wartość. Zachowanie usuwania, które zostanie wyzwolone na Copy
, jeśli instancja Patron
zostanie usunięta, gdy ta Copy
została wyrejestrowana, polega na zerwaniu relacji, pozostawiając Copy
nienaruszoną, ustawiając pole Patron
na null.
Ten sam typ pola, models.ForeignKey
, może wyrażać bardzo różne relacje między obiektami. Jedyną relacją, której nie mogłem dokładnie dopasować w tym przykładzie, jest pole wiele do wielu, które jest jak pole jeden do jednego, z tą różnicą, że jak sugeruje nazwa, każda instancja może być powiązana z wieloma innymi instancjami a każdy inny i każdy z nich może być powiązany z wieloma innymi, na przykład jak książka może mieć wielu autorów, z których każdy napisał wiele książek.
Migracje
Być może zastanawiasz się, skąd baza danych wie, co jest wyrażone w modelu. Z mojego doświadczenia wynika, że migracje są jedną z tych rzeczy, które są dość proste, dopóki nie są, a potem zjadają twoją twarz. Oto, jak zachować swój kubek w nienaruszonym stanie, dla początkujących: dowiedz się o migracjach i sposobach interakcji z nimi, ale staraj się unikać ręcznych zmian w plikach migracji. Jeśli już wiesz, co robisz, pomiń tę sekcję i obserwuj to, co działa dla Ciebie.
Tak czy inaczej, sprawdź oficjalną dokumentację, aby uzyskać pełne omówienie tematu.
Migracje przekładają zmiany w modelu na zmiany w schemacie bazy danych. Nie musisz ich pisać samodzielnie, Django tworzy je za pomocą polecenia python manage.py makemigrations
. Należy uruchomić to polecenie podczas tworzenia nowego modelu lub edycji pól istniejącego modelu, ale nie ma takiej potrzeby podczas tworzenia lub edycji metod modelu. Należy zauważyć, że migracje istnieją jako łańcuch, a każda z nich odwołuje się do poprzedniej, dzięki czemu można bezbłędnie edytować schemat bazy danych. Dlatego jeśli współpracujesz nad projektem, ważne jest, aby zachować jedną spójną historię migracji w kontroli wersji. Jeśli istnieją niezastosowane migracje, uruchom python manage.py migrate
, aby zastosować je przed uruchomieniem serwera.
Przykładowy projekt jest dystrybuowany z pojedynczą migracją, records/migrations/0001_initial.py . Ponownie, jest to automatycznie generowany kod, którego nie powinieneś edytować, więc nie będę go tutaj kopiował, ale jeśli chcesz zorientować się, co dzieje się za kulisami, spójrz na to.
Oprawy
W przeciwieństwie do migracji, osprzęt nie jest powszechnym aspektem rozwoju Django. Używam ich do dystrybucji przykładowych danych z artykułami i nigdy nie używałem ich w inny sposób. Ponieważ jednak korzystaliśmy z jednego wcześniej, czuję się zmuszony do wprowadzenia tematu.
Tym razem oficjalna dokumentacja jest trochę cienka na ten temat. Ogólnie rzecz biorąc, powinieneś wiedzieć, że urządzenia to sposób na importowanie i eksportowanie danych z bazy danych w różnych formatach, w tym JSON, którego używam. Ta funkcja ma głównie na celu pomóc w takich rzeczach, jak automatyczne testowanie i nie jest systemem tworzenia kopii zapasowych ani sposobem na edycję danych w działającej bazie danych. Co więcej, urządzenia nie są aktualizowane podczas migracji i jeśli spróbujesz zastosować urządzenie do bazy danych z niezgodnym schematem, nie powiedzie się.
Aby wygenerować osprzęt dla całej bazy danych, uruchom:
python manage.py dumpdata --format json > fixture.json
Aby załadować urządzenie, uruchom:
python manage.py loaddata fixture.json
Wniosek
Pisanie modeli w Django to ogromny temat, a korzystanie z panelu administracyjnego to kolejny. W 3000 słów udało mi się tylko przedstawić każdy. Mamy nadzieję, że korzystanie z panelu administracyjnego zapewniło lepszy interfejs do odkrywania, w jaki sposób modele działają i są ze sobą powiązane, dając pewność eksperymentowania i opracowywania własnych relacyjnych reprezentacji danych.
Jeśli szukasz łatwego miejsca do rozpoczęcia, spróbuj dodać model Librarian
, który dziedziczy po User
, tak jak robi to Profile
. Aby uzyskać większe wyzwanie, spróbuj zaimplementować historię kasy dla każdej Copy
i/lub Patron
(jest kilka sposobów na osiągnięcie tego).
Django Highlights to seria wprowadzająca ważne koncepcje tworzenia stron internetowych w Django. Każdy artykuł jest napisany jako samodzielny przewodnik po aspekcie rozwoju Django, który ma pomóc programistom i projektantom front-end w osiągnięciu głębszego zrozumienia „drugiej połowy” bazy kodu. Artykuły te są w większości skonstruowane tak, aby pomóc Ci zrozumieć teorię i konwencję, ale zawierają kilka przykładów kodu napisanych w Django 3.0.
Dalsza lektura
Możesz zainteresować się następującymi artykułami i dokumentacją.
- Najważniejsze cechy Django: modele użytkownika i uwierzytelnianie (część 1)
- Najważniejsze cechy Django: Szablonowanie zapisuje wiersze (część 2)
- Najważniejsze cechy Django: walka o zasoby statyczne i pliki multimedialne (część 4)
- Dokumentacja administratora Django
- Modele Django
- Opis pola modelu Django
- Migracje Django