Seperti disebutkan dalam komentar, alasan bahwa "dengan pengaturan ini membutuhkan keduanya" hanya karena Anda lupa menambahkan blank=True
ke bidang FK Anda, sehingga Anda ModelForm
(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 (dengan null=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:
class Foo(models.Model):
name = models.TextField()
class Bar(models.Model):
foo = models.ForeignKey(Foo)
foo = Foo.objects.create(name="foo")
bar = Bar.objects.create(foo=foo)
maka bar.foo
akan memutuskan untuk foo
, bukan untuk foo.id
. Jadi, Anda tentu ingin mengubah nama InspectionID
dan SiteID
bidang menjadi benarinspection
dan site
. 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) Anda
- tambahkan yang tepat kendala pemeriksaan yang dalam meta model Anda - dokumen tersebut succint tetapi masih cukup eksplisit jika Anda tahu untuk melakukan pertanyaan kompleks dengan ORM (dan jika Anda tidak saatnya Anda belajar ;-))
- tambahkan
clean()
metode ke model Anda yang memeriksa apakah Anda memiliki salah satu atau bidang lain dan memunculkan kesalahan validasi yang lain
dan 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 ke InspectionReport
.
Sekarang mungkin ada solusi lain - jaga model Inspeksi, tetapi letakkan FK sebagai OneToOneField di ujung lain hubungan (di Situs dan Grup):
class Inspection(models.Model):
id = models.AutoField(primary_key=True) # a pk is always unique !
class InspectionReport(models.Model):
# you actually don't need to manually specify a PK field,
# Django will provide one for you if you don't
# id = models.AutoField(primary_key=True)
inspection = ForeignKey(Inspection, ...)
date = models.DateField(null=True) # you should have a default then
comment = models.CharField(max_length=255, blank=True default="")
signature = models.CharField(max_length=255, blank=True, default="")
class Group(models.Model):
inspection = models.OneToOneField(Inspection, null=True, blank=True)
class Site(models.Model):
inspection = models.OneToOneField(Inspection, null=True, blank=True)
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.
Inspection
kelas, dan kemudian subkelas ke dalamSiteInspection
danGroupInspection
untuk bagian-bagian yang tidak umum.