Saya menulis proyek di Django dan saya melihat bahwa 80% dari kode ada di file models.py
. Kode ini membingungkan dan, setelah waktu tertentu, saya berhenti memahami apa yang sebenarnya terjadi.
Inilah yang menggangguku:
- Saya merasa jelek bahwa level model saya (yang seharusnya hanya bertanggung jawab atas pekerjaan dengan data dari database) juga mengirim email, berjalan di API ke layanan lain, dll.
- Juga, saya merasa tidak dapat diterima untuk menempatkan logika bisnis dalam tampilan, karena cara ini menjadi sulit untuk dikendalikan. Sebagai contoh, dalam aplikasi saya setidaknya ada tiga cara untuk membuat contoh baru
User
, tetapi secara teknis harus membuatnya secara seragam. - Saya tidak selalu memperhatikan ketika metode dan properti model saya menjadi non-deterministik dan ketika mereka mengembangkan efek samping.
Ini adalah contoh sederhana. Pada awalnya, User
modelnya seperti ini:
class User(db.Models):
def get_present_name(self):
return self.name or 'Anonymous'
def activate(self):
self.status = 'activated'
self.save()
Seiring waktu, itu berubah menjadi ini:
class User(db.Models):
def get_present_name(self):
# property became non-deterministic in terms of database
# data is taken from another service by api
return remote_api.request_user_name(self.uid) or 'Anonymous'
def activate(self):
# method now has a side effect (send message to user)
self.status = 'activated'
self.save()
send_mail('Your account is activated!', '…', [self.email])
Yang saya inginkan adalah memisahkan entitas dalam kode saya:
- Entitas basis data saya, tingkat basis data: Apa yang mengandung aplikasi saya?
- Entitas aplikasi saya, tingkat logika bisnis: Apa yang dapat membuat aplikasi saya?
Apa praktik baik untuk menerapkan pendekatan seperti itu yang dapat diterapkan di Django?
Jawaban:
Sepertinya Anda bertanya tentang perbedaan antara model data dan model domain - yang terakhir adalah di mana Anda dapat menemukan logika dan entitas bisnis seperti yang dirasakan oleh pengguna akhir Anda, yang pertama adalah tempat Anda benar-benar menyimpan data Anda.
Selain itu, saya telah menafsirkan bagian ke-3 dari pertanyaan Anda sebagai: bagaimana memperhatikan kegagalan untuk memisahkan model-model ini.
Ini adalah dua konsep yang sangat berbeda dan selalu sulit untuk memisahkannya. Namun, ada beberapa pola dan alat umum yang dapat digunakan untuk tujuan ini.
Tentang Model Domain
Hal pertama yang perlu Anda ketahui adalah bahwa model domain Anda tidak benar-benar tentang data; ini tentang tindakan dan pertanyaan seperti "aktifkan pengguna ini", "nonaktifkan pengguna ini", "pengguna mana yang saat ini diaktifkan?", dan "siapa nama pengguna ini?". Dalam istilah klasik: ini tentang kueri dan perintah .
Berpikir dalam Perintah
Mari kita mulai dengan melihat perintah dalam contoh Anda: "aktifkan pengguna ini" dan "nonaktifkan pengguna ini". Hal yang menyenangkan tentang perintah adalah bahwa mereka dapat dengan mudah diekspresikan oleh skenario yang diberikan saat kecil:
Skenario seperti itu berguna untuk melihat bagaimana bagian-bagian berbeda dari infrastruktur Anda dapat dipengaruhi oleh satu perintah - dalam hal ini database Anda (semacam bendera 'aktif'), server mail Anda, log sistem Anda, dll.
Skenario seperti itu juga sangat membantu Anda dalam menyiapkan lingkungan Pengembangan Berbasis Tes.
Dan akhirnya, berpikir dalam perintah sangat membantu Anda membuat aplikasi berorientasi tugas. Pengguna Anda akan menghargai ini :-)
Mengekspresikan Perintah
Django menyediakan dua cara mudah untuk mengekspresikan perintah; keduanya adalah opsi yang valid dan bukan tidak biasa untuk menggabungkan kedua pendekatan.
Lapisan layanan
The modul layanan telah dijelaskan oleh @Hedde . Di sini Anda mendefinisikan modul terpisah dan setiap perintah direpresentasikan sebagai suatu fungsi.
services.py
Menggunakan formulir
Cara lain adalah dengan menggunakan Formulir Django untuk setiap perintah. Saya lebih suka pendekatan ini, karena menggabungkan beberapa aspek yang terkait erat:
forms.py
Berpikir dalam Pertanyaan
Contoh Anda tidak mengandung pertanyaan apa pun, jadi saya mengambil kebebasan untuk membuat beberapa pertanyaan yang bermanfaat. Saya lebih suka menggunakan istilah "pertanyaan", tetapi pertanyaan adalah terminologi klasik. Pertanyaan yang menarik adalah: "Apa nama pengguna ini?", "Bisakah pengguna ini login?", "Tunjukkan daftar pengguna yang dinonaktifkan", dan "Apa distribusi geografis dari pengguna yang dinonaktifkan?"
Sebelum memulai menjawab pertanyaan ini, Anda harus selalu bertanya pada diri sendiri dua pertanyaan: apakah ini permintaan presentasi hanya untuk template saya, dan / atau permintaan logika bisnis terkait dengan menjalankan perintah saya, dan / atau permintaan pelaporan .
Pertanyaan presentasi hanya dibuat untuk meningkatkan antarmuka pengguna. Jawaban untuk pertanyaan logika bisnis secara langsung mempengaruhi pelaksanaan perintah Anda. Pertanyaan pelaporan hanya untuk tujuan analitis dan memiliki batasan waktu yang lebih longgar. Kategori-kategori ini tidak saling eksklusif.
Pertanyaan lainnya adalah: "Apakah saya memiliki kendali penuh atas jawaban?" Misalnya, ketika menanyakan nama pengguna (dalam konteks ini) kami tidak memiliki kendali atas hasilnya, karena kami mengandalkan API eksternal.
Membuat Pertanyaan
Permintaan paling mendasar di Django adalah penggunaan objek Manajer:
Tentu saja, ini hanya berfungsi jika data benar-benar terwakili dalam model data Anda. Ini tidak selalu terjadi. Dalam hal ini, Anda dapat mempertimbangkan opsi di bawah ini.
Tag dan filter khusus
Alternatif pertama berguna untuk kueri yang hanya bersifat presentasi: tag khusus dan filter template.
template.html
template_tags.py
Metode kueri
Jika kueri Anda bukan hanya presentasi, Anda bisa menambahkan kueri ke services.py Anda (jika Anda menggunakannya), atau memperkenalkan modul queries.py :
queries.py
Model proksi
Model proxy sangat berguna dalam konteks logika bisnis dan pelaporan. Anda pada dasarnya mendefinisikan subset yang disempurnakan dari model Anda. Anda bisa mengganti QuerySet basis Manajer dengan mengganti
Manager.get_queryset()
metode.models.py
Model permintaan
Untuk kueri yang secara inheren rumit, tetapi dieksekusi cukup sering, ada kemungkinan model kueri. Model kueri adalah bentuk denormalisasi di mana data yang relevan untuk satu kueri disimpan dalam model terpisah. Triknya tentu saja adalah menjaga agar model yang dinormalisasi tetap sinkron dengan model utama. Model kueri hanya dapat digunakan jika perubahan sepenuhnya di bawah kendali Anda.
models.py
Opsi pertama adalah memperbarui model-model ini dalam perintah Anda. Ini sangat berguna jika model ini hanya diubah oleh satu atau dua perintah.
forms.py
Pilihan yang lebih baik adalah menggunakan sinyal khusus. Sinyal-sinyal ini tentu saja dipancarkan oleh perintah Anda. Sinyal memiliki keuntungan bahwa Anda dapat tetap menyinkronkan beberapa model permintaan dengan model asli Anda. Selanjutnya, pemrosesan sinyal dapat diturunkan ke tugas-tugas latar belakang, menggunakan Seledri atau kerangka kerja serupa.
signal.py
forms.py
models.py
Menjaga kebersihannya
Saat menggunakan pendekatan ini, sangat mudah untuk menentukan apakah kode Anda tetap bersih. Cukup ikuti panduan ini:
Hal yang sama berlaku untuk pandangan (karena pandangan sering menderita dari masalah yang sama).
Beberapa Referensi
Dokumentasi Django: model proxy
Dokumentasi Django: sinyal
Arsitektur: Desain Berbasis Domain
sumber
User.objects.inactive_users()
. Tetapi contoh model proksi di sini IMO mengarah ke semantik yang salah:u = InactiveUser.objects.all()[0]; u.active = True; u.save()
dan belumisinstance(u, InactiveUser) == True
. Juga saya akan menyebutkan cara yang efektif untuk mempertahankan model permintaan dalam banyak kasus adalah dengan tampilan db.Saya biasanya menerapkan lapisan layanan di antara tampilan dan model. Ini bertindak seperti API proyek Anda dan memberi Anda pandangan helikopter yang baik tentang apa yang sedang terjadi. Saya mewarisi praktik ini dari seorang rekan saya yang banyak menggunakan teknik pelapisan ini dengan proyek Java (JSF), misalnya:
models.py
services.py
views.py
sumber
Pertama-tama, Jangan ulangi diri Anda sendiri .
Maka, harap berhati-hati untuk tidak overengineer, kadang-kadang hanya buang-buang waktu, dan membuat seseorang kehilangan fokus pada apa yang penting. Tinjau zen python dari waktu ke waktu.
Lihatlah proyek aktif
yang repositori kain juga satu yang baik untuk melihat.
yourapp/models/logicalgroup.py
User
,Group
dan model terkait bisa gagalyourapp/models/users.py
Poll
,Question
,Answer
... bisa pergi di bawahyourapp/models/polls.py
__all__
dalamyourapp/models/__init__.py
Lebih lanjut tentang MVC
request.GET
/request.POST
... dlltastypie
ataupiston
Manfaatkan middleware / templatetags
Manfaatkan manajer model
User
bisa masuk aUserManager(models.Manager)
.models.Model
.queryset
bisa masuk amodels.Manager
.User
satu per satu, jadi Anda mungkin berpikir bahwa itu harus hidup pada model itu sendiri, tetapi saat membuat objek, Anda mungkin tidak memiliki semua detail:Contoh:
Manfaatkan formulir jika memungkinkan
Banyak kode boilerplate dapat dihilangkan jika Anda memiliki formulir yang memetakan ke model. The
ModelForm documentation
cukup bagus. Memisahkan kode untuk formulir dari kode model bisa baik jika Anda memiliki banyak penyesuaian (atau terkadang menghindari kesalahan impor siklik untuk penggunaan yang lebih lanjut).Gunakan perintah manajemen bila memungkinkan
yourapp/management/commands/createsuperuser.py
yourapp/management/commands/activateinbulk.py
jika Anda memiliki logika bisnis, Anda dapat memisahkannya
django.contrib.auth
menggunakan backend , sama seperti db memiliki backend ... dll.setting
untuk logika bisnis Anda (mis.AUTHENTICATION_BACKENDS
)django.contrib.auth.backends.RemoteUserBackend
yourapp.backends.remote_api.RemoteUserBackend
yourapp.backends.memcached.RemoteUserBackend
contoh backend:
bisa menjadi:
lebih lanjut tentang pola desain
lebih lanjut tentang batas antarmuka
yourapp.models
yourapp.vendor
yourapp.libs
yourapp.libs.vendor
atauyourapp.vendor.libs
Singkatnya, Anda bisa melakukannya
yourapp/core/backends.py
yourapp/core/models/__init__.py
yourapp/core/models/users.py
yourapp/core/models/questions.py
yourapp/core/backends.py
yourapp/core/forms.py
yourapp/core/handlers.py
yourapp/core/management/commands/__init__.py
yourapp/core/management/commands/closepolls.py
yourapp/core/management/commands/removeduplicates.py
yourapp/core/middleware.py
yourapp/core/signals.py
yourapp/core/templatetags/__init__.py
yourapp/core/templatetags/polls_extras.py
yourapp/core/views/__init__.py
yourapp/core/views/users.py
yourapp/core/views/questions.py
yourapp/core/signals.py
yourapp/lib/utils.py
yourapp/lib/textanalysis.py
yourapp/lib/ratings.py
yourapp/vendor/backends.py
yourapp/vendor/morebusinesslogic.py
yourapp/vendor/handlers.py
yourapp/vendor/middleware.py
yourapp/vendor/signals.py
yourapp/tests/test_polls.py
yourapp/tests/test_questions.py
yourapp/tests/test_duplicates.py
yourapp/tests/test_ratings.py
atau apa pun yang membantu Anda; menemukan antarmuka yang Anda butuhkan dan batas - batas akan membantu Anda.
sumber
Django menggunakan jenis MVC yang sedikit dimodifikasi. Tidak ada konsep "pengontrol" di Django. Proxy terdekat adalah "tampilan", yang cenderung menyebabkan kebingungan dengan konversi MVC karena dalam MVC tampilan lebih mirip "templat" Django.
Di Django, "model" bukan sekadar abstraksi basis data. Dalam beberapa hal, ia berbagi tugas dengan "tampilan" Django sebagai pengontrol MVC. Itu memegang keseluruhan perilaku yang terkait dengan sebuah instance. Jika instance itu perlu berinteraksi dengan API eksternal sebagai bagian dari perilakunya, maka itu masih kode model. Faktanya, model tidak diharuskan untuk berinteraksi dengan database sama sekali, sehingga Anda dapat memiliki model yang sepenuhnya ada sebagai lapisan interaktif ke API eksternal. Ini adalah konsep "model" yang jauh lebih bebas.
sumber
Dalam Django, struktur MVC adalah seperti yang dikatakan Chris Pratt, berbeda dari model MVC klasik yang digunakan dalam kerangka kerja lain, saya pikir alasan utama untuk melakukan ini adalah menghindari struktur aplikasi yang terlalu ketat, seperti yang terjadi pada kerangka kerja MVC lainnya seperti CakePHP.
Di Django, MVC dilaksanakan dengan cara berikut:
Lapisan tampilan dibagi menjadi dua. Pandangan harus digunakan hanya untuk mengelola permintaan HTTP, mereka dipanggil dan menanggapinya. Tampilan berkomunikasi dengan seluruh aplikasi Anda (formulir, formulir model, kelas khusus, dalam kasus sederhana langsung dengan model). Untuk membuat antarmuka, kami menggunakan Template. Template mirip-string ke Django, memetakan konteks ke dalamnya, dan konteks ini dikomunikasikan ke tampilan oleh aplikasi (ketika tampilan bertanya).
Lapisan Model memberikan enkapsulasi, abstraksi, validasi, intelijen, dan membuat data Anda berorientasi objek (kata mereka suatu hari nanti DBMS juga akan). Ini tidak berarti bahwa Anda harus membuat file models.py besar (sebenarnya saran yang sangat bagus adalah untuk membagi model Anda menjadi file yang berbeda, masukkan ke dalam folder yang disebut 'model', buat file '__init__.py' ke dalam file ini. folder di mana Anda mengimpor semua model Anda dan akhirnya menggunakan atribut 'app_label' dari models.Model kelas). Model harus abstrak Anda dari operasi dengan data, itu akan membuat aplikasi Anda lebih sederhana. Anda juga harus, jika diperlukan, membuat kelas eksternal, seperti "alat" untuk model Anda. Anda juga dapat menggunakan warisan dalam model, mengatur atribut 'abstrak' dari kelas Meta model Anda ke 'Benar'.
Di mana sisanya? Nah, aplikasi web kecil umumnya adalah semacam antarmuka untuk data, dalam beberapa kasus program kecil menggunakan tampilan untuk permintaan atau memasukkan data sudah cukup. Kasus yang lebih umum akan menggunakan Formulir atau ModelForms, yang sebenarnya "pengendali". Ini tidak lain dari solusi praktis untuk masalah umum, dan sangat cepat. Itulah yang digunakan situs web untuk dilakukan.
Jika Formulir tidak enogh untuk Anda, maka Anda harus membuat kelas Anda sendiri untuk melakukan keajaiban, contoh yang sangat bagus dari ini adalah aplikasi admin: Anda dapat membaca kode ModelAmin, ini sebenarnya berfungsi sebagai pengontrol. Tidak ada struktur standar, saya sarankan Anda untuk memeriksa aplikasi Django yang ada, itu tergantung pada setiap kasus. Inilah yang dimaksudkan pengembang Django, Anda dapat menambahkan kelas parser xml, kelas konektor API, menambahkan Seledri untuk melakukan tugas, memutar untuk aplikasi berbasis reaktor, hanya menggunakan ORM, membuat layanan web, memodifikasi aplikasi admin dan banyak lagi. .. Ini adalah tanggung jawab Anda untuk membuat kode kualitas yang baik, menghormati filosofi MVC atau tidak, membuatnya berdasarkan modul dan membuat lapisan abstraksi Anda sendiri. Ini sangat fleksibel.
Saran saya: baca kode sebanyak yang Anda bisa, ada banyak aplikasi Django, tetapi jangan menganggapnya serius. Setiap kasus berbeda, pola dan teori membantu, tetapi tidak selalu, ini merupakan pengalaman yang tidak tepat, Django hanya memberikan Anda alat yang baik yang dapat Anda gunakan untuk menghilangkan beberapa rasa sakit (seperti antarmuka admin, validasi formulir web, i18n, penerapan pola pengamat, semua yang disebutkan sebelumnya dan lainnya), tetapi desain yang baik datang dari desainer berpengalaman.
PS:. Gunakan kelas 'Pengguna' dari aplikasi auth (dari standar Django), Anda dapat membuat misalnya profil pengguna, atau setidaknya membaca kode, itu akan berguna untuk kasus Anda.
sumber
Sebuah pertanyaan lama, tapi saya tetap ingin menawarkan solusi saya. Ini didasarkan pada penerimaan bahwa objek model juga memerlukan beberapa fungsionalitas tambahan sementara itu canggung untuk menempatkannya di dalam models.py . Logika bisnis yang berat dapat ditulis secara terpisah tergantung pada selera pribadi, tetapi saya setidaknya suka model untuk melakukan segala sesuatu yang berkaitan dengan dirinya sendiri. Solusi ini juga mendukung mereka yang suka menempatkan semua logika di dalam model itu sendiri.
Karena itu, saya merancang hack yang memungkinkan saya untuk memisahkan logika dari definisi model dan masih mendapatkan semua petunjuk dari IDE saya.
Keuntungannya harus jelas, tetapi ini mencantumkan beberapa yang telah saya amati:
Saya telah menggunakan ini dengan Python 3.4 dan lebih besar dan Django 1.8 dan lebih besar.
app / models.py
app / logic / user.py
Satu-satunya hal yang saya tidak tahu adalah bagaimana membuat IDE saya (PyCharm dalam kasus ini) mengenali bahwa UserLogic sebenarnya adalah model Pengguna. Tapi karena ini jelas hack, saya cukup senang menerima sedikit gangguan dari selalu menentukan tipe untuk
self
parameter.sumber
Saya harus setuju dengan Anda. Ada banyak kemungkinan dalam Django tetapi tempat terbaik untuk memulai adalah meninjau filosofi desain Django .
Memanggil API dari properti model tidak akan ideal, sepertinya akan lebih masuk akal untuk melakukan sesuatu seperti ini dalam tampilan dan mungkin membuat lapisan layanan untuk menjaga hal-hal tetap kering. Jika panggilan ke API non-blocking dan panggilan itu mahal, mengirimkan permintaan ke pekerja layanan (pekerja yang mengkonsumsi dari antrian) mungkin masuk akal.
Sesuai model filosofi desain Django merangkum setiap aspek dari "objek". Jadi semua logika bisnis yang terkait dengan objek itu harus tinggal di sana:
Efek samping yang Anda jelaskan jelas, logika di sini bisa lebih baik dipecah menjadi Querysets dan manajer. Berikut ini sebuah contoh:
models.py
admin.py
sumber
Saya sebagian besar setuju dengan jawaban yang dipilih ( https://stackoverflow.com/a/12857584/871392 ), tetapi ingin menambahkan opsi di bagian Making Queries.
Satu dapat mendefinisikan kelas QuerySet untuk model untuk membuat permintaan filter dan sebagainya. Setelah itu Anda dapat mem-proxy kelas queryset ini untuk manajer model, seperti yang dilakukan oleh manajer dan kelas QuerySet.
Meskipun, jika Anda harus meminta beberapa model data untuk mendapatkan satu model domain, tampaknya lebih masuk akal bagi saya untuk menempatkan ini dalam modul terpisah seperti yang disarankan sebelumnya.
sumber
Artikel paling komprehensif tentang berbagai opsi dengan pro dan kontra:
Sumber: https://sunscrapers.com/blog/where-to-put-business-logic-django/
sumber
Django dirancang agar mudah digunakan untuk mengirimkan halaman web. Jika Anda tidak nyaman dengan ini mungkin Anda harus menggunakan solusi lain.
Saya sedang mengerjakan root atau operasi umum pada model (untuk memiliki antarmuka yang sama) dan yang lain pada pengontrol model. Jika saya memerlukan operasi dari model lain saya mengimpor controller-nya.
Pendekatan ini cukup bagi saya dan kompleksitas aplikasi saya.
Tanggapan Hedde adalah contoh yang menunjukkan fleksibilitas django dan python itu sendiri.
Pertanyaan yang sangat menarik!
sumber