Django 2.1 - The “sites” framework

"साइट" ढांचा




django

"साइट" ढांचा

Django एक वैकल्पिक "साइटों" ढांचे के साथ आता है। यह विशेष वेबसाइटों के लिए वस्तुओं और कार्यक्षमता को जोड़ने के लिए एक हुक है, और यह डोमेन नाम और आपके Django संचालित साइटों के "क्रिया" नामों के लिए एक होल्डिंग जगह है।

यदि आपकी एकल Django स्थापना शक्तियाँ एक से अधिक साइट पर हैं और आपको किसी तरह से उन साइटों के बीच अंतर करने की आवश्यकता है, तो इसका उपयोग करें।

साइट रूपरेखा मुख्य रूप से एक साधारण मॉडल पर आधारित है:

class models.Site

domain और वेबसाइट की name विशेषताओं को संग्रहीत करने के लिए एक मॉडल।

domain

पूरी तरह से योग्य डोमेन नाम वेबसाइट के साथ जुड़ा हुआ है। उदाहरण के लिए, www.example.com

name

वेबसाइट के लिए एक मानव पठनीय "क्रिया" नाम।

SITE_ID सेटिंग उस विशेष सेटिंग फ़ाइल से संबद्ध Site ऑब्जेक्ट की डेटाबेस आईडी को निर्दिष्ट करती है। यदि सेटिंग को छोड़ दिया जाता है, तो get_current_site() फ़ंक्शन domain को होस्ट नाम के साथ request.get_host() तुलना करके वर्तमान साइट प्राप्त करने का प्रयास करेगा request.get_host() विधि।

आप इसका उपयोग कैसे करते हैं यह आप पर निर्भर करता है, लेकिन Django इसका उपयोग सरल सम्मेलनों के माध्यम से स्वचालित रूप से एक-दो तरीकों से करता है।

उदाहरण उपयोग

आप साइटों का उपयोग क्यों करेंगे? यह उदाहरणों के माध्यम से सबसे अच्छा समझाया गया है।

सामग्री को कई साइटों से जोड़ना

Django द्वारा संचालित साइट्स LJWorld.com और Lawrence.com एक ही समाचार संगठन - लॉरेंस जर्नल-वर्ल्ड अखबार लॉरेंस, कंसास में संचालित हैं। LJWorld.com समाचार पर केंद्रित है, जबकि लॉरेंस.कॉम स्थानीय मनोरंजन पर केंद्रित है। लेकिन कभी-कभी संपादक दोनों साइटों पर एक लेख प्रकाशित करना चाहते हैं।

समस्या को हल करने का भोला तरीका साइट निर्माताओं को एक ही कहानी को दो बार प्रकाशित करने की आवश्यकता होगी: एक बार LJWorld.com के लिए और फिर लॉरेंस.कॉम के लिए। लेकिन साइट के उत्पादकों के लिए यह अक्षम है, और यह डेटाबेस में एक ही कहानी की कई प्रतियों को संग्रहीत करने के लिए अनावश्यक है।

बेहतर समाधान सरल है: दोनों साइटें एक ही लेख डेटाबेस का उपयोग करती हैं, और एक लेख एक या अधिक साइटों से जुड़ा होता है। Django मॉडल शब्दावली में, यह एक ManyToManyField द्वारा Article मॉडल में दर्शाया गया है:

from django.contrib.sites.models import Site
from django.db import models

class Article(models.Model):
    headline = models.CharField(max_length=200)
    # ...
    sites = models.ManyToManyField(Site)

यह बहुत अच्छी तरह से कई चीजों को पूरा करता है:

  • यह साइट के उत्पादकों को सभी सामग्री - दोनों साइटों पर - एक ही इंटरफ़ेस (Django व्यवस्थापक) को संपादित करने देता है।
  • इसका मतलब है कि एक ही कहानी को डेटाबेस में दो बार प्रकाशित नहीं किया जाना चाहिए; इसका केवल डेटाबेस में एक ही रिकॉर्ड है।
  • यह साइट डेवलपर्स को दोनों साइटों के लिए समान Django दृश्य कोड का उपयोग करने देता है। दृश्य कोड जो किसी दी गई कहानी को प्रदर्शित करता है, यह सुनिश्चित करने के लिए जांचता है कि अनुरोधित कहानी वर्तमान साइट पर है। यह कुछ इस तरह दिखता है:

    from django.contrib.sites.shortcuts import get_current_site
    
    def article_detail(request, article_id):
        try:
            a = Article.objects.get(id=article_id, sites__id=get_current_site(request).id)
        except Article.DoesNotExist:
            raise Http404("Article does not exist on this site")
        # ...
    

किसी एकल साइट के साथ सामग्री जोड़ना

इसी तरह, आप ForeignKey का उपयोग करके एक मॉडल को कई-से-एक रिश्ते में Site मॉडल से जोड़ सकते हैं।

उदाहरण के लिए, यदि किसी लेख को केवल एक साइट पर अनुमति दी जाती है, तो आप इस तरह से एक मॉडल का उपयोग करेंगे:

from django.contrib.sites.models import Site
from django.db import models

class Article(models.Model):
    headline = models.CharField(max_length=200)
    # ...
    site = models.ForeignKey(Site, on_delete=models.CASCADE)

इसका वही लाभ है जो अंतिम अनुभाग में वर्णित है।

विचारों से वर्तमान साइट में हुकिंग

जिस साइट पर दृश्य कहा जा रहा है, उसके आधार पर आप अपने Django दृश्यों में साइटों की रूपरेखा का उपयोग कर सकते हैं। उदाहरण के लिए:

from django.conf import settings

def my_view(request):
    if settings.SITE_ID == 3:
        # Do something.
        pass
    else:
        # Do something else.
        pass

बेशक, यह इस तरह से साइट आईडी को हार्ड-कोड करने के लिए बदसूरत है। इस तरह के हार्ड-कोडिंग हैकिश फिक्स के लिए सबसे अच्छा है जो आपको जल्दी से करने की आवश्यकता है। एक ही चीज़ को पूरा करने का क्लीनर तरीका वर्तमान साइट के डोमेन की जांच करना है:

from django.contrib.sites.shortcuts import get_current_site

def my_view(request):
    current_site = get_current_site(request)
    if current_site.domain == 'foo.com':
        # Do something
        pass
    else:
        # Do something else.
        pass

यह जाँचने का भी लाभ है कि क्या साइट फ्रेमवर्क स्थापित है, और अगर यह नहीं है तो एक RequestSite उदाहरण RequestSite

यदि आपके पास अनुरोध ऑब्जेक्ट तक नहीं है, तो आप Site मॉडल के प्रबंधक के get_current() विधि का उपयोग कर सकते हैं। फिर आपको यह सुनिश्चित करना चाहिए कि आपकी सेटिंग फ़ाइल में SITE_ID सेटिंग है। यह उदाहरण पिछले एक के बराबर है:

from django.contrib.sites.models import Site

def my_function_without_request():
    current_site = Site.objects.get_current()
    if current_site.domain == 'foo.com':
        # Do something
        pass
    else:
        # Do something else.
        pass

प्रदर्शन के लिए वर्तमान डोमेन प्राप्त करना

LJWorld.com और Lawrence.com दोनों में ईमेल अलर्ट कार्यक्षमता है, जो पाठकों को समाचार होने पर सूचनाएं प्राप्त करने के लिए साइन अप करने की सुविधा देता है। यह बहुत बुनियादी है: एक पाठक वेब फॉर्म पर साइन अप करता है और तुरंत एक ईमेल प्राप्त करता है, "आपकी सदस्यता के लिए धन्यवाद।"

दो बार इस साइन अप प्रोसेसिंग कोड को लागू करना अक्षम और बेमानी होगा, इसलिए साइटें पर्दे के पीछे एक ही कोड का उपयोग करती हैं। लेकिन "साइन अप करने के लिए धन्यवाद" नोटिस प्रत्येक साइट के लिए अलग होना चाहिए। Site ऑब्जेक्ट का उपयोग करके, हम वर्तमान साइट के name और domain के मूल्यों का उपयोग करने के लिए "धन्यवाद" नोटिस को सार कर सकते हैं।

यहाँ एक उदाहरण है कि फ़ॉर्म-हैंडलिंग दृश्य कैसा दिखता है:

from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import send_mail

def register_for_newsletter(request):
    # Check form values, etc., and subscribe the user.
    # ...

    current_site = get_current_site(request)
    send_mail(
        'Thanks for subscribing to %s alerts' % current_site.name,
        'Thanks for your subscription. We appreciate it.\n\n-The %s team.' % (
            current_site.name,
        ),
        '[email protected]%s' % current_site.domain,
        [user.email],
    )

    # ...

Lawrence.com पर, इस ईमेल की विषय पंक्ति है "Lawrence.com अलर्ट की सदस्यता के लिए धन्यवाद।" LJWorld.com पर, ईमेल में विषय है "LJWorld.com अलर्ट की सदस्यता के लिए धन्यवाद।" वही ईमेल के संदेश निकाय के लिए जाता है। ।

ध्यान दें कि ऐसा करने का एक और अधिक लचीला (लेकिन अधिक भारी) तरीका Django के टेम्पलेट सिस्टम का उपयोग करना होगा। Lawrence.com और LJWorld.com की मानें तो अलग-अलग टेम्प्लेट डायरेक्टरीज़ ( DIRS ) हैं, आप बस टेम्प्लेट सिस्टम की तरह खेती कर सकते हैं:

from django.core.mail import send_mail
from django.template import Context, loader

def register_for_newsletter(request):
    # Check form values, etc., and subscribe the user.
    # ...

    subject = loader.get_template('alerts/subject.txt').render(Context({}))
    message = loader.get_template('alerts/message.txt').render(Context({}))
    send_mail(subject, message, '[email protected]', [user.email])

    # ...

इस स्थिति में, आपको LJWorld.com और Lawrence.com टेम्प्लेट निर्देशिकाओं के लिए subject.txt और message.txt टेम्प्लेट फ़ाइलों को बनाना होगा। यह आपको अधिक लचीलापन देता है, लेकिन यह अधिक जटिल भी है।

अनावश्यक वस्तुओं और अतिरेक को दूर करने के लिए Site ऑब्जेक्ट्स का यथासंभव उपयोग करना एक अच्छा विचार है।

पूर्ण URL के लिए वर्तमान डोमेन प्राप्त करना

Django का get_absolute_url() कन्वेंशन डोमेन नाम के बिना आपकी वस्तुओं का URL प्राप्त करने के लिए अच्छा है, लेकिन कुछ मामलों में आप पूर्ण URL - http:// और डोमेन और सब कुछ - एक ऑब्जेक्ट के लिए प्रदर्शित करना चाह सकते हैं। ऐसा करने के लिए, आप साइटों के ढांचे का उपयोग कर सकते हैं। एक सरल उदाहरण:

>>> from django.contrib.sites.models import Site
>>> obj = MyModel.objects.get(id=3)
>>> obj.get_absolute_url()
'/mymodel/objects/3/'
>>> Site.objects.get_current().domain
'example.com'
>>> 'https://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
'https://example.com/mymodel/objects/3/'

साइटों के ढांचे को सक्षम करना

साइटों के ढांचे को सक्षम करने के लिए, इन चरणों का पालन करें:

  1. अपनी INSTALLED_APPS सेटिंग में 'django.contrib.sites' जोड़ें।
  2. SITE_ID सेटिंग परिभाषित करें:

    SITE_ID = 1
    
  3. migrate चलाएं।

django.contrib.sites एक post_migrate सिग्नल हैंडलर को पंजीकृत करता है जो डोमेन example.com साथ example.com नामक एक डिफ़ॉल्ट साइट बनाता है। Django टेस्ट डेटाबेस बनाने के बाद इस साइट को भी बनाया जाएगा। अपनी परियोजना के लिए सही नाम और डोमेन सेट करने के लिए, आप डेटा माइग्रेशन का उपयोग कर सकते हैं।

उत्पादन में विभिन्न साइटों की सेवा करने के लिए, आप प्रत्येक SITE_ID साथ एक अलग सेटिंग्स फ़ाइल SITE_ID (शायद साझा सेटिंग्स से बचने के लिए एक सामान्य सेटिंग फ़ाइल से आयात करें) और फिर प्रत्येक साइट के लिए उपयुक्त DJANGO_SETTINGS_MODULE निर्दिष्ट करें।

वर्तमान Site ऑब्जेक्ट कैशिंग

जैसा कि वर्तमान साइट डेटाबेस में संग्रहीत है, प्रत्येक साइट Site.objects.get_current() एक डेटाबेस क्वेरी में परिणाम हो सकता है। लेकिन Django इससे थोड़ा चालाक है: पहले अनुरोध पर, वर्तमान साइट कैश की गई है, और किसी भी बाद की कॉल डेटाबेस को मारने के बजाय कैश किए गए डेटा को वापस करती है।

यदि किसी कारण से आप डेटाबेस क्वेरी को बाध्य करना चाहते हैं, तो आप Django को Site.objects.clear_cache() का उपयोग करके कैश साफ़ करने के लिए कह सकते हैं:

# First call; current site fetched from database.
current_site = Site.objects.get_current()
# ...

# Second call; current site fetched from cache.
current_site = Site.objects.get_current()
# ...

# Force a database query for the third call.
Site.objects.clear_cache()
current_site = Site.objects.get_current()

CurrentSiteManager

class managers.CurrentSiteManager

यदि Site आपके आवेदन में एक महत्वपूर्ण भूमिका निभाती है, तो अपने मॉडल (ओं) में सहायक CurrentSiteManager का उपयोग करने पर विचार करें। यह एक मॉडल manager जो स्वचालित रूप से वर्तमान Site जुड़ी वस्तुओं को शामिल करने के लिए अपने प्रश्नों को फ़िल्टर करता है।

अनिवार्य SITE_ID

जब आपकी सेटिंग में SITE_ID सेटिंग परिभाषित हो, तो SITE_ID केवल उपयोग करने योग्य है।

अपने मॉडल में स्पष्ट रूप से जोड़कर CurrentSiteManager उपयोग करें। उदाहरण के लिए:

from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.db import models

class Photo(models.Model):
    photo = models.FileField(upload_to='photos')
    photographer_name = models.CharField(max_length=100)
    pub_date = models.DateField()
    site = models.ForeignKey(Site, on_delete=models.CASCADE)
    objects = models.Manager()
    on_site = CurrentSiteManager()

इस मॉडल के साथ, Photo.objects.all() डेटाबेस में सभी Photo ऑब्जेक्ट Photo.on_site.all() , लेकिन Photo.on_site.all() केवल SITE_ID सेटिंग के अनुसार, वर्तमान साइट से जुड़े Photo ऑब्जेक्ट वापस कर देगा।

एक और तरीका है, इन दो बयानों के बराबर हैं:

Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()

CurrentSiteManager कैसे पता CurrentSiteManager कि Photo किस क्षेत्र की Site ? डिफ़ॉल्ट रूप से, CurrentSiteManager site पर फ़िल्टर करने के लिए या तो एक ForeignKey site या ManyToManyField नामक sites की तलाश करता है। यदि आप site या sites अलावा किसी अन्य नाम की फ़ील्ड का उपयोग करते हैं, तो यह पहचानने के लिए कि आपकी ऑब्जेक्ट किस Site से संबंधित है, तो आपको अपने मॉडल पर CurrentSiteManager पैरामीटर के रूप में कस्टम फ़ील्ड नाम को स्पष्ट रूप से पास करना होगा। निम्न मॉडल, जिसमें publish_on नामक publish_on , इसे प्रदर्शित करता है:

from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.db import models

class Photo(models.Model):
    photo = models.FileField(upload_to='photos')
    photographer_name = models.CharField(max_length=100)
    pub_date = models.DateField()
    publish_on = models.ForeignKey(Site, on_delete=models.CASCADE)
    objects = models.Manager()
    on_site = CurrentSiteManager('publish_on')

यदि आप CurrentSiteManager का उपयोग करने का प्रयास करते हैं और ऐसा फ़ील्ड नाम पास करते हैं जो मौजूद नहीं है, तो Django एक ValueError बढ़ाएगा।

अंत में, ध्यान दें कि आप शायद अपने मॉडल पर एक सामान्य (गैर-साइट-विशिष्ट) Manager रखना चाहते हैं, भले ही आप CurrentSiteManager उपयोग CurrentSiteManager । जैसा कि manager में समझाया गया है, यदि आप मैन्युअल रूप से प्रबंधक परिभाषित करते हैं, तो Django आपके लिए स्वचालित objects = models.Manager() नहीं बना सकता है। यह भी ध्यान दें कि Django के कुछ भागों - अर्थात्, Django व्यवस्थापक साइट और सामान्य विचार - जो भी प्रबंधक का उपयोग मॉडल में सबसे पहले परिभाषित किया गया है, इसलिए यदि आप चाहते हैं कि आपके व्यवस्थापक साइट पर सभी वस्तुओं (न केवल साइट-विशिष्ट वाले) तक पहुंच हो, तो अपने मॉडल में objects = models.Manager() CurrentSiteManager , इससे पहले कि आप CurrentSiteManager को परिभाषित CurrentSiteManager

साइट के मिडिलवेयर

यदि आप अक्सर इस पैटर्न का उपयोग करते हैं:

from django.contrib.sites.models import Site

def my_view(request):
    site = Site.objects.get_current()
    ...

पुनरावृत्ति से बचने का सरल तरीका है। django.contrib.sites.middleware.CurrentSiteMiddleware को MIDDLEWARE जोड़ें। मिडलवेयर हर अनुरोध ऑब्जेक्ट पर site विशेषता सेट करता है, ताकि आप वर्तमान साइट प्राप्त करने के लिए request.site का उपयोग कर सकें।

Django साइट्स फ्रेमवर्क का उपयोग कैसे करता है

यद्यपि यह आवश्यक नहीं है कि आप साइटों के ढांचे का उपयोग करते हैं, लेकिन यह दृढ़ता से प्रोत्साहित किया जाता है, क्योंकि Django कुछ स्थानों पर इसका लाभ उठाता है। यहां तक ​​कि अगर आपका Django इंस्टॉलेशन केवल एक साइट को पावर कर रहा है, तो आपको अपने domain और name साथ साइट ऑब्जेक्ट बनाने के लिए दो सेकंड का समय लेना चाहिए, और अपनी SITE_ID सेटिंग में इसकी आईडी को SITE_ID करना चाहिए।

यहाँ बताया गया है कि कैसे Django साइटों की रूपरेखा का उपयोग करता है:

  • redirects framework , प्रत्येक रीडायरेक्ट ऑब्जेक्ट किसी विशेष साइट से जुड़ा होता है। जब Django रीडायरेक्ट करता है, तो यह वर्तमान साइट को ध्यान में रखता है।
  • flatpages framework , प्रत्येक फ़्लैटपेज किसी विशेष साइट से जुड़ा होता है। जब एक फ़्लैटपेज बनाया जाता है, तो आप इसकी Site निर्दिष्ट करते हैं, और FlatpageFallbackMiddleware फ़ॉलबैकमैडलवेयर प्रदर्शित करने के लिए फ़्लैटपेज को पुनः प्राप्त करने में वर्तमान साइट की जाँच करता है।
  • syndication framework , title और description लिए टेम्पलेट्स में स्वचालित रूप से एक चर {{ site }} तक पहुंच होती है, जो वर्तमान साइट का प्रतिनिधित्व करने वाली Site ऑब्जेक्ट है। साथ ही, आइटम URL प्रदान करने के लिए हुक वर्तमान Site ऑब्जेक्ट से domain उपयोग करेगा यदि आप पूरी तरह से योग्य डोमेन निर्दिष्ट नहीं करते हैं।
  • authentication framework , django.contrib.auth.views.LoginView वर्तमान Site नाम टेम्पलेट को {{ site_name }}
  • शॉर्टकट दृश्य ( django.contrib.contenttypes.views.shortcut ) किसी ऑब्जेक्ट के URL की गणना करते समय वर्तमान Site ऑब्जेक्ट के डोमेन का उपयोग करता है।
  • व्यवस्थापक ढांचे में, "साइट पर दृश्य" लिंक वर्तमान Site का उपयोग उस Site लिए डोमेन को बाहर करने के लिए करता है जिसे वह पुनर्निर्देशित करेगा।

RequestSite वस्तुएं

कुछ django.contrib एप्लिकेशन साइट फ्रेमवर्क का लाभ उठाते हैं, लेकिन इसे इस तरह से आर्किटेक्चर किया जाता है, जिसके लिए आपके डेटाबेस में साइट्स फ्रेमवर्क को इंस्टॉल करने की आवश्यकता नहीं होती है। (कुछ लोग साइट ढांचे की आवश्यकता वाले अतिरिक्त डेटाबेस तालिका को स्थापित नहीं करना चाहते हैं या बस नहीं कर पा रहे हैं।) उन मामलों के लिए, फ्रेमवर्क RequestSite वर्ग प्रदान करता है, जो हो सकता है। डेटाबेस समर्थित साइटों की रूपरेखा उपलब्ध नहीं होने पर एक कमबैक के रूप में उपयोग किया जाता है।

class requests.RequestSite

एक वर्ग जो Site के प्राथमिक इंटरफ़ेस को साझा करता है (यानी, इसमें domain और name विशेषताएँ हैं) लेकिन इसका डेटा एक डेटाबेस के बजाय एक Django HttpRequest ऑब्जेक्ट से प्राप्त होता है।

__init__(request)

request.get_host() के मान पर name और domain विशेषताएँ सेट करता है request.get_host()

एक RequestSite ऑब्जेक्ट का सामान्य Site ऑब्जेक्ट के समान इंटरफ़ेस है, सिवाय इसके __init__() विधि HttpRequest ऑब्जेक्ट लेता है। यह अनुरोध के डोमेन को देखकर domain और name को कम करने में सक्षम है। इसमें Site के इंटरफ़ेस से मिलान करने के लिए save() और delete() तरीके हैं, लेकिन तरीके NotImplementedError

get_current_site शॉर्टकट

अंत में, दोहराए गए get_current_site() कोड से बचने के लिए, फ्रेमवर्क एक get_current_site() फ़ंक्शन प्रदान करता है।

shortcuts.get_current_site(request)

एक फ़ंक्शन जो चेक करता है कि क्या django.contrib.sites स्थापित है और अनुरोध के आधार पर या तो वर्तमान Site ऑब्जेक्ट या एक RequestSite ऑब्जेक्ट वापस करता है। यदि request.get_host() SITE_ID सेटिंग परिभाषित नहीं है, तो यह request.get_host() आधार पर वर्तमान साइट को SITE_ID है।

डोमेन और पोर्ट दोनों को request.get_host() द्वारा वापस किया जा सकता है request.get_host() जब होस्ट हेडर में पोर्ट स्पष्ट रूप से निर्दिष्ट होता है, जैसे example.com:80 । ऐसे मामलों में, यदि लुकअप विफल हो जाता है क्योंकि होस्ट डेटाबेस में रिकॉर्ड से मेल नहीं खाता है, तो पोर्ट छीन लिया जाता है और लुकअप केवल डोमेन भाग के साथ वापस लिया जाता है। यह RequestSite लागू नहीं होता है जो हमेशा RequestSite होस्ट का उपयोग करेगा।