Як згадувалося в коментарях, причиною того, що "за допомогою цього налаштування йому потрібно і те й інше", є лише те, що ви забули додати blank=True
свої поля до FK, тому ваше ModelForm
(або спеціальне, або за замовчуванням згенероване адміністратором) зробить поле форми необхідним . На рівні схеми db ви можете заповнити обидва, або один, або жоден із цих FK, це було б нормально, оскільки ви зробили ці nb-поля нульовими (з null=True
аргументом).
Крім того, (див. Інші мої коментарі), ви можете перевірити, чи дійсно ви хочете, щоб FKS були унікальними. Це технічно перетворює відносини один до багатьох на відносини один на один - вам дозволяється лише один єдиний запис "перевірки" для даного GroupID або SiteId (у вас не може бути двох і більше "перевірок" для одного GroupId або SiteId) . Якщо це дійсно те, що ви хочете, ви можете скористатися явним OneToOneField замість цього (схема db буде такою ж, але модель буде більш чіткою і пов'язаний дескриптор набагато більш придатний для цього випадку використання).
Як бічна примітка: у моделі Django поле ForeignKey матеріалізується як пов'язаний екземпляр моделі, а не як необроблений ідентифікатор. IOW, враховуючи це:
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)
то bar.foo
вирішить foo
, а не foo.id
. Таким чином , ви , звичайно , хочете перейменувати InspectionID
і SiteID
поле власне inspection
і site
. BTW, в Python, умовна назва називає "all_lower_with_underscores" для будь-чого іншого, крім імен класів та псевдоконстант.
Тепер для вашого основного питання: немає специфічного стандартного способу SQL примусового застосування "одного чи іншого" обмеження на рівні бази даних, тому зазвичай це робиться за допомогою обмеження CHECK , яке робиться в моделі Django з мета "обмеженнями" моделі. варіант .
Це говорить про те, як фактично підтримуються та застосовуються обмеження на рівні db, залежить від вашого постачальника баз даних (MySQL <8.0.16, просто проігноруйте їх, наприклад), і вид обмеження, яке вам знадобиться тут , не буде застосовано до форми або валідація рівня моделі , лише при спробі збереження моделі, тому ви також хочете додати валідацію або на рівні моделі (бажано), або перевірку рівня форми, в обох випадках у clean()
методі (відповідно) моделі чи формі .
Отже, щоб коротко розповісти:
спочатку перевірте, чи дійсно ви хочете цього unique=True
обмеження, і якщо так, то замініть поле FK на OneToOneField.
додайте blank=True
аргумент у обидва поля FK (або OneToOne)
- додайте належне обмеження перевірки в мета вашої моделі - документ є стислим, але все ще досить явним, якщо ви знаєте робити складні запити з ORM (і якщо ви цього не встигли, то навчитесь ;-))
- додайте
clean()
у свою модель метод, який перевіряє ваше поле або інше, і інше викликає помилку перевірки
і вам повинно бути все в порядку, якщо припустити, що ваш RDBMS поважає обмеження перевірки.
Зауважте лише, що при такому дизайні ваша Inspection
модель є абсолютно марною (але дорогою!) Непрямою діяльністю - ви отримаєте такі самі функції за меншу ціну, перемістивши FK (та обмеження, валідацію тощо) безпосередньо в InspectionReport
.
Тепер може бути інше рішення - збережіть модель перевірки, але поставте FK як OneToOneField на інший кінець взаємозв'язку (у Сайті та Групі):
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)
І тоді ви можете отримати всі звіти для певного сайту чи групи yoursite.inspection.inspectionreport_set.all()
.
Це дозволяє уникнути необхідності додавати будь-які конкретні обмеження чи перевірку, але ціною додаткового рівня непрямості ( join
пункт SQL тощо).
Яке з цих рішень було б "найкращим" насправді залежить від контексту, тому ви повинні зрозуміти наслідки обох і перевірити, як ви зазвичай використовуєте свої моделі, щоб з'ясувати, що більше відповідає вашим власним потребам. Що стосується мене і без більшого контексту (або сумнівів) я б скоріше використовував рішення з меншими рівнями непрямості, але YMMV.
Примітка щодо загальних відносин: вони можуть бути зручними, коли у вас дійсно багато можливих пов'язаних моделей та / або не знаєте заздалегідь, які моделі ви хочете пов’язати з вашими. Це особливо корисно для багаторазових програм (думайте, що "коментарі" або "теги" тощо) або розширюваних (рамки управління вмістом тощо). Мінусом є те, що він робить запити набагато важчими (і досить непрактично, коли ви хочете робити запити вручну на своєму db). З досвіду, вони можуть швидко стати ботом wrt / код та перфоманти PITA, тому краще тримати їх, коли немає кращого рішення (та / або коли накладні витрати на технічне обслуговування та час роботи не є проблемою).
Мої 2 копійки.
Inspection
клас, а потім підклас вSiteInspection
іGroupInspection
для не є -Загальна частин.