जैसा कि टिप्पणियों में उल्लेख किया गया है, इसका कारण यह है कि "इस सेट अप के साथ दोनों की आवश्यकता है" बस इतना है कि आप blank=True
अपने एफके फ़ील्ड में जोड़ना भूल गए हैं , इसलिए आपका ModelForm
(या तो कस्टम एक या व्यवस्थापक द्वारा उत्पन्न डिफ़ॉल्ट) फॉर्म फ़ील्ड को आवश्यक बना देगा । डीबी स्कीमा स्तर पर, आप दोनों में से किसी एक या एफके को नहीं भर सकते हैं, यह ठीक होगा क्योंकि आपने उन डीबी क्षेत्रों को अशक्त ( null=True
तर्क के साथ ) बना दिया है।
इसके अलावा, (cf my other comments), आप यह जांचना चाहते हैं कि आपका वास्तव में FK अद्वितीय होना चाहता है। यह तकनीकी रूप से आपके एक रिश्ते को एक से एक रिश्ते में बदल देता है - आपको दिए गए GroupID या SiteId के लिए केवल एक एकल 'निरीक्षण' रिकॉर्ड की अनुमति है (आप एक GroupId या SiteId के लिए दो या अधिक 'निरीक्षण' नहीं कर सकते) । यदि यह वास्तव में है कि आप क्या चाहते हैं, तो आप इसके बजाय एक स्पष्ट OneToOneField का उपयोग करना चाह सकते हैं (db स्कीमा समान होगी लेकिन मॉडल अधिक स्पष्ट होगा और संबंधित डिस्क्रिप्टर इस उपयोग के मामले के लिए बहुत अधिक उपयोगी होगा)।
एक साइड नोट के रूप में: एक Django मॉडल में, एक विदेशीके फील्ड एक संबंधित मॉडल उदाहरण के रूप में, एक कच्ची आईडी के रूप में भौतिकवाद करता है। 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
। बीटीडब्ल्यू, पायथन में, नामकरण सम्मेलन 'all_lower_with_underscores' वर्ग के नाम और छद्म-स्थिरांक के अलावा और कुछ के लिए है।
अब आपके मूल प्रश्न के लिए: डेटाबेस स्तर पर "एक या दूसरे" बाधा को लागू करने का कोई विशिष्ट मानक SQL तरीका नहीं है, इसलिए यह आमतौर पर एक CHECK बाधा का उपयोग करके किया जाता है , जो कि मॉडल के मेटा "बाधाओं" के साथ एक Django मॉडल में किया जाता है विकल्प ।
यह कहा जा रहा है, कि db स्तर पर कैसे बाधाओं का वास्तव में समर्थन और लागू किया जाता है, यह आपके DB विक्रेता (MySQL <8.0.16 केवल सादा उदाहरण के लिए उन्हें अनदेखा करता है) पर निर्भर करता है , और आपको जिस तरह की बाधा की आवश्यकता होगी, वह फ़ॉर्म पर लागू नहीं होगी या मॉडल स्तर की मान्यता , केवल जब मॉडल को बचाने की कोशिश कर रहा है, तो आप भी मॉडल के स्तर (अधिमानतः) या प्रपत्र स्तर सत्यापन में दोनों (या) मॉडल या प्रपत्र की clean()
विधि में सत्यापन जोड़ना चाहते हैं ।
तो एक लंबी कहानी बनाने के लिए:
पहले unique=True
दोहराएं कि आप वास्तव में यह बाधा चाहते हैं , और यदि हाँ तो अपने FK फ़ील्ड को OneToOneField से बदलें।
blank=True
अपने FK (या OneToOne) दोनों फ़ील्ड में एक arg जोड़ें
- अपने मॉडल के मेटा में उचित चेक बाधा जोड़ें - डॉक्टर सक्सेस है, लेकिन फिर भी पर्याप्त स्पष्ट है यदि आप ORM के साथ जटिल प्रश्न करना जानते हैं (और यदि आपके पास सीखने का समय नहीं है;;)
clean()
अपने मॉडल के लिए एक विधि जोड़ें जो आपके या तो एक या दूसरे क्षेत्र की जाँच करता है और एक सत्यापन त्रुटि उठाता है
और आपको ठीक होना चाहिए, अपने RDBMS का सम्मान करते हुए निश्चित रूप से चेक बाधाओं को दूर करना चाहिए।
बस ध्यान दें, इस डिजाइन के साथ, आपका Inspection
मॉडल पूरी तरह से बेकार है (अभी तक महंगा है!) अप्रत्यक्ष - आपको एफकेएस (और बाधाओं, सत्यापन आदि) को सीधे स्थानांतरित करके कम कीमत पर सटीक समान सुविधाएं मिलेंगी InspectionReport
।
अब एक और समाधान हो सकता है - निरीक्षण मॉडल रखें, लेकिन रिश्ते के दूसरे छोर पर (साइट और समूह में) एफके को वनटॉइनफिल्ड के रूप में रखें:
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
क्लॉज आदि) की कीमत पर।
उन समाधानों में से कौन सा "सबसे अच्छा" वास्तव में संदर्भ-निर्भर है, इसलिए आपको दोनों के निहितार्थों को समझना होगा और जांचना होगा कि आप आमतौर पर अपने मॉडल का उपयोग कैसे करते हैं यह जानने के लिए कि आपकी अपनी आवश्यकताओं के लिए अधिक उपयुक्त है। जहां तक मेरा संबंध है और अधिक संदर्भ के बिना (या संदेह में) मैं कम अप्रत्यक्ष स्तरों के साथ समाधान का उपयोग करूंगा, लेकिन YMMV।
जेनेरिक संबंधों के बारे में एनबी: वे काम कर सकते हैं जब आपके पास वास्तव में बहुत सारे संबंधित मॉडल होते हैं और / या पहले से नहीं जानते हैं कि कौन से मॉडल आपके खुद से संबंधित होना चाहते हैं। यह पुन: प्रयोज्य एप्लिकेशन ("टिप्पणी" या "टैग" आदि सुविधाओं) या एक्स्टेंसिबल लोगों (सामग्री प्रबंधन रूपरेखा आदि) के लिए विशेष रूप से उपयोगी है। नकारात्मक पक्ष यह है कि यह क्वेरी को अधिक भारी बनाता है (और जब आप अपने db पर मैन्युअल क्वेरी करना चाहते हैं तो अव्यवहारिक)। अनुभव से, वे जल्दी से एक PITA बॉट wrt / code और perfs बन सकते हैं, इसलिए जब कोई बेहतर समाधान (और / या जब रखरखाव और रनवे ओवरहेड एक मुद्दा नहीं है) के लिए उन्हें रखना बेहतर होगा।
मेरे 2 सेंट।
Inspection
वर्ग, और उसके बाद में उपवर्गSiteInspection
औरGroupInspection
के लिए गैर -common भागों।