Django 2.1 - Built-in class-based generic views

बिल्ट-इन क्लास-आधारित जेनेरिक विचार




django

बिल्ट-इन क्लास-आधारित जेनेरिक विचार

वेब एप्लिकेशन लिखना नीरस हो सकता है, क्योंकि हम कुछ पैटर्न को बार-बार दोहराते हैं। Django मॉडल और टेम्पलेट परतों में उस एकरसता को दूर करने की कोशिश करता है, लेकिन वेब डेवलपर्स भी दृश्य स्तर पर इस ऊब का अनुभव करते हैं।

उस दर्द को कम करने के लिए Django के सामान्य विचारों को विकसित किया गया था। वे विकास में पाए जाने वाले कुछ सामान्य मुहावरों और प्रतिमानों को लेते हैं और उन्हें अमूर्त करते हैं ताकि आप बहुत अधिक कोड लिखने के बिना डेटा के सामान्य दृश्य जल्दी से लिख सकें।

हम कुछ सामान्य कार्यों को पहचान सकते हैं, जैसे वस्तुओं की सूची प्रदर्शित करना, और कोड लिखना जो किसी भी वस्तु की सूची प्रदर्शित करता है। फिर प्रश्न में मॉडल को URLconf के अतिरिक्त तर्क के रूप में पारित किया जा सकता है।

निम्नलिखित करने के लिए सामान्य विचारों वाले Django के जहाज:

  • किसी एकल ऑब्जेक्ट के लिए सूची और विवरण पृष्ठ प्रदर्शित करें। यदि हम सम्मेलनों को प्रबंधित करने के लिए एक एप्लिकेशन बना रहे थे तो एक TalkListView और एक RegisteredUserListView सूची विचारों के उदाहरण होंगे। एक एकल टॉक पेज एक उदाहरण है जिसे हम "विस्तार" दृश्य कहते हैं।
  • वर्ष / माह / दिन संग्रह पृष्ठों, संबंधित विवरण और "नवीनतम" पृष्ठों में दिनांक आधारित वस्तुओं को प्रस्तुत करें।
  • उपयोगकर्ताओं को प्राधिकरण के साथ या उसके बिना - ऑब्जेक्ट बनाने, अपडेट करने और हटाने की अनुमति दें।

एक साथ लिया गया, ये दृश्य सबसे आम कार्य डेवलपर्स मुठभेड़ करने के लिए आसान इंटरफेस प्रदान करते हैं।

जेनेरिक विचारों का विस्तार

इसमें कोई सवाल नहीं है कि सामान्य विचारों का उपयोग करने से विकास में काफी तेजी आ सकती है। अधिकांश परियोजनाओं में, हालांकि, एक क्षण आता है जब सामान्य विचार अब पर्याप्त नहीं होते हैं। वास्तव में, नए Django डेवलपर्स द्वारा पूछा जाने वाला सबसे आम सवाल यह है कि सामान्य विचारों को स्थितियों की एक व्यापक सरणी को कैसे संभालना है।

यह एक कारण है कि सामान्य विचारों को 1.3 रिलीज के लिए पुन: डिज़ाइन किया गया था - पहले, वे केवल विकल्पों की एक शानदार सरणी के साथ कार्य देख रहे थे; अब, URLconf में बड़ी मात्रा में कॉन्फ़िगरेशन को पारित करने के बजाय, जेनेरिक विचारों को विस्तारित करने का अनुशंसित तरीका उन्हें उप-वर्ग करना है, और उनकी विशेषताओं या विधियों को ओवरराइड करना है।

उस ने कहा, सामान्य विचारों की एक सीमा होगी। यदि आप पाते हैं कि आप अपने दृष्टिकोण को एक सामान्य दृश्य के उपवर्ग के रूप में लागू करने के लिए संघर्ष कर रहे हैं, तो आप अपने स्वयं के वर्ग-आधारित या कार्यात्मक विचारों का उपयोग करते हुए, केवल अपनी आवश्यकता वाले कोड को लिखने के लिए अधिक प्रभावी हो सकते हैं।

जेनेरिक विचारों के अधिक उदाहरण कुछ तीसरे पक्ष के अनुप्रयोगों में उपलब्ध हैं, या आप अपनी आवश्यकतानुसार लिख सकते हैं।

वस्तुओं के सामान्य विचार

