ऐसा लगता है कि आप डेटा मॉडल और डोमेन मॉडल के बीच के अंतर के बारे में पूछ रहे हैं - बाद वाला वह है जहां आप अपने अंतिम उपयोगकर्ता द्वारा बताए गए अनुसार व्यापारिक तर्क और इकाइयां पा सकते हैं, पूर्व वह है जहां आप वास्तव में अपना डेटा संग्रहीत करते हैं।
इसके अलावा, मैंने आपके प्रश्न के तीसरे भाग की व्याख्या की है: इन मॉडलों को अलग रखने में विफलता को कैसे नोटिस किया जाए।
ये दो बहुत अलग अवधारणाएँ हैं और इन्हें अलग रखना हमेशा कठिन होता है। हालांकि, कुछ सामान्य पैटर्न और उपकरण हैं जिनका उपयोग इस उद्देश्य के लिए किया जा सकता है।
डोमेन मॉडल के बारे में
पहली चीज जिसे आपको पहचानना है, वह यह है कि आपका डोमेन मॉडल वास्तव में डेटा के बारे में नहीं है; यह क्रियाओं और प्रश्नों के बारे में है जैसे "इस उपयोगकर्ता को सक्रिय करें", "इस उपयोगकर्ता को निष्क्रिय करें", "कौन से उपयोगकर्ता वर्तमान में सक्रिय हैं?", और "यह उपयोगकर्ता का नाम क्या है?"। शास्त्रीय शब्दों में: यह प्रश्नों और आदेशों के बारे में है ।
कमांडों में सोच रहा था
आइए अपने उदाहरण में आज्ञाओं को देखकर शुरू करें: "इस उपयोगकर्ता को सक्रिय करें" और "इस उपयोगकर्ता को निष्क्रिय करें"। आदेशों के बारे में अच्छी बात यह है कि वे आसानी से छोटे दिए गए जब-तब परिदृश्य द्वारा व्यक्त किए जा सकते हैं:
दिए गए किसी निष्क्रिय उपयोगकर्ता
जब व्यवस्थापक को सक्रिय करता है इस प्रयोक्ता
तो उपयोगकर्ता सक्रिय हो जाता है
और एक पुष्टिकरण ई-मेल उपयोगकर्ता के लिए भेज दिया जाता है
और एक प्रवेश सिस्टम लॉग में जोड़ा जाता है
(आदि आदि)
इस तरह के परिदृश्य यह देखने के लिए उपयोगी हैं कि आपके बुनियादी ढांचे के विभिन्न भाग एक ही आदेश से कैसे प्रभावित हो सकते हैं - इस मामले में आपका डेटाबेस (किसी प्रकार का 'सक्रिय' ध्वज), आपका मेल सर्वर, आपका सिस्टम लॉग इत्यादि।
इस तरह का परिदृश्य वास्तव में टेस्ट ड्रिवेन डेवलपमेंट के माहौल को स्थापित करने में आपकी मदद करता है।
और अंत में, कमांड्स में सोचना वास्तव में आपको एक कार्य-उन्मुख अनुप्रयोग बनाने में मदद करता है। आपके उपयोगकर्ता इस की सराहना करेंगे :-)
व्यक्त करना
Django व्यक्त करने के दो आसान तरीके प्रदान करता है; वे दोनों वैध विकल्प हैं और दो दृष्टिकोणों को मिलाना असामान्य नहीं है।
सेवा परत
सेवा मॉड्यूल पहले से ही किया गया है @Hedde द्वारा वर्णित । यहां आप एक अलग मॉड्यूल को परिभाषित करते हैं और प्रत्येक कमांड को एक फ़ंक्शन के रूप में दर्शाया जाता है।
services.py
def activate_user(user_id):
user = User.objects.get(pk=user_id)
# set active flag
user.active = True
user.save()
# mail user
send_mail(...)
# etc etc
रूपों का उपयोग करना
दूसरा तरीका प्रत्येक कमांड के लिए एक Django फॉर्म का उपयोग करना है। मैं इस दृष्टिकोण को पसंद करता हूं, क्योंकि यह कई निकट से संबंधित पहलुओं को जोड़ता है:
- कमांड का निष्पादन (यह क्या करता है?)
- कमांड मापदंडों का सत्यापन (क्या यह ऐसा कर सकता है?)
- कमांड की प्रस्तुति (मैं यह कैसे कर सकता हूं?)
forms.py
class ActivateUserForm(forms.Form):
user_id = IntegerField(widget = UsernameSelectWidget, verbose_name="Select a user to activate")
# the username select widget is not a standard Django widget, I just made it up
def clean_user_id(self):
user_id = self.cleaned_data['user_id']
if User.objects.get(pk=user_id).active:
raise ValidationError("This user cannot be activated")
# you can also check authorizations etc.
return user_id
def execute(self):
"""
This is not a standard method in the forms API; it is intended to replace the
'extract-data-from-form-in-view-and-do-stuff' pattern by a more testable pattern.
"""
user_id = self.cleaned_data['user_id']
user = User.objects.get(pk=user_id)
# set active flag
user.active = True
user.save()
# mail user
send_mail(...)
# etc etc
विचार में प्रश्न
आपके उदाहरण में कोई प्रश्न नहीं था, इसलिए मैंने कुछ उपयोगी प्रश्नों को बनाने की स्वतंत्रता ली। मैं "प्रश्न" शब्द का उपयोग करना पसंद करता हूं, लेकिन प्रश्न शास्त्रीय शब्दावली है। दिलचस्प प्रश्न हैं: "इस उपयोगकर्ता का नाम क्या है?", "क्या यह उपयोगकर्ता लॉग इन कर सकता है?", "मुझे निष्क्रिय उपयोगकर्ताओं की सूची दिखाएं", और "निष्क्रिय उपयोगकर्ताओं का भौगोलिक वितरण क्या है?"
इन प्रश्नों का उत्तर देने से पहले, आपको हमेशा अपने आप से दो प्रश्न पूछने चाहिए: क्या यह केवल मेरे टेम्प्लेट के लिए एक प्रेजेंटेशनल क्वेरी है, और / या एक बिजनेस लॉजिक क्वेरी जो मेरी कमांड्स को निष्पादित करने के लिए बंधी है, और / या रिपोर्टिंग क्वेरी है।
उपयोगकर्ता इंटरफ़ेस को बेहतर बनाने के लिए केवल प्रेजेंटेशनल क्वेश्चन किए जाते हैं। व्यावसायिक तर्क प्रश्नों के उत्तर सीधे आपके आदेशों के निष्पादन को प्रभावित करते हैं। रिपोर्टिंग के प्रश्न केवल विश्लेषणात्मक उद्देश्यों के लिए हैं और इसमें समय की कमी है। ये श्रेणियां परस्पर अनन्य नहीं हैं।
दूसरा सवाल यह है: "क्या मुझे उत्तरों पर पूरा नियंत्रण है?" उदाहरण के लिए, जब उपयोगकर्ता के नाम (इस संदर्भ में) का प्रश्न किया जाता है तो परिणाम पर हमारा कोई नियंत्रण नहीं होता है, क्योंकि हम बाहरी एपीआई पर निर्भर होते हैं।
प्रश्न बनाना
Django में सबसे बुनियादी क्वेरी प्रबंधक ऑब्जेक्ट का उपयोग है:
User.objects.filter(active=True)
बेशक, यह केवल तभी काम करता है जब डेटा वास्तव में आपके डेटा मॉडल में दर्शाया गया हो। ऐसी स्थिति हर बार नहीं होती है। उन मामलों में, आप नीचे दिए गए विकल्पों पर विचार कर सकते हैं।
कस्टम टैग और फ़िल्टर
पहला विकल्प उन प्रश्नों के लिए उपयोगी है जो केवल प्रस्तुतिकरण हैं: कस्टम टैग और टेम्पलेट फ़िल्टर।
template.html
<h1>Welcome, {{ user|friendly_name }}</h1>
template_tags.py
@register.filter
def friendly_name(user):
return remote_api.get_cached_name(user.id)
क्वेरी के तरीके
अगर आपकी क्वेरी केवल प्रस्तुतिकरण नहीं है, तो आप अपने लिए क्वेरी जोड़ सकता है services.py (यदि आपको लगता है कि प्रयोग कर रहे हैं), या एक परिचय queries.py मॉड्यूल:
queries.py
def inactive_users():
return User.objects.filter(active=False)
def users_called_publysher():
for user in User.objects.all():
if remote_api.get_cached_name(user.id) == "publysher":
yield user
प्रॉक्सी मॉडल
व्यावसायिक तर्क और रिपोर्टिंग के संदर्भ में प्रॉक्सी मॉडल बहुत उपयोगी हैं। आप मूल रूप से अपने मॉडल के एक बढ़ाए गए सबसेट को परिभाषित करते हैं। आप Manager.get_queryset()
विधि को ओवरराइड करके किसी प्रबंधक के आधार QuerySet को ओवरराइड कर सकते हैं ।
models.py
class InactiveUserManager(models.Manager):
def get_queryset(self):
query_set = super(InactiveUserManager, self).get_queryset()
return query_set.filter(active=False)
class InactiveUser(User):
"""
>>> for user in InactiveUser.objects.all():
… assert user.active is False
"""
objects = InactiveUserManager()
class Meta:
proxy = True
क्वेरी मॉडल
उन प्रश्नों के लिए जो स्वाभाविक रूप से जटिल हैं, लेकिन अक्सर निष्पादित किए जाते हैं, क्वेरी मॉडल की संभावना है। एक क्वेरी मॉडल एक प्रकार का विकृतीकरण है जहां किसी एकल क्वेरी के लिए प्रासंगिक डेटा को एक अलग मॉडल में संग्रहीत किया जाता है। पाठ्यक्रम की चाल प्राथमिक मॉडल के साथ समरूप मॉडल को सिंक में रखना है। क्वेरी मॉडल का उपयोग केवल तभी किया जा सकता है जब परिवर्तन पूरी तरह से आपके नियंत्रण में हों।
models.py
class InactiveUserDistribution(models.Model):
country = CharField(max_length=200)
inactive_user_count = IntegerField(default=0)
पहला विकल्प इन मॉडलों को आपकी आज्ञाओं में अपडेट करना है। यह बहुत उपयोगी है अगर ये मॉडल केवल एक या दो कमांड द्वारा बदल दिए जाते हैं।
forms.py
class ActivateUserForm(forms.Form):
# see above
def execute(self):
# see above
query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
query_model.inactive_user_count -= 1
query_model.save()
एक बेहतर विकल्प कस्टम सिग्नल का उपयोग करना होगा। ये संकेत निश्चित रूप से आपके आदेशों द्वारा उत्सर्जित होते हैं। सिग्नल का यह फायदा है कि आप अपने मूल मॉडल के साथ कई क्वेरी मॉडल को सिंक में रख सकते हैं। इसके अलावा, सिग्नल प्रोसेसिंग को सेलरी या इसी तरह के फ्रेमवर्क का उपयोग करके पृष्ठभूमि कार्यों के लिए बंद किया जा सकता है।
signals.py
user_activated = Signal(providing_args = ['user'])
user_deactivated = Signal(providing_args = ['user'])
forms.py
class ActivateUserForm(forms.Form):
# see above
def execute(self):
# see above
user_activated.send_robust(sender=self, user=user)
models.py
class InactiveUserDistribution(models.Model):
# see above
@receiver(user_activated)
def on_user_activated(sender, **kwargs):
user = kwargs['user']
query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
query_model.inactive_user_count -= 1
query_model.save()
इसे साफ रखना
इस दृष्टिकोण का उपयोग करते समय, यह निर्धारित करना हास्यास्पद हो जाता है कि आपका कोड साफ रहता है या नहीं। बस इन दिशानिर्देशों का पालन करें:
- क्या मेरे मॉडल में ऐसी विधियाँ हैं जो डेटाबेस स्थिति को प्रबंधित करने से अधिक हैं? आपको एक आदेश निकालना चाहिए।
- क्या मेरे मॉडल में ऐसे गुण हैं जो डेटाबेस फ़ील्ड पर मैप नहीं करते हैं? आपको एक क्वेरी निकालना चाहिए।
- क्या मेरा मॉडल संदर्भ संरचना है जो मेरा डेटाबेस (जैसे मेल) नहीं है? आपको एक आदेश निकालना चाहिए।
वही विचारों के लिए जाता है (क्योंकि विचार अक्सर एक ही समस्या से ग्रस्त होते हैं)।
- क्या मेरा विचार सक्रिय रूप से डेटाबेस मॉडल का प्रबंधन करता है? आपको एक आदेश निकालना चाहिए।
कुछ सन्दर्भ
Django प्रलेखन: प्रॉक्सी मॉडल
Django प्रलेखन: संकेत
वास्तुकला: डोमेन संचालित डिजाइन