Моя проблема в том, что у меня есть модель, которая может взять один из двух внешних ключей, чтобы сказать, что это за модель. Я хочу, чтобы это заняло хотя бы один, но не оба. Могу ли я иметь это по-прежнему одной модели или я должен разделить его на два типа. Вот код:
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)
Проблема в Inspection
модели. Это должно быть связано либо с группой, либо с сайтом, но не с обоими. В настоящее время с этой настройкой нужно и то и другое.
Я бы предпочел не разбивать это на две почти идентичные модели, GroupInspection
и SiteInspection
поэтому любое решение, которое сохраняет его как одну модель, было бы идеальным.
django
django-models
Калмык
источник
источник
Inspection
класс, а затем подкласс вSiteInspection
иGroupInspection
для не являющихся -Общих частей.unique=True
часть в ваших полях FK означает, что только одинInspection
экземпляр может существовать для одного данногоGroupID
илиSiteID
экземпляра - IOW, это отношение один к одному, а не один ко многим. Это действительно то, что вы хотите?Inspection
быть связующим звеном междуGroup
илиSite
и anInspectionID
, тогда я могу провести несколько «проверок» в формеInspectionReport
этих отношений. Это было сделано для того, чтобы мне было легче сортироватьDate
все записи, относящиеся к одномуGroup
илиSite
. Надеюсь, что это имеет смыслОтветы:
Я бы посоветовал вам сделать такую проверку способом Джанго
переопределив
clean
метод модели Джангоисточник
Как упомянуто в комментариях, причина того, что «с этой настройкой нужны оба», заключается в том, что вы забыли добавить
blank=True
поля FK, так что вашеModelForm
(либо пользовательское, либо сгенерированное по умолчанию администратором) сделает поле формы обязательным. , На уровне схемы БД вы можете заполнить оба, либо один, либо ни один из этих FK, это будет нормально, поскольку вы сделали эти поля БД обнуляемыми (сnull=True
аргументом).Кроме того, (см. Мои другие комментарии), вы можете проверить, действительно ли вы хотите, чтобы FK были уникальными. Это технически превращает ваши отношения один-ко-многим в отношения один-к-одному - вам разрешена только одна запись «осмотра» для данного GroupID или SiteId (у вас не может быть двух или более «проверок» для одного GroupId или SiteId) , Если это действительно то, что вы хотите, вы можете вместо этого использовать явный OneToOneField (схема БД будет такой же, но модель будет более явной, а связанный дескриптор гораздо более пригодным для этого варианта использования).
В качестве примечания: в модели Django поле ForeignKey материализуется как экземпляр связанной модели, а не как необработанный идентификатор. IOW, учитывая это:
тогда
bar.foo
разрешуfoo
, а неfoo.id
. Таким образом , вы , конечно , хотите переименоватьInspectionID
иSiteID
поле собственноinspection
иsite
. Кстати, в Python соглашение об именах называется all_lower_with_underscores для всего, кроме имен классов и псевдоконстант.Теперь о вашем основном вопросе: не существует конкретного стандартного способа SQL для принудительного применения «одного или другого» ограничения на уровне базы данных, поэтому обычно это делается с использованием ограничения CHECK , что делается в модели Django с мета-«ограничениями» модели. вариант .
При этом, как фактически поддерживаются и применяются ограничения на уровне базы данных, зависит от вашего поставщика БД (MySQL <8.0.16 просто игнорирует их, например), и вид ограничения, который вам здесь понадобится , не будет применяться в форме или проверка на уровне модели , только при попытке сохранить модель, поэтому вы также хотите добавить проверку либо на уровне модели (предпочтительно), либо на уровне формы, в обоих случаях в (соответственно) модели или
clean()
методе формы .Короче говоря:
сначала проверьте, действительно ли вы хотите это
unique=True
ограничение, и если да, то замените поле FK на OneToOneField.добавьте
blank=True
аргумент в оба поля FK (или OneToOne)clean()
метод в вашу модель, который проверяет, есть ли у вас одно или другое поле, и выдает ошибку проверки ещеи вы должны быть в порядке, если, конечно, ваша СУБД соблюдает контрольные ограничения.
Просто отметьте, что с этим дизайном ваша
Inspection
модель является абсолютно бесполезной (но дорогостоящей!) Ссылкой - вы получите те же функции с меньшими затратами, переместив FK (и ограничения, валидацию и т. Д.) Непосредственно вInspectionReport
.Теперь может быть другое решение - сохранить модель Inspection, но поместить FK как OneToOneField на другом конце отношения (в Site и Group):
И тогда вы можете получить все отчеты для данного сайта или группы с
yoursite.inspection.inspectionreport_set.all()
.Это избавляет от необходимости добавлять какие-либо конкретные ограничения или проверки, но за счет дополнительного уровня косвенности (
join
пункт SQL и т. Д.).То, какое из этих решений будет «лучшим», действительно зависит от контекста, поэтому вы должны понять значение обоих и проверить, как вы обычно используете свои модели, чтобы выяснить, что больше подходит для ваших собственных нужд. Насколько мне известно, без какого-либо контекста (или сомнений) я бы предпочел использовать решение с меньшим уровнем косвенности, но с YMMV.
Обратите внимание на родовые отношения: они могут быть полезны, когда у вас действительно много возможных связанных моделей и / или вы заранее не знаете, какие модели вы хотите связать с вашими собственными. Это особенно полезно для многократно используемых приложений (например, «комментарии» или «теги» и т. Д.) Или расширяемых (структуры управления контентом и т. Д.). Недостатком является то, что это делает запросы намного более тяжелыми (и довольно непрактичными, когда вы хотите выполнять ручные запросы к вашей базе данных). Исходя из опыта, они могут быстро стать ботом PITA по отношению к коду и программному обеспечению, поэтому лучше их хранить, когда нет лучшего решения (и / или когда нет проблем с обслуживанием и временем выполнения).
Мои 2 цента.
источник
В Django появился новый (начиная с 2.2) интерфейс для создания ограничений БД: https://docs.djangoproject.com/en/3.0/ref/models/constraints/
Вы можете использовать
CheckConstraint
для принудительного применения один-единственный-ненулевой. Я использую два для ясности:Это будет только применять ограничение на уровне БД. Вам нужно будет проверить входные данные в ваших формах или сериализаторах вручную.
Как примечание, вы, вероятно, должны использовать
OneToOneField
вместоForeignKey(unique=True)
. Тебе тоже захочетсяblank=True
.источник
Я думаю, что вы говорите об общих отношениях , документы . Ваш ответ похож на этот .
Некоторое время назад мне нужно было использовать родовые отношения, но я читал в книге, а где-то еще, что использования следует избегать, я думаю, что это были Two Scoops of Django.
В итоге я создал такую модель:
Я не уверен, что это хорошее решение, и, как вы упомянули, вы бы не хотели его использовать, но в моем случае это работает.
источник
Возможно, уже поздно ответить на ваш вопрос, но я подумал, что моё решение может подойти к делу другого человека.
Я хотел бы создать новую модель, назовем ее
Dependency
и применить логику в этой модели.Тогда я написал бы логику, чтобы быть применимой очень явно.
Теперь вам просто нужно создать сингл
ForeignKey
для вашейInspection
модели.В ваших
view
функциях вам нужно создатьDependency
объект, а затем назначить его вашейInspection
записи. Убедитесь, что вы используетеcreate_dependency_object
в своихview
функциях.Это в значительной степени делает ваш код явным и защищенным от ошибок. Исполнение может быть обойдено слишком легко. Но дело в том, что для того, чтобы обойти это точное ограничение, необходимо предварительное знание.
источник