TemplateView निश्चित रूप से उपयोगी है, लेकिन जब आपके डेटाबेस की सामग्री के विचार प्रस्तुत करने की बात आती है, तो Django के सामान्य विचार वास्तव में चमकते हैं। क्योंकि यह इतना सामान्य कार्य है, Django मुट्ठी भर में निर्मित सामान्य विचारों के साथ आता है जो वस्तुओं की सूची और विवरणों को अविश्वसनीय रूप से आसान बनाते हैं।

आइए वस्तुओं की सूची या किसी व्यक्तिगत वस्तु को दिखाने के कुछ उदाहरणों को देखकर शुरू करें।

हम इन मॉडलों का उपयोग करेंगे:

# models.py
from django.db import models

class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()

    class Meta:
        ordering = ["-name"]

    def __str__(self):
        return self.name

class Author(models.Model):
    salutation = models.CharField(max_length=10)
    name = models.CharField(max_length=200)
    email = models.EmailField()
    headshot = models.ImageField(upload_to='author_headshots')

    def __str__(self):
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField('Author')
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    publication_date = models.DateField()

अब हमें एक दृश्य को परिभाषित करने की आवश्यकता है:

# views.py
from django.views.generic import ListView
from books.models import Publisher

class PublisherList(ListView):
    model = Publisher

अंत में अपने मूत्रालयों में देखने वाले हुक:

# urls.py
from django.urls import path
from books.views import PublisherList

urlpatterns = [
    path('publishers/', PublisherList.as_view()),
]

यह सब पायथन कोड है जिसे हमें लिखना होगा। हमें फिर भी एक खाका लिखना होगा। हम स्पष्ट रूप से उस दृश्य को बता सकते हैं जो टेम्पलेट_name विशेषता को देखने के लिए किस टेम्पलेट का उपयोग करता है, लेकिन एक स्पष्ट टेम्पलेट की अनुपस्थिति में Django ऑब्जेक्ट के नाम से एक अनुमान लगाएगा। इस मामले में, अनुमानित टेम्प्लेट "books/publisher_list.html" - "किताबें" भाग मॉडल को परिभाषित करने वाले ऐप के नाम से आता है, जबकि "प्रकाशक" बिट मॉडल के नाम का केवल निचला संस्करण है।

ध्यान दें

इस प्रकार, जब (उदाहरण के लिए) एक DjangoTemplates बैकेंड का APP_DIRS विकल्प TEMPLATES में सही पर सेट किया गया है, तो एक टेम्पलेट स्थान हो सकता है: /path/to/project/books/templates/books/ublisher_list.html

यह टेम्प्लेट एक संदर्भ के विरुद्ध प्रदान किया जाएगा जिसमें ऑब्जेक्ट_लिस्ट नामक एक चर है जिसमें सभी प्रकाशक ऑब्जेक्ट हैं। एक बहुत ही सरल टेम्पलेट निम्नलिखित की तरह लग सकता है:

{% extends "base.html" %}

{% block content %}
    <h2>Publishers</h2>
    <ul>
        {% for publisher in object_list %}
            <li>{{ publisher.name }}</li>
        {% endfor %}
    </ul>
{% endblock %}

वास्तव में यही सब कुछ है। जेनेरिक विचारों की सभी शांत विशेषताएं जेनेरिक दृश्य पर निर्धारित विशेषताओं को बदलने से आती हैं। जेनेरिक विचार संदर्भ दस्तावेज सभी जेनेरिक विचारों और उनके विकल्पों के बारे में विस्तार से; इस दस्तावेज़ के बाकी कुछ सामान्य तरीकों पर विचार करेंगे जिन्हें आप अनुकूलित कर सकते हैं और सामान्य विचार बढ़ा सकते हैं।

"अनुकूल" टेम्पलेट संदर्भ बनाना

आपने देखा होगा कि हमारा नमूना प्रकाशक सूची टेम्प्लेट सभी प्रकाशकों को object_list नाम के एक चर में संग्रहीत करता है। हालांकि यह ठीक काम करता है, यह सब "अनुकूल" टेम्पलेट लेखकों के लिए नहीं है: उन्हें "बस पता है" कि वे यहां प्रकाशकों के साथ काम कर रहे हैं।

ठीक है, यदि आप एक मॉडल ऑब्जेक्ट के साथ काम कर रहे हैं, तो यह आपके लिए पहले से ही किया गया है। जब आप किसी ऑब्जेक्ट या क्वेरीसेट के साथ काम कर रहे होते हैं, तो Django मॉडल क्लास के नाम के निचले आवरण संस्करण का उपयोग करके संदर्भ को पॉप्युलेट करने में सक्षम होता है। यह डिफ़ॉल्ट object_list प्रविष्टि के अतिरिक्त प्रदान किया जाता है, लेकिन इसमें समान डेटा, अर्थात publisher_list

यदि यह अभी भी एक अच्छा मैच नहीं है, तो आप संदर्भ चर का नाम मैन्युअल रूप से सेट कर सकते हैं। सामान्य दृश्य पर context_object_name विशेषता संदर्भ चर का उपयोग करने के लिए निर्दिष्ट करती है:

# views.py
from django.views.generic import ListView
from books.models import Publisher

class PublisherList(ListView):
    model = Publisher
    context_object_name = 'my_favorite_publishers'

एक उपयोगी context_object_name प्रदान context_object_name हमेशा एक अच्छा विचार है। आपके सहकर्मी जो टेम्पलेट डिज़ाइन करते हैं, वे आपको धन्यवाद देंगे।

अतिरिक्त संदर्भ जोड़ना

अक्सर आपको बस कुछ अतिरिक्त जानकारी प्रस्तुत करने की आवश्यकता होती है जो कि सामान्य दृश्य द्वारा प्रदान की जाती है। उदाहरण के लिए, प्रत्येक प्रकाशक विवरण पृष्ठ पर सभी पुस्तकों की एक सूची दिखाने के बारे में सोचें। DetailView जेनेरिक दृश्य प्रकाशक को संदर्भ प्रदान करता है, लेकिन हम उस टेम्पलेट में अतिरिक्त जानकारी कैसे प्राप्त करते हैं?

इसका जवाब है विस्तार से DetailView और get_context_data पद्धति का अपना कार्यान्वयन प्रदान करें। डिफ़ॉल्ट कार्यान्वयन बस टेम्पलेट में प्रदर्शित की जा रही वस्तु को जोड़ता है, लेकिन आप इसे अधिक भेजने के लिए ओवरराइड कर सकते हैं:

from django.views.generic import DetailView
from books.models import Book, Publisher

class PublisherDetail(DetailView):

    model = Publisher

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)
        # Add in a QuerySet of all the books
        context['book_list'] = Book.objects.all()
        return context

ध्यान दें

आम तौर पर, get_context_data सभी मूल वर्गों के संदर्भ डेटा को वर्तमान वर्ग के साथ मिला देगा। इस व्यवहार को अपनी कक्षाओं में संरक्षित करने के लिए जहां आप संदर्भ को बदलना चाहते हैं, आपको सुपर क्लास पर get_context_data को कॉल करना सुनिश्चित करना चाहिए। जब कोई भी दो वर्ग एक ही कुंजी को परिभाषित करने का प्रयास नहीं करते हैं, तो यह अपेक्षित परिणाम देगा। हालाँकि, यदि कोई भी वर्ग माता-पिता की कक्षाओं को सेट करने के बाद एक कुंजी को ओवरराइड करने का प्रयास करता है (सुपर को कॉल करने के बाद), तो उस वर्ग के किसी भी बच्चे को सुपर के बाद स्पष्ट रूप से सेट करने की आवश्यकता होगी यदि वे सभी माता-पिता को ओवरराइड करना सुनिश्चित करना चाहते हैं। यदि आपको परेशानी हो रही है, तो अपने दृश्य के विधि समाधान क्रम की समीक्षा करें।

एक और विचार यह है कि क्लास-आधारित जेनेरिक विचारों से संदर्भ डेटा संदर्भ प्रोसेसर द्वारा प्रदान किए गए डेटा को ओवरराइड करेगा; एक उदाहरण के लिए get_context_data() देखें।

वस्तुओं के सबसेट को देखना

अब हम उस model तर्क पर करीब से नज़र डालते हैं जिसका हम सभी उपयोग कर रहे हैं। model तर्क, जो डेटाबेस मॉडल को निर्दिष्ट करता है कि दृश्य किस पर काम करेगा, सभी सामान्य विचारों पर उपलब्ध है जो एक वस्तु या वस्तुओं के संग्रह पर काम करता है। हालाँकि, model तर्क उन ऑब्जेक्ट्स को निर्दिष्ट करने का एकमात्र तरीका नहीं है जिन्हें देखने पर काम होगा - आप queryset तर्क का उपयोग करके ऑब्जेक्ट्स की सूची भी निर्दिष्ट कर सकते हैं:

from django.views.generic import DetailView
from books.models import Publisher

class PublisherDetail(DetailView):

    context_object_name = 'publisher'
    queryset = Publisher.objects.all()

model = Publisher निर्दिष्ट करना model = Publisher को वास्तव में queryset = Publisher.objects.all() कहने के लिए केवल शॉर्टहैंड है। हालाँकि, ऑब्जेक्ट्स की फ़िल्टर की गई सूची को परिभाषित करने के लिए queryset का उपयोग करके आप उन वस्तुओं के बारे में अधिक विशिष्ट हो सकते हैं जो दृश्य में दिखाई देंगी ( QuerySet ऑब्जेक्ट के बारे में अधिक जानकारी के लिए प्रश्न बनाना , और संपूर्ण विवरण के लिए वर्ग-आधारित विचार संदर्भ देखें। )।

एक साधारण उदाहरण चुनने के लिए, हम प्रकाशन की तारीख से पुस्तकों की सूची मंगाना चाहते हैं, सबसे हाल ही में:

from django.views.generic import ListView
from books.models import Book

class BookList(ListView):
    queryset = Book.objects.order_by('-publication_date')
    context_object_name = 'book_list'

यह एक बहुत सरल उदाहरण है, लेकिन यह अच्छी तरह से विचार दिखाता है। बेशक, आप आमतौर पर सिर्फ रिऑर्डर ऑब्जेक्ट्स से अधिक करना चाहते हैं। यदि आप किसी विशेष प्रकाशक द्वारा पुस्तकों की सूची प्रस्तुत करना चाहते हैं, तो आप उसी तकनीक का उपयोग कर सकते हैं:

from django.views.generic import ListView
from books.models import Book

class AcmeBookList(ListView):

    context_object_name = 'book_list'
    queryset = Book.objects.filter(publisher__name='ACME Publishing')
    template_name = 'books/acme_list.html'

ध्यान दें कि फ़िल्टर किए गए queryset के साथ, हम एक कस्टम टेम्पलेट नाम का भी उपयोग कर रहे हैं। यदि हम नहीं करते हैं, तो सामान्य दृश्य "वैनिला" ऑब्जेक्ट सूची के समान टेम्पलेट का उपयोग करेगा, जो कि हम जो चाहते हैं वह नहीं हो सकता है।

यह भी ध्यान दें कि यह प्रकाशक-विशिष्ट पुस्तकों को करने का बहुत सुरुचिपूर्ण तरीका नहीं है। यदि हम एक अन्य प्रकाशक पृष्ठ जोड़ना चाहते हैं, तो हमें URLconf में एक और मुट्ठी भर लाइनों की आवश्यकता होगी, और कुछ से अधिक प्रकाशकों को अनुचित मिलेगा। हम अगले भाग में इस समस्या से निपटेंगे।

ध्यान दें

यदि आपको अनुरोध करते समय /books/acme/ पाने के लिए 404 मिलता है, तो यह सुनिश्चित करने के लिए जांच करें कि आपके पास वास्तव में 'ACME प्रकाशन' नाम के साथ प्रकाशक है। सामान्य विचारों में इस मामले के लिए एक allow_empty पैरामीटर है। अधिक विवरण के लिए वर्ग-आधारित-विचार संदर्भ देखें।

गतिशील फ़िल्टरिंग

एक अन्य सामान्य आवश्यकता URL में कुछ कुंजी द्वारा सूची पृष्ठ में दी गई वस्तुओं को फ़िल्टर करना है। पहले हमने URLconf में प्रकाशक के नाम को हार्ड-कोडित किया था, लेकिन क्या होगा अगर हम कुछ मनमानी प्रकाशक द्वारा सभी पुस्तकों को प्रदर्शित करने वाला दृश्य लिखना चाहते हैं?

हाथ से, ListView में एक get_queryset() विधि है जिसे हम ओवरराइड कर सकते हैं। पहले, यह केवल queryset विशेषता का मान लौटा रहा है, लेकिन अब हम अधिक तर्क जोड़ सकते हैं।

इस काम को बनाने का मुख्य हिस्सा यह है कि जब क्लास-आधारित विचारों को बुलाया जाता है, तो विभिन्न उपयोगी चीजें self पर संग्रहीत होती self ; साथ ही अनुरोध ( self.request ) में self.request गई स्थिति ( self.args ) और नाम-आधारित ( self.kwargs ) तर्क शामिल हैं।

यहाँ, हमारे पास एक एकल कब्जा समूह वाला URLconf है:

# urls.py
from django.urls import path
from books.views import PublisherBookList

urlpatterns = [
    path('books/<publisher>/', PublisherBookList.as_view()),
]

इसके बाद, हम स्वयं PublisherBookList दृश्य लिखेंगे:

# views.py
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from books.models import Book, Publisher

class PublisherBookList(ListView):

    template_name = 'books/books_by_publisher.html'

    def get_queryset(self):
        self.publisher = get_object_or_404(Publisher, name=self.kwargs['publisher'])
        return Book.objects.filter(publisher=self.publisher)

जैसा कि आप देख सकते हैं, क्वेरी चयन में अधिक तर्क जोड़ना काफी आसान है; यदि हम चाहते थे, तो हम वर्तमान उपयोगकर्ता या अन्य अधिक जटिल तर्क का उपयोग करके फ़िल्टर करने के लिए self.request.user का उपयोग कर सकते हैं।

हम प्रकाशक को उसी समय संदर्भ में भी जोड़ सकते हैं, इसलिए हम इसका उपयोग टेम्पलेट में कर सकते हैं:

# ...

def get_context_data(self, **kwargs):
    # Call the base implementation first to get a context
    context = super().get_context_data(**kwargs)
    # Add in the publisher
    context['publisher'] = self.publisher
    return context

अतिरिक्त कार्य करना

अंतिम सामान्य पैटर्न जिसे हम देखेंगे, जेनेरिक दृश्य को कॉल करने से पहले या बाद में कुछ अतिरिक्त काम करना शामिल है।

कल्पना करें कि हमारे पास हमारे Author मॉडल पर एक last_accessed क्षेत्र था last_accessed उपयोग हम उस Author को देखने वाले अंतिम समय पर नज़र रखने के लिए कर रहे थे:

# models.py
from django.db import models

class Author(models.Model):
    salutation = models.CharField(max_length=10)
    name = models.CharField(max_length=200)
    email = models.EmailField()
    headshot = models.ImageField(upload_to='author_headshots')
    last_accessed = models.DateTimeField()

जेनेरिक DetailView क्लास, बेशक, इस क्षेत्र के बारे में कुछ भी नहीं जानता होगा, लेकिन एक बार फिर हम आसानी से उस क्षेत्र को अपडेट रखने के लिए एक कस्टम दृश्य लिख सकते हैं।

सबसे पहले, हमें URLconf में एक कस्टम दृश्य को इंगित करने के लिए एक लेखक विस्तार बिट जोड़ना होगा:

from django.urls import path
from books.views import AuthorDetailView

urlpatterns = [
    #...
    path('authors/<int:pk>/', AuthorDetailView.as_view(), name='author-detail'),
]

तब हम अपना नया दृश्य get_object - get_object वह विधि है जो ऑब्जेक्ट को पुनः प्राप्त करती है - इसलिए हम बस इसे ओवरराइड करते हैं और कॉल को लपेटते हैं:

from django.utils import timezone
from django.views.generic import DetailView
from books.models import Author

class AuthorDetailView(DetailView):

    queryset = Author.objects.all()

    def get_object(self):
        obj = super().get_object()
        # Record the last accessed date
        obj.last_accessed = timezone.now()
        obj.save()
        return obj

ध्यान दें

यहां URLconf नामित समूह pk का उपयोग करता है - यह नाम डिफ़ॉल्ट नाम है जो DetailView को फ़िल्टर करने के लिए उपयोग की जाने वाली प्राथमिक कुंजी का मान खोजने के लिए DetailView का उपयोग करता है।

यदि आप समूह को कुछ और कॉल करना चाहते हैं, तो आप दृश्य पर pk_url_kwarg सेट कर सकते हैं। विवरण के संदर्भ में अधिक जानकारी पाई जा सकती है