Masalah saya adalah bahwa saya memiliki model yang dapat mengambil salah satu dari dua kunci asing untuk mengatakan model apa itu. Saya ingin mengambil setidaknya satu tetapi tidak keduanya. Dapatkah saya memiliki ini masih menjadi satu model atau haruskah saya membaginya menjadi dua jenis. Ini kodenya:
class Inspection(models.Model):
InspectionID = models.AutoField(primary_key=True, unique=True)
GroupID = models.ForeignKey('PartGroup', on_delete=models.CASCADE, null=True, unique=True)
SiteID = models.ForeignKey('Site', on_delete=models.CASCADE, null=True, unique=True)
@classmethod
def create(cls, groupid, siteid):
inspection = cls(GroupID = groupid, SiteID = siteid)
return inspection
def __str__(self):
return str(self.InspectionID)
class InspectionReport(models.Model):
ReportID = models.AutoField(primary_key=True, unique=True)
InspectionID = models.ForeignKey('Inspection', on_delete=models.CASCADE, null=True)
Date = models.DateField(auto_now=False, auto_now_add=False, null=True)
Comment = models.CharField(max_length=255, blank=True)
Signature = models.CharField(max_length=255, blank=True)
Masalahnya adalah Inspection
model. Ini harus dikaitkan dengan salah satu grup atau situs, tetapi tidak keduanya. Saat ini dengan pengaturan ini perlu keduanya.
Saya lebih suka tidak harus membagi ini menjadi dua model yang hampir identik GroupInspection
dan SiteInspection
, jadi solusi apa pun yang menjadikannya sebagai satu model akan ideal.
django
django-models
CalMac
sumber
sumber
Inspection
kelas, dan kemudian subkelas ke dalamSiteInspection
danGroupInspection
untuk bagian-bagian yang tidak umum.unique=True
bagian dalam bidang FK Anda berarti bahwa hanya satuInspection
contoh yang bisa ada untuk satu contohGroupID
atauSiteID
contoh - TKI, itu adalah hubungan satu ke satu, bukan hubungan satu ke banyak. Benarkah ini yang Anda inginkan?Inspection
tautan antaraGroup
atauSite
danInspectionID
, maka saya dapat memiliki beberapa "inspeksi" dalam bentukInspectionReport
hubungan satu itu. Ini dilakukan agar saya bisa lebih mudah mengurutkan berdasarkanDate
semua catatan yang terkait dengan satuGroup
atauSite
. Harapan itu masuk akalJawaban:
Saya menyarankan agar Anda melakukan validasi seperti itu dengan cara Django
dengan menimpa
clean
metode Model Djangosumber
Seperti disebutkan dalam komentar, alasan bahwa "dengan pengaturan ini membutuhkan keduanya" hanya karena Anda lupa menambahkan
blank=True
ke bidang FK Anda, sehingga AndaModelForm
(salah satu yang kustom atau default yang dihasilkan oleh admin) akan membuat bidang formulir diperlukan . Pada tingkat skema db, Anda dapat mengisi keduanya, salah satu atau tidak satupun dari FK tersebut, akan baik-baik saja karena Anda membuat bidang-bidang db tersebut dapat dibatalkan (dengannull=True
argumen).Juga, (lihat komentar saya yang lain), Anda mungkin ingin memeriksa apakah Anda benar-benar ingin FK menjadi unik. Ini secara teknis mengubah hubungan satu ke banyak menjadi hubungan satu ke satu - Anda hanya diperbolehkan satu catatan 'inspeksi' tunggal untuk GroupID atau SiteId yang diberikan (Anda tidak dapat memiliki dua atau lebih 'inspeksi' untuk satu GroupId atau SiteId) . Jika itu BENAR-BENAR apa yang Anda inginkan, Anda mungkin ingin menggunakan OneToOneField eksplisit sebagai gantinya (skema db akan sama tetapi model akan lebih eksplisit dan deskriptor terkait jauh lebih dapat digunakan untuk kasus penggunaan ini).
Sebagai catatan tambahan: dalam Model Django, bidang ForeignKey terwujud sebagai contoh model terkait, bukan sebagai id mentah. TKI, mengingat ini:
maka
bar.foo
akan memutuskan untukfoo
, bukan untukfoo.id
. Jadi, Anda tentu ingin mengubah namaInspectionID
danSiteID
bidang menjadi benarinspection
dansite
. BTW, dengan Python, konvensi penamaan adalah 'all_lower_with_underscores' untuk hal lain selain nama kelas dan konstanta pseudo.Sekarang untuk pertanyaan inti Anda: tidak ada cara standar SQL spesifik untuk menegakkan batasan "satu atau yang lain" di tingkat basis data, jadi biasanya dilakukan dengan menggunakan PERIKSA kendala , yang dilakukan dalam model Django dengan meta "kendala" model. opsi .
Ini dikatakan, bagaimana kendala sebenarnya didukung dan ditegakkan di tingkat db tergantung pada vendor DB Anda (MySQL <8.0.16 jelas mengabaikannya misalnya), dan jenis kendala yang Anda butuhkan di sini tidak akan ditegakkan di formulir atau validasi tingkat model , hanya ketika mencoba menyimpan model, jadi Anda juga ingin menambahkan validasi pada level model (lebih disukai) atau validasi level form, dalam kedua kasus dalam model (resp.) model atau
clean()
metode form .Jadi untuk membuat cerita panjang pendek:
periksa dulu apakah Anda benar-benar menginginkan ini
unique=True
batasan , dan jika ya maka ganti bidang FK Anda dengan OneToOneField.tambah sebuah
blank=True
argumen ke bidang FK (atau OneToOne) Andaclean()
metode ke model Anda yang memeriksa apakah Anda memiliki salah satu atau bidang lain dan memunculkan kesalahan validasi yang laindan Anda harus baik-baik saja, dengan asumsi RDBMS Anda menghormati batasan pemeriksaan tentu saja.
Perhatikan bahwa, dengan desain ini,
Inspection
model Anda adalah tipuan yang sama sekali tidak berguna (namun mahal!) - Anda akan mendapatkan fitur yang sama persis dengan biaya lebih rendah dengan memindahkan FK (dan kendala, validasi dll) langsung keInspectionReport
.Sekarang mungkin ada solusi lain - jaga model Inspeksi, tetapi letakkan FK sebagai OneToOneField di ujung lain hubungan (di Situs dan Grup):
Dan kemudian Anda bisa mendapatkan semua laporan untuk Situs atau Grup tertentu
yoursite.inspection.inspectionreport_set.all()
.Ini menghindari keharusan menambahkan kendala atau validasi tertentu, tetapi dengan biaya tingkat tipuan tambahan (SQL
join
klausa dll).Solusi mana yang merupakan "yang terbaik" benar-benar tergantung pada konteks, jadi Anda harus memahami implikasi keduanya dan memeriksa bagaimana Anda biasanya menggunakan model Anda untuk mencari tahu mana yang lebih tepat untuk kebutuhan Anda sendiri. Sejauh yang saya ketahui dan tanpa lebih banyak konteks (atau ragu-ragu) saya lebih suka menggunakan solusi dengan tingkat tipuan yang lebih sedikit, tetapi YMMV.
NB tentang hubungan generik: itu bisa berguna ketika Anda benar-benar memiliki banyak model terkait yang mungkin dan / atau tidak tahu sebelumnya model mana yang ingin dihubungkan dengan milik Anda. Ini sangat berguna untuk aplikasi yang dapat digunakan kembali (pikirkan fitur "komentar" atau "tag" dll) atau yang dapat dikembangkan (kerangka kerja manajemen konten, dll.). The downside adalah bahwa itu membuat permintaan lebih berat (dan agak tidak praktis ketika Anda ingin melakukan permintaan manual pada db Anda). Dari pengalaman, mereka dapat dengan cepat menjadi PITA bot wrt / kode dan perf, jadi lebih baik menyimpannya ketika tidak ada solusi yang lebih baik (dan / atau ketika overhead pemeliharaan dan runtime tidak menjadi masalah).
2 sen saya.
sumber
Django memiliki antarmuka baru (sejak 2.2) untuk membuat batasan DB: https://docs.djangoproject.com/en/3.0/ref/models/constraints/
Anda dapat menggunakan a
CheckConstraint
untuk menegakkan satu-dan-hanya-satu yang bukan nol. Saya menggunakan dua untuk kejelasan:Ini hanya akan menegakkan batasan di tingkat DB. Anda perlu memvalidasi input dalam formulir Anda atau serializers secara manual.
Sebagai catatan, Anda mungkin harus menggunakan
OneToOneField
bukanForeignKey(unique=True)
. Anda juga akan menginginkannyablank=True
.sumber
Saya pikir Anda berbicara tentang hubungan Generik , dok . Jawaban Anda terlihat mirip dengan yang ini .
Beberapa waktu yang lalu saya perlu menggunakan hubungan Generik tetapi saya membaca di sebuah buku dan di tempat lain bahwa penggunaannya harus dihindari, saya pikir itu adalah Two Scoops of Django.
Saya akhirnya membuat model seperti ini:
Saya tidak yakin apakah ini solusi yang baik dan seperti yang Anda sebutkan, Anda lebih suka tidak menggunakannya, tetapi ini berhasil dalam kasus saya.
sumber
Mungkin sudah terlambat untuk menjawab pertanyaan Anda, tetapi saya pikir solusi saya mungkin cocok untuk kasus orang lain.
Saya akan membuat model baru, sebut saja
Dependency
, dan terapkan logika dalam model itu.Kemudian saya akan menulis logika agar dapat diterapkan dengan sangat eksplisit.
Sekarang Anda hanya perlu membuat satu
ForeignKey
untukInspection
model Anda .Dalam
view
fungsi Anda, Anda perlu membuatDependency
objek dan kemudian menetapkannya keInspection
catatan Anda . Pastikan Anda menggunakancreate_dependency_object
diview
fungsi .Ini cukup banyak membuat kode Anda eksplisit dan bukti bug. Penegakannya dapat dilewati dengan sangat mudah. Tetapi intinya adalah bahwa ia membutuhkan pengetahuan sebelumnya untuk batasan yang tepat ini untuk dilewati.
sumber