2025, Oct 31 01:32
Django DetailView में परमिशन-अनुकूल नेक्स्ट/प्रिवियस नेविगेशन
Django DetailView में उपयोगकर्ता-स्कोप और परमिशन के साथ नेक्स्ट/प्रिवियस, परिपत्र नेविगेशन जोड़ें—बिना पूरी सूची लोड किए; ORM क्वेरियों से तेज, सुरक्षित समाधान
Django की DetailView में अगला/पिछला नेविगेशन जोड़ना आसान लगता है, लेकिन जैसे ही अनुमतियाँ शामिल होती हैं, सबकुछ बदल जाता है. जब उपयोगकर्ता केवल अपने ही रिकॉर्ड देख सकता है, तो आप ID बढ़ाकर मनमाने तरीके से आगे नहीं जा सकते और न ही पूरी तालिका पर इटरेशन कर सकते हैं. उद्देश्य साफ है: उपयोगकर्ता के अनुमत उपसमूह के भीतर चलना, नेविगेशन को परिपत्र रखना, और लिंक्ड लिस्ट जैसी भारी संरचनाओं को पहले से लोड करने से बचना. नीचे दिया गया छोटा, ORM-आधारित तरीका यही काम करता है.
समस्या सेटअप
मान लीजिए आप एंटिटीज़ की सूची रेंडर करते हैं और हर एक को उसकी डिटेल पेज से लिंक करते हैं. URL पैटर्न कुछ ऐसे दिख सकते हैं, और टेम्पलेट से डिटेल पेज लिंक होता है:
urlpatterns = [
    path("records/", views.RecordListView.as_view(), name="records"),
    path("record/<int:pk>/", views.RecordDetailView.as_view(), name="record"),
]
href='{% url "record" obj.id %}'
DetailView सीधी-सादी है:
class RecordDetailView(LoginRequiredMixin, DetailView):
    model = Record
    template_name = "record.html"
आप डिटेल पेज पर ऐसे बटन दिखाना चाहते हैं जो अगला और पिछला रिकॉर्ड खोलें—वही रिकॉर्ड जो मौजूदा उपयोगकर्ता के दायरे में आते हों—और किनारों पर पहुँचने पर घूमकर शुरू/अंत पर चले जाएँ. टेम्पलेट में कुछ ऐसा इस्तेमाल करने की जरूरत पड़ सकती है:
<a class='button' href='{% url "record" next_record_id %}'>Next</a>
कमी बस इतनी है कि next_record_id (और उसका prev समकक्ष) टेम्पलेट तक कैसे पहुँचाएँ—बिना ऐसे डेटा पर इटरेशन किए जिनकी पहुँच आपके पास नहीं है, या मेमोरी में कोई लिंक्ड लिस्ट बनाकर.
ऐसा क्यों होता है
साधारण तौर पर ID बढ़ाकर नेविगेट करने की तरकीब इसलिए काम नहीं करती क्योंकि उपयोगकर्ता के डेटासेट में ID क्रमागत होने की गारंटी नहीं है. कुछ ID दूसरे उपयोगकर्ताओं की हो सकती हैं या हो ही न. हर क्लिक पर पूरी सूची लोड करना अनावश्यक बोझ है, खासकर तब जब डेटाबेस सरल, सीमित क्वेरियों से अगला/पिछला पड़ोसी खुद निकाल सकता है. सबसे साफ उपाय है: डेटाबेस से उपयोगकर्ता के उपसमूह में निकटतम बड़ी या छोटी ID माँगें, और परिपत्रता बनाए रखने के लिए आवश्यकता पड़ने पर पहले या आखिरी आइटम पर लौट आएँ.
समाधान: काम ORM को करने दें
मौजूदा रिकॉर्ड के सापेक्ष अगला और पिछला रिकॉर्ड पाने के लिए दो लक्षित क्वेरियाँ चलाएँ. यदि उपयोगकर्ता के रिकॉर्ड में "अगला" न मिले, तो सबसे छोटी ID पर लपकें. यदि "पिछला" न मिले, तो सबसे बड़ी ID पर जाएँ. दोनों ID को कॉन्टेक्स्ट में जोड़ें और टेम्पलेट में बाँध दें.
class RecordDetailView(LoginRequiredMixin, DetailView):
    model = Record
    template_name = "record.html"
    def get_context_data(self, **kwargs):
        payload = super().get_context_data(**kwargs)
        current = self.get_object()
        nxt = Record.objects.filter(
            user=self.request.user,
            id__gt=current.id
        ).order_by('id').first()
        prv = Record.objects.filter(
            user=self.request.user,
            id__lt=current.id
        ).order_by('-id').first()
        if not nxt:
            nxt = Record.objects.filter(user=self.request.user).order_by('id').first()
        if not prv:
            prv = Record.objects.filter(user=self.request.user).order_by('-id').first()
        payload['next_record_id'] = nxt.id if nxt else None
        payload['prev_record_id'] = prv.id if prv else None
        return payload
और टेम्पलेट में:
{% if prev_record_id %}
    <a class='button' href='{% url "record" prev_record_id %}'>Previous</a>
{% endif %}
{% if next_record_id %}
    <a class='button' href='{% url "record" next_record_id %}'>Next</a>
{% endif %}
यह क्यों मायने रखता है
यह तरीका किसी इन-मेमोरी सीक्वेंस को लोड या बनाए रखने की जरूरत खत्म कर देता है, इसलिए संसाधन-किफायती है. हर लुकअप को उपयोगकर्ता-स्कोप तक सीमित रखने से दृश्यता नियमों का सम्मान भी होता है. साथ ही, बिना किसी खास डेटा स्ट्रक्चर के परिपत्र नेविगेशन मिल जाता है—ORM क्वेरियाँ साफ, पूर्वानुमेय और सँभालने में आसान रहती हैं.
निष्कर्ष
Django की DetailView में अगला/पिछला नेविगेशन के लिए न तो लिंक्ड लिस्ट चाहिए, न सेशन में कोई सीक्वेंस. बस मौजूदा उपयोगकर्ता के डेटा में निकटतम बड़ी और छोटी ID के लिए क्वेरी करें और सिरों पर पहुँचने पर रैप कर दें. लॉजिक को get_context_data के भीतर रखें, गणना की गई ID टेम्पलेट को दें, और लिंक तभी रेंडर करें जब वे मौजूद हों. नतीजा: उपयोगकर्ता के अपने रिकॉर्ड्स में सरल, सुरक्षित और दक्ष नेविगेशन.
यह लेख StackOverflow के एक प्रश्न (लेखक: Franz Müller) और Mahrez BenHamad के उत्तर पर आधारित है।