Sorotan Django: Model, Admin, Dan Memanfaatkan Basis Data Relasional (Bagian 3)
Diterbitkan: 2022-03-10Sebelum kita mulai, saya ingin mencatat bahwa kemampuan administratif bawaan Django, bahkan setelah penyesuaian, tidak dimaksudkan untuk pengguna akhir. Panel admin ada sebagai alat pengembang, operator, dan administrator untuk membuat dan memelihara perangkat lunak. Ini tidak dimaksudkan untuk digunakan untuk memberikan kemampuan moderasi kepada pengguna akhir atau kemampuan administrator lainnya di atas platform yang Anda kembangkan.
Artikel ini didasarkan pada hipotesis dalam dua bagian:
- Panel admin Django sangat intuitif sehingga pada dasarnya Anda sudah tahu bagaimana menggunakannya.
- Panel admin Django sangat kuat sehingga kita dapat menggunakannya sebagai alat untuk belajar tentang merepresentasikan data dalam database relasional menggunakan model Django.
Saya menawarkan ide-ide ini dengan peringatan bahwa kita masih perlu menulis beberapa kode konfigurasi untuk mengaktifkan kemampuan panel admin yang lebih kuat, dan kita masih perlu menggunakan ORM berbasis model Django (pemetaan relasional objek) untuk menentukan representasi data dalam sistem kami.
Bacaan yang Direkomendasikan
“Django Highlights” adalah seri yang memperkenalkan konsep penting pengembangan web di Django. Anda mungkin ingin membaca tentang menyediakan alur otentikasi pengguna yang aman dan mengikuti bersama demonstrasi penggunaan templating Django untuk menulis halaman yang kompleks.
Pengaturan
Kami akan bekerja dengan proyek sampel di artikel ini. Proyek ini memodelkan beberapa data yang akan disimpan perpustakaan tentang buku dan pelanggannya. Contoh tersebut harus dapat diterapkan secara adil untuk banyak jenis sistem yang mengelola pengguna dan/atau inventaris. Berikut ini adalah tampilan datanya:

Harap selesaikan langkah-langkah berikut untuk menjalankan kode contoh di mesin lokal Anda.
1. Memasang Paket
Dengan Python 3.6 atau lebih tinggi terinstal, buat direktori dan lingkungan virtual. Kemudian, instal paket-paket berikut:
pip install django django-grappelli Django adalah kerangka kerja web yang kami kerjakan dalam artikel ini. ( django-grappelli adalah tema panel admin yang akan kita bahas secara singkat.)
2. Mendapatkan Proyek
Dengan paket-paket sebelumnya terinstal, unduh kode contoh dari GitHub. Lari:
git clone https://github.com/philipkiely/library_records.git cd library_records/library3. Membuat Pengguna Super
Dengan menggunakan perintah berikut, siapkan database Anda dan buat pengguna super. Antarmuka baris perintah akan memandu Anda melalui proses pembuatan pengguna super. Akun pengguna super Anda akan segera menjadi cara Anda mengakses panel admin, jadi pastikan untuk mengingat kata sandi yang Anda tetapkan. Menggunakan:
python manage.py migrate python manage.py createsuperuser4. Memuat Data
Untuk penjelajahan kami, saya membuat kumpulan data yang disebut perlengkapan yang dapat Anda muat ke dalam database (lebih lanjut tentang cara membuat perlengkapan di akhir artikel). Gunakan perlengkapan untuk mengisi database Anda sebelum menjelajahinya di panel admin. Lari:
python manage.py loaddata ../fixture.json5. Menjalankan Proyek Contoh
Terakhir, Anda siap menjalankan kode contoh. Untuk menjalankan server, gunakan perintah berikut:
python manage.py runserverBuka browser Anda ke https://127.0.0.1:8000 untuk melihat proyek. Perhatikan bahwa Anda secara otomatis diarahkan ke panel admin di /admin/ . Saya menyelesaikannya dengan konfigurasi berikut di 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), ]dikombinasikan dengan pengalihan sederhana berikut di records/views.py :
from django.http import HttpResponseRedirect def index(request): return HttpResponseRedirect('/admin/')Menggunakan Panel Admin
Kami telah berhasil! Saat Anda memuat halaman Anda, Anda akan melihat sesuatu seperti berikut:

Tampilan ini dicapai dengan kode boilerplate berikut di 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) Tampilan ini akan memberi Anda pemahaman awal tentang data yang disimpan sistem. Saya akan menghapus beberapa misteri: Groups dan Users ditentukan oleh Django dan menyimpan informasi dan izin untuk akun pada sistem. Anda dapat membaca lebih lanjut tentang model User di artikel sebelumnya dalam seri ini. Books , Copys , dan Patrons adalah tabel dalam database yang kami buat saat menjalankan migrasi dan diisi dengan memuat perlengkapan. Catat bahwa Django secara naif membuat jamak nama model dengan menambahkan "s," bahkan dalam kasus seperti "salinan" di mana ejaannya salah.

Dalam proyek kami, Book adalah catatan dengan judul, penulis, tanggal penerbitan, dan ISBN (Nomor Buku Standar Internasional). Perpustakaan menyimpan Copy setiap Book , atau mungkin beberapa. Setiap Copy dapat diperiksa oleh Patron , atau saat ini dapat didaftarkan. Patron adalah perpanjangan dari User yang mencatat alamat dan tanggal lahir mereka.
Buat, Baca, Perbarui, Hancurkan
Salah satu kemampuan standar panel admin adalah menambahkan instance dari setiap model. Klik "buku" untuk membuka halaman model, dan klik tombol "Tambah Buku" di sudut kanan atas. Setelah itu, formulir akan ditampilkan, yang dapat Anda isi dan simpan untuk membuat buku.

Membuat Patron mengungkapkan kemampuan bawaan lain dari formulir buat admin: Anda dapat membuat model yang terhubung langsung dari formulir yang sama. Tangkapan layar di bawah ini menunjukkan pop-up yang dipicu oleh tanda plus hijau di sebelah kanan drop-down User . Dengan demikian, Anda dapat membuat kedua model di halaman admin yang sama.

Anda dapat membuat COPY melalui mekanisme yang sama.
Untuk setiap rekaman, Anda dapat mengklik baris untuk mengeditnya menggunakan formulir yang sama. Anda juga dapat menghapus catatan menggunakan tindakan admin.
Tindakan Admin
Meskipun kemampuan bawaan panel admin sangat berguna, Anda dapat membuat alat sendiri menggunakan tindakan admin. Kami akan membuat dua: satu untuk membuat salinan buku dan satu untuk memeriksa buku yang telah dikembalikan ke perpustakaan.
Untuk membuat Copy Book , buka URL /admin/records/book/ dan gunakan menu tarik-turun “Tindakan” untuk memilih “Tambahkan salinan buku” lalu gunakan kotak centang di kolom sebelah kiri tabel untuk memilih buku atau buku mana yang akan ditambahkan salinannya ke inventaris.

Membuat ini bergantung pada metode model yang akan kita bahas nanti. Kita dapat menyebutnya sebagai tindakan admin dengan membuat kelas ModelAdmin untuk model Profile sebagai berikut di 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) Properti list_display menunjukkan bidang mana yang digunakan untuk mewakili model di halaman ikhtisar model. Properti actions mencantumkan tindakan admin. Tindakan admin kami didefinisikan sebagai fungsi dalam BookAdmin dan mengambil tiga argumen: objek admin itu sendiri, permintaan (permintaan HTTP aktual yang dikirim oleh klien), dan queryset (daftar objek yang kotaknya dicentang). Kami melakukan tindakan yang sama pada setiap item dalam kumpulan kueri, lalu memberi tahu pengguna bahwa tindakan telah selesai. Setiap tindakan admin memerlukan deskripsi singkat sehingga dapat diidentifikasi dengan benar di menu tarik-turun. Terakhir, kita sekarang menambahkan BookAdmin saat mendaftarkan model.
Menulis tindakan admin untuk mengatur properti secara massal cukup berulang. Berikut kode untuk memeriksa Copy , perhatikan persamaannya yang hampir sama dengan tindakan sebelumnya.
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 Admin
Secara default, Django menyediakan gaya yang cukup sederhana untuk panel admin. Anda dapat membuat tema sendiri atau menggunakan tema pihak ketiga untuk memberikan tampilan baru pada panel admin. Salah satu tema open-source yang populer adalah grappelli, yang kami instal sebelumnya di artikel. Anda dapat memeriksa dokumentasi untuk kemampuan penuhnya.
Memasang tema cukup mudah, hanya membutuhkan dua baris. Pertama, tambahkan grappelli ke INSTALLED_APPS sebagai berikut di 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', ]Kemudian, sesuaikan 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), ]Dengan perubahan tersebut, panel admin akan terlihat seperti berikut:

Ada sejumlah tema lain di luar sana, dan sekali lagi Anda dapat mengembangkannya sendiri. Saya akan tetap menggunakan tampilan default untuk sisa artikel ini.

Memahami Model
Sekarang setelah Anda merasa nyaman dengan panel admin dan menggunakannya untuk menavigasi data, mari kita lihat model yang mendefinisikan struktur database kita. Setiap model mewakili satu tabel dalam database relasional.
Sebuah database relasional menyimpan data dalam satu atau lebih tabel. Masing-masing tabel ini memiliki struktur kolom tertentu, termasuk kunci utama (pengidentifikasi unik untuk setiap elemen) dan satu atau lebih kolom nilai, yang terdiri dari berbagai jenis seperti string, bilangan bulat, dan tanggal. Setiap objek yang disimpan dalam database direpresentasikan sebagai satu baris. Bagian "relasional" dari nama tersebut berasal dari fitur teknologi yang paling penting: menciptakan hubungan antar tabel. Sebuah objek (baris) dapat memiliki pemetaan satu-ke-satu, satu-ke-banyak (kunci asing), atau banyak-ke-banyak ke baris di tabel lain. Kami akan membahas ini lebih lanjut dalam contoh.
Django, secara default, menggunakan SQLite3 untuk pengembangan. SQLite3 adalah mesin basis data relasional sederhana dan basis data Anda secara otomatis dibuat sebagai db.sqlite3 saat pertama kali Anda menjalankan python manage.py migrate . Kami akan melanjutkan dengan SQLite3 untuk artikel ini, tetapi tidak cocok untuk penggunaan produksi, terutama karena penimpaan dimungkinkan dengan pengguna bersamaan. Dalam produksi, atau saat menulis sistem yang suatu hari ingin Anda terapkan, gunakan PostgreSQL atau MySQL.
Django menggunakan model untuk antarmuka dengan database. Menggunakan bagian dari ORM Django, file records/models.py menyertakan banyak model, yang memungkinkan untuk menetapkan bidang, properti, dan metode untuk setiap objek. Saat membuat model, kami berusaha keras untuk arsitektur "Model Gemuk", dengan alasan. Itu berarti bahwa validasi data, penguraian, pemrosesan, logika bisnis, penanganan pengecualian, penyelesaian kasus tepi, dan tugas serupa sebanyak mungkin harus ditangani dalam spesifikasi model itu sendiri. Di bawah tenda, model Django sangat kompleks, objek fitur dengan perilaku default yang sangat berguna. Ini membuat arsitektur "Model Gemuk" mudah dicapai bahkan tanpa menulis kode dalam jumlah besar.
Mari kita telusuri tiga model dalam contoh aplikasi kita. Kami tidak dapat mencakup semuanya, karena ini seharusnya menjadi artikel pengantar, bukan dokumentasi lengkap kerangka kerja Django, tetapi saya akan menyoroti pilihan paling penting yang saya buat dalam membangun model sederhana ini.
Kelas Book adalah model yang paling sederhana. Ini dia dari 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) Semua bidang CharField memerlukan atribut max_length yang ditentukan. Panjang konvensional adalah 150 karakter, yang saya gandakan untuk title jika judulnya sangat panjang. Tentu saja, masih ada batas sewenang-wenang, yang bisa dilampaui. Untuk panjang teks tak terbatas, gunakan TextField . Bidang yang published adalah DateField . Waktu buku itu diterbitkan tidak masalah, tetapi jika ya, saya akan menggunakan DateTimeField . Terakhir, ISBN adalah bilangan bulat (ISBN terdiri dari 10 atau 13 digit dan dengan demikian semuanya sesuai dengan nilai maksimum bilangan bulat) dan kami menggunakan unique=True karena tidak ada dua buku yang dapat memiliki ISBN yang sama, yang kemudian diterapkan di tingkat basis data.
Semua objek memiliki metode __str__(self) yang mendefinisikan representasi string mereka. Kami mengganti implementasi default yang disediakan oleh kelas models.Model dan sebagai gantinya mewakili buku sebagai "judul oleh penulis" di semua tempat di mana model akan direpresentasikan sebagai string. Ingat bahwa sebelumnya kita menggunakan list_display di objek admin Book untuk menentukan bidang apa yang akan ditampilkan dalam daftar panel admin. Jika list_display itu tidak ada, daftar admin malah menunjukkan representasi string dari model, seperti halnya untuk Patron dan Copy .
Akhirnya, kami memiliki metode di Book yang kami panggil dalam tindakan adminnya yang kami tulis sebelumnya. Fungsi ini membuat Copy yang terkait dengan contoh tertentu dari Book di database.
Beralih ke Patron , model ini memperkenalkan konsep one-to-one relationship, dalam hal ini dengan model built-in User . Lihat dari 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 Bidang user bukanlah fungsi bijektif. BISA menjadi contoh User tanpa contoh Patron terkait. Namun, User TIDAK BISA dikaitkan dengan lebih dari satu contoh Patron , dan Patron tidak dapat ada tanpa tepat satu hubungan dengan pengguna. Ini diberlakukan di tingkat basis data, dan dijamin oleh spesifikasi on_delete=models.CASCADE : jika instance User dihapus, Profile terkait akan dihapus.
Bidang lain dan __str__(self) yang telah kita lihat sebelumnya. Perlu dicatat bahwa Anda dapat menjangkau melalui relasi satu-ke-satu untuk mendapatkan atribut, dalam hal ini user.username , dalam fungsi model.
Untuk memperluas kegunaan relasi database, mari kita alihkan perhatian kita ke 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() Sekali lagi, kita telah melihat sebagian besar dari ini sebelumnya, jadi mari kita fokus pada hal-hal baru: models.ForeignKey . Copy harus dari satu Book , tetapi perpustakaan dapat memiliki beberapa Copy dari setiap Book . Sebuah Book bisa ada di database tanpa perpustakaan memiliki Copy di katalognya, tapi Copy tidak bisa ada tanpa Book yang mendasarinya.
Hubungan kompleks ini dinyatakan dengan baris berikut:
book = models.ForeignKey(Book, on_delete=models.CASCADE) Perilaku penghapusan sama dengan Patron yang mengacu pada User .
Hubungan antara Copy dan Patron sedikit berbeda. Copy dapat diperiksa hingga satu Patron , tetapi setiap Patron dapat memeriksa sebanyak mungkin Copy yang diizinkan perpustakaan. Namun, ini bukan hubungan permanen, Copy terkadang tidak diperiksa. Patron s dan Copy s ada secara independen satu sama lain dalam database; menghapus sebuah instance dari satu tidak harus menghapus setiap instance dari yang lain.
Hubungan ini masih merupakan kasus penggunaan untuk kunci asing, tetapi dengan argumen yang berbeda:
out_to = models.ForeignKey(Patron, blank=True, null=True, on_delete=models.SET_NULL) Di sini, memiliki blank=True memungkinkan formulir menerima None sebagai nilai untuk relasi dan null=True memungkinkan kolom untuk relasi Patron di tabel Copy dalam database menerima null sebagai nilai. Perilaku hapus, yang akan dipicu pada Copy jika contoh Patron dihapus saat mereka memiliki Copy yang diperiksa, adalah memutuskan hubungan sambil membiarkan Copy tetap utuh dengan menyetel bidang Patron ke nol.
Jenis bidang yang sama, models.ForeignKey , dapat mengekspresikan hubungan yang sangat berbeda antar objek. Satu relasi yang tidak dapat saya masukkan dengan jelas dalam contoh adalah bidang banyak-ke-banyak, yang seperti bidang satu-ke-satu, kecuali bahwa, seperti yang disarankan oleh namanya, setiap instance dapat dikaitkan dengan banyak instance lainnya dan masing-masing dan masing-masing dapat dihubungkan kembali ke banyak orang lain, seperti bagaimana sebuah buku dapat memiliki banyak penulis, yang masing-masing telah menulis banyak buku.
Migrasi
Anda mungkin bertanya-tanya bagaimana database mengetahui apa yang diekspresikan dalam model. Dalam pengalaman saya, migrasi adalah salah satu hal yang cukup mudah sampai tidak, dan kemudian mereka memakan wajah Anda. Berikut cara menjaga mug Anda tetap utuh, untuk pemula: pelajari tentang migrasi dan cara berinteraksi dengannya, tetapi cobalah untuk menghindari pengeditan manual pada file migrasi. Jika Anda sudah tahu apa yang Anda lakukan, lewati bagian ini dan lanjutkan apa yang berhasil untuk Anda.
Either way, periksa dokumentasi resmi untuk perawatan lengkap dari subjek.
Migrasi menerjemahkan perubahan dalam model ke perubahan skema database. Anda tidak harus menulisnya sendiri, Django membuatnya dengan perintah python manage.py makemigrations . Anda harus menjalankan perintah ini saat membuat model baru atau mengedit bidang model yang ada, tetapi tidak perlu melakukannya saat membuat atau mengedit metode model. Penting untuk dicatat bahwa migrasi ada sebagai rantai, masing-masing mereferensikan yang sebelumnya sehingga dapat melakukan pengeditan bebas kesalahan pada skema database. Jadi, jika Anda berkolaborasi dalam sebuah proyek, penting untuk menyimpan satu riwayat migrasi yang konsisten di kontrol versi. Jika ada migrasi yang belum diterapkan, jalankan python manage.py migrate untuk menerapkannya sebelum menjalankan server.
Contoh proyek didistribusikan dengan satu migrasi, records/migrations/0001_initial.py . Sekali lagi, ini adalah kode yang dibuat secara otomatis yang tidak perlu Anda edit, jadi saya tidak akan menyalinnya di sini, tetapi jika Anda ingin memahami apa yang terjadi di balik layar, silakan dan lihatlah.
Perlengkapan
Tidak seperti migrasi, perlengkapan bukanlah aspek umum dari pengembangan Django. Saya menggunakannya untuk mendistribusikan data sampel dengan artikel, dan tidak pernah menggunakannya sebaliknya. Namun, karena kami menggunakan satu sebelumnya, saya merasa terdorong untuk memperkenalkan topik tersebut.
Untuk sekali ini, dokumentasi resmi sedikit membahas topik ini. Secara keseluruhan, yang harus Anda ketahui adalah bahwa perlengkapan adalah cara mengimpor dan mengekspor data dari database Anda dalam berbagai format, termasuk JSON, yang saya gunakan. Fitur ini sebagian besar ada untuk membantu hal-hal seperti pengujian otomatis, dan bukan merupakan sistem cadangan atau cara untuk mengedit data dalam database langsung. Selain itu, perlengkapan tidak diperbarui dengan migrasi, dan jika Anda mencoba menerapkan perlengkapan ke database dengan skema yang tidak kompatibel, itu akan gagal.
Untuk menghasilkan perlengkapan untuk seluruh database, jalankan:
python manage.py dumpdata --format json > fixture.jsonUntuk memuat perlengkapan, jalankan:
python manage.py loaddata fixture.jsonKesimpulan
Menulis model di Django adalah topik besar, dan menggunakan panel admin adalah hal lain. Dalam 3.000 kata, saya hanya berhasil memperkenalkan masing-masing. Mudah-mudahan, menggunakan panel admin telah memberi Anda antarmuka yang lebih baik untuk mengeksplorasi bagaimana model bekerja dan berhubungan satu sama lain, memberi Anda kepercayaan diri untuk bereksperimen dan mengembangkan representasi data relasional Anda sendiri.
Jika Anda mencari tempat yang mudah untuk memulai, coba tambahkan model Librarian yang mewarisi dari User seperti yang dilakukan Profile . Untuk tantangan lebih lanjut, coba terapkan riwayat checkout untuk setiap Copy dan/atau Patron (ada beberapa cara untuk menyelesaikan yang satu ini).
Django Highlights adalah seri yang memperkenalkan konsep penting pengembangan web di Django. Setiap artikel ditulis sebagai panduan yang berdiri sendiri untuk aspek pengembangan Django yang dimaksudkan untuk membantu pengembang dan perancang front-end mencapai pemahaman yang lebih dalam tentang "separuh lainnya" dari basis kode. Artikel-artikel ini sebagian besar dibangun untuk membantu Anda memperoleh pemahaman tentang teori dan konvensi, tetapi berisi beberapa contoh kode yang ditulis dalam Django 3.0.
Bacaan lebih lanjut
Anda mungkin tertarik dengan artikel dan dokumentasi berikut.
- Sorotan Django: Model Pengguna Dan Otentikasi (Bagian 1)
- Sorotan Django: Templat Menyimpan Garis (Bagian 2)
- Sorotan Django: Perselisihan Aset Statis Dan Berkas Media (Bagian 4)
- Dokumentasi Admin Django
- Model Django
- Referensi Bidang Model Django
- Migrasi Django
