Mój problem polega na tym, że mam model, który może wziąć jeden z dwóch kluczy obcych, aby powiedzieć, jaki to model. Chcę, aby zajęło to co najmniej jedno, ale nie jedno i drugie. Czy mogę nadal mieć ten sam model, czy powinienem podzielić go na dwa typy? Oto kod:
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)
Problemem jest Inspection
model. Powinno to być powiązane z grupą lub witryną, ale nie z obydwoma. Obecnie przy tej konfiguracji potrzebuje obu.
Wolałbym nie trzeba podzielić to na dwie prawie identycznych modeli GroupInspection
i SiteInspection
tak każde rozwiązanie, które utrzymuje ją jako jeden model byłby idealny.
django
django-models
CalMac
źródło
źródło
Inspection
klasę, a następnie do podklasySiteInspection
iGroupInspection
dla niezarejestrowanych części -common.unique=True
część w polach FK oznacza, żeInspection
może istnieć tylko jedna instancja dla jednej danejGroupID
lubSiteID
instancji - IOW, to relacja jeden do jednego, a nie jeden do wielu. Czy tego naprawdę chcesz?Inspection
być łącznikiem międzyGroup
iSite
aInspectionID
, a następnie mogę mieć wiele „inspekcji” w postaciInspectionReport
tego jednego związku. Zrobiono to, aby łatwiej było sortować wedługDate
wszystkich rekordów związanych z jednymGroup
lubSite
. Mam nadzieję, że to ma sensOdpowiedzi:
Sugerowałbym, abyś dokonał takiej weryfikacji na sposób Django
przez przesłonięcie
clean
metody Django Modelźródło
Jak wspomniano w komentarzach, powodem, dla którego „przy tej konfiguracji wymaga obu” jest to, że zapomniałeś dodać
blank=True
pola FK, więc twojeModelForm
(niestandardowe lub domyślne wygenerowane przez administratora) spowoduje, że pole formularza będzie wymagane . Na poziomie schematu db można wypełnić oba, jeden lub żaden z tych FK, byłoby dobrze, ponieważ te pola db zostały dopuszczone do null (znull=True
argumentem).Ponadto (porównaj moje inne komentarze), możesz chcieć sprawdzić, czy naprawdę chcesz, aby FK były wyjątkowe. Technicznie zmienia to relację jeden do wielu w relację jeden do jednego - dozwolony jest tylko jeden rekord „inspekcji” dla danego GroupID lub SiteId (nie można mieć dwóch lub więcej „inspekcji” dla jednego GroupId lub SiteId) . Jeśli jest to NAPRAWDĘ to, czego chcesz, możesz zamiast tego użyć jawnego OneToOneField (schemat db będzie taki sam, ale model będzie bardziej wyraźny, a powiązany deskryptor znacznie bardziej użyteczny w tym przypadku użycia).
Na marginesie: w modelu Django pole ForeignKey materializuje się jako instancja modelu pokrewnego, a nie jako surowy identyfikator. IOW, biorąc pod uwagę to:
wtedy
bar.foo
zdecyduje się nafoo
, a nie nafoo.id
. Więc na pewno chcesz zmienić nazwę polaInspectionID
iSiteID
pola na właściweinspection
isite
. BTW, w Pythonie konwencja nazewnictwa to „all_lower_with_underscores” dla wszystkiego innego niż nazwy klas i pseudo-stałe.Teraz najważniejsze pytanie: nie ma określonego standardowego sposobu SQL narzucenia ograniczenia „jedno lub drugie” na poziomie bazy danych, więc zwykle odbywa się to za pomocą ograniczenia CHECK , które jest wykonywane w modelu Django z meta „więzami” modelu opcja .
Biorąc to pod uwagę, sposób, w jaki ograniczenia są faktycznie obsługiwane i egzekwowane na poziomie db, zależy od twojego dostawcy DB (MySQL <8.0.16 po prostu zignoruj je na przykład), a rodzaj ograniczenia, którego będziesz potrzebować tutaj , nie będzie egzekwowany w formularzu lub sprawdzanie poprawności na poziomie modelu , tylko przy próbie zapisania modelu, dlatego też chcesz dodać sprawdzanie poprawności na poziomie modelu (najlepiej) lub sprawdzanie poprawności na poziomie formularza, w obu przypadkach w (odpowiednio) modelu lub
clean()
metodzie formularza .Krótko mówiąc:
najpierw dwukrotnie sprawdź, czy naprawdę chcesz tego
unique=True
ograniczenia, a jeśli tak, zamień swoje pole FK na OneToOneField.dodaj
blank=True
argument do obu pól FK (lub OneToOne)clean()
metodę do swojego modelu, która sprawdza, czy masz jedno lub drugie pole i podnosi inny błąd sprawdzania poprawnościi powinieneś być w porządku, zakładając, że RDBMS oczywiście przestrzega ograniczeń sprawdzania.
Pamiętaj tylko, że dzięki tej konstrukcji Twój
Inspection
model jest całkowicie bezużyteczną (ale kosztowną!) Pośrednią - uzyskasz dokładnie te same funkcje przy niższym koszcie, przenosząc FK (i ograniczenia, walidację itp.) Bezpośrednio doInspectionReport
.Teraz może istnieć inne rozwiązanie - zachowaj model Inspection, ale umieść FK jako OneToOneField na drugim końcu relacji (w witrynie i grupie):
Następnie możesz uzyskać wszystkie raporty dla danej witryny lub grupy
yoursite.inspection.inspectionreport_set.all()
.Pozwala to uniknąć konieczności dodawania jakichkolwiek konkretnych ograniczeń lub sprawdzania poprawności, ale kosztem dodatkowego poziomu pośredniego (
join
klauzula SQL itp.).To, które z tych rozwiązań będzie „najlepsze”, naprawdę zależy od kontekstu, więc musisz zrozumieć implikacje obu i sprawdzić, w jaki sposób zazwyczaj używasz swoich modeli, aby dowiedzieć się, które jest bardziej odpowiednie dla twoich potrzeb. Jeśli chodzi o mnie i bez większego kontekstu (lub wątpliwości) wolałbym używać rozwiązania z mniejszymi poziomami pośrednimi, ale YMMV.
Uwaga: ogólne relacje: mogą się przydać, gdy naprawdę masz wiele możliwych powiązanych modeli i / lub nie wiesz z góry, które modele chcesz odnosić do swoich. Jest to szczególnie przydatne w przypadku aplikacji wielokrotnego użytku (pomyśl „komentarze” lub „tagi” itp.) Lub rozszerzalnych (ramy zarządzania treścią itp.). Minusem jest to, że sprawia, że zapytania są znacznie cięższe (i raczej niepraktyczne, gdy chcesz wykonywać zapytania ręczne na db). Z doświadczenia mogą szybko stać się kodem / kodem i perfem bota PITA, więc lepiej je zachować, gdy nie ma lepszego rozwiązania (i / lub gdy narzut związany z konserwacją i czasem wykonywania nie stanowi problemu).
Moje 2 centy.
źródło
Django ma nowy (od 2.2) interfejs do tworzenia ograniczeń DB: https://docs.djangoproject.com/en/3.0/ref/models/constraints/
Możesz użyć a,
CheckConstraint
aby wymusić, że jeden i jedyny jest inny niż null. Dla jasności używam dwóch:Wymusi to tylko ograniczenie na poziomie bazy danych. Konieczne będzie ręczne sprawdzenie poprawności danych wejściowych w formularzach lub serializatorach.
Na marginesie, prawdopodobnie powinieneś użyć
OneToOneField
zamiastForeignKey(unique=True)
. Będziesz także chciałblank=True
.źródło
Myślę, że mówisz o ogólnych stosunkach , dokumentach . Twoja odpowiedź wygląda podobnie do tej .
Jakiś czas temu musiałem używać relacji rodzajowych, ale czytałem w książce i gdzieś indziej, że należy tego unikać, myślę, że to były dwie miarki Django.
Ostatecznie stworzyłem taki model:
Nie jestem pewien, czy jest to dobre rozwiązanie i jak wspomniałeś, wolisz go nie używać, ale w moim przypadku to działa.
źródło
Może być późno na odpowiedź na twoje pytanie, ale pomyślałem, że moje rozwiązanie może pasować do przypadku innej osoby.
Stworzyłbym nowy model, nazwijmy go
Dependency
i zastosuję logikę w tym modelu.Następnie napisałbym logikę, aby można ją było zastosować bardzo wyraźnie.
Teraz wystarczy utworzyć singiel
ForeignKey
do swojegoInspection
modelu.W swoich
view
funkcjach musisz utworzyćDependency
obiekt, a następnie przypisać go do swojegoInspection
rekordu. Upewnij się, że używaszcreate_dependency_object
w swoichview
funkcjach.To sprawia, że Twój kod jest wyraźny i odporny na błędy. Egzekucję można zbyt łatwo ominąć. Chodzi jednak o to, że trzeba ominąć tę dokładną wiedzę, aby ominąć to dokładne ograniczenie.
źródło