Django 2.1 - Query Expressions

क्वेरी एक्सप्रेशंस




django

क्वेरी एक्सप्रेशंस

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

समर्थित अंकगणित

Django पायथन स्थिरांक, चर, और यहां तक ​​कि अन्य अभिव्यक्तियों का उपयोग करते हुए नकारात्मक, जोड़, घटाव, गुणा, भाग, मोडुलो अंकगणित और क्वेरी अभिव्यक्ति पर पावर ऑपरेटर का समर्थन करता है।

Django 2.1 में परिवर्तित:

निषेध के लिए समर्थन जोड़ा गया था।

कुछ उदाहरण

from django.db.models import Count, F, Value
from django.db.models.functions import Length, Upper

# Find companies that have more employees than chairs.
Company.objects.filter(num_employees__gt=F('num_chairs'))

# Find companies that have at least twice as many employees
# as chairs. Both the querysets below are equivalent.
Company.objects.filter(num_employees__gt=F('num_chairs') * 2)
Company.objects.filter(
    num_employees__gt=F('num_chairs') + F('num_chairs'))

# How many chairs are needed for each company to seat all employees?
>>> company = Company.objects.filter(
...    num_employees__gt=F('num_chairs')).annotate(
...    chairs_needed=F('num_employees') - F('num_chairs')).first()
>>> company.num_employees
120
>>> company.num_chairs
50
>>> company.chairs_needed
70

# Create a new company using expressions.
>>> company = Company.objects.create(name='Google', ticker=Upper(Value('goog')))
# Be sure to refresh it if you need to access the field.
>>> company.refresh_from_db()
>>> company.ticker
'GOOG'

# Annotate models with an aggregated value. Both forms
# below are equivalent.
Company.objects.annotate(num_products=Count('products'))
Company.objects.annotate(num_products=Count(F('products')))

# Aggregates can contain complex computations also
Company.objects.annotate(num_offerings=Count(F('products') + F('services')))

# Expressions can also be used in order_by(), either directly
Company.objects.order_by(Length('name').asc())
Company.objects.order_by(Length('name').desc())
# or using the double underscore lookup syntax.
from django.db.models import CharField
from django.db.models.functions import Length
CharField.register_lookup(Length)
Company.objects.order_by('name__length')

बिल्ट-इन एक्सप्रेशंस

ध्यान दें

इन अभिव्यक्तियों को django.db.models.expressions और django.db.models.aggregates में परिभाषित किया गया है, लेकिन सुविधा के लिए वे उपलब्ध हैं और आमतौर पर django.db.models से आयात किए django.db.models

F() अभिव्यक्ति

class F [source]

एक F() ऑब्जेक्ट एक मॉडल फ़ील्ड या एनोटेट कॉलम के मूल्य का प्रतिनिधित्व करता है। यह वास्तव में उन्हें पायथन मेमोरी में डेटाबेस से बाहर खींचने के लिए बिना मॉडल फ़ील्ड मानों को संदर्भित करना और उनका उपयोग करके डेटाबेस संचालन करना संभव बनाता है।

इसके बजाय, Django एक SQL अभिव्यक्ति उत्पन्न करने के लिए F() ऑब्जेक्ट का उपयोग करता है जो डेटाबेस स्तर पर आवश्यक ऑपरेशन का वर्णन करता है।

एक उदाहरण के माध्यम से समझना सबसे आसान है। आम तौर पर, कोई ऐसा कुछ कर सकता है:

# Tintin filed a news story!
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1
reporter.save()

यहां, हमने डेटाबेस से reporter.stories_filed .stories_filed का मान मेमोरी में खींच लिया है और परिचित पायथन ऑपरेटरों का उपयोग करके इसे हेरफेर किया है, और फिर ऑब्जेक्ट को डेटाबेस में वापस सहेजा है। लेकिन इसके बजाय हम भी कर सकते थे:

from django.db.models import F

reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()

हालांकि reporter.stories_filed = F('stories_filed') + 1 एक उदाहरण विशेषता के लिए मूल्य के एक सामान्य पायथन असाइनमेंट की तरह दिखता है, वास्तव में यह एक SQL निर्माण है जो डेटाबेस पर एक ऑपरेशन का वर्णन करता है।

जब Django F() की एक आवृत्ति का सामना करता है F() , यह एक मानक SQL अभिव्यक्ति बनाने के लिए मानक पायथन ऑपरेटरों को ओवरराइड करता है; इस स्थिति में, वह जो डेटाबेस को reporter.stories_filed द्वारा दर्शाए गए डेटाबेस क्षेत्र को बढ़ाने के लिए निर्देश देता है।

जो भी मूल्य है या reporter.stories_filed .stories_filed पर reporter.stories_filed , पायथन को इसके बारे में कभी पता नहीं चलता है - यह पूरी तरह से डेटाबेस द्वारा निपटा जाता है। Django के F() वर्ग के माध्यम से सभी पायथन करता है, फ़ील्ड को संदर्भित करने और ऑपरेशन का वर्णन करने के लिए SQL सिंटैक्स बनाता है।

इस तरह से सहेजे गए नए मान तक पहुँचने के लिए, ऑब्जेक्ट को पुनः लोड किया जाना चाहिए:

reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()

ऊपर दिए गए एकल उदाहरणों के संचालन में उपयोग किए जाने के साथ-साथ, F() का उपयोग वस्तु उदाहरणों के QuerySets पर update() साथ किया जा सकता है। यह उन दो प्रश्नों को कम करता है जो हम ऊपर प्रयोग कर रहे थे - get() और save() - सिर्फ एक के लिए:

reporter = Reporters.objects.filter(name='Tintin')
reporter.update(stories_filed=F('stories_filed') + 1)

हम कई वस्तुओं पर क्षेत्र मूल्य बढ़ाने के लिए update() का भी उपयोग कर सकते हैं - जो कि डेटाबेस से उन सभी को पायथन में खींचने से बहुत तेज हो सकता है, उन पर लूपिंग, प्रत्येक के क्षेत्र मूल्य में वृद्धि, और प्रत्येक को वापस बचा सकता है। डेटाबेस:

Reporter.objects.all().update(stories_filed=F('stories_filed') + 1)

F() इसलिए प्रदर्शन लाभ की पेशकश कर सकते हैं:

  • काम करने के लिए अजगर के बजाय डेटाबेस प्राप्त करना
  • कुछ संचालन के लिए आवश्यक प्रश्नों की संख्या को कम करना

F() का उपयोग कर दौड़ की स्थितियों से बचना

F() का एक अन्य उपयोगी लाभ यह है कि डेटाबेस होने के बजाय - पायथन से - एक क्षेत्र के मूल्य को अपडेट करें एक दौड़ की स्थिति से बचा जाता है।

यदि दो पायथन धागे उपरोक्त उदाहरण में कोड निष्पादित करते हैं, तो एक धागा डेटाबेस से पुनर्प्राप्त करने के बाद एक फ़ील्ड के मान को पुनः प्राप्त कर सकता है, बढ़ा सकता है और बचा सकता है। दूसरा थ्रेड सहेजने वाला मान मूल मान पर आधारित होगा; पहले धागे का काम बस खो जाएगा।

यदि डेटाबेस फ़ील्ड को अपडेट करने के लिए ज़िम्मेदार है, तो प्रक्रिया अधिक मज़बूत है: यह केवल डेटाबेस में फ़ील्ड के मूल्य के आधार पर उस फ़ील्ड को अपडेट करेगा जब save() या update() को निष्पादित किया जाता है, बजाय उसके आधार पर जब उदाहरण पुनर्प्राप्त किया गया था तब मूल्य।

Model.save() बाद F() असाइनमेंट जारी Model.save()

मॉडल क्षेत्रों को सौंपी गई F() ऑब्जेक्ट मॉडल उदाहरण को सहेजने के बाद बनी रहती है और प्रत्येक save() पर लागू होगी। उदाहरण के लिए:

reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()

reporter.name = 'Tintin Jr.'
reporter.save()

stories_filed को इस मामले में दो बार अपडेट किया जाएगा। यदि यह शुरू में 1 , तो अंतिम मूल्य 3 होगा।

फिल्टर में F() का उपयोग करना

F() QuerySet फिल्टर में भी बहुत उपयोगी है, जहां वे पायथन मूल्यों के बजाय अपने क्षेत्र मूल्यों के आधार पर मापदंड के खिलाफ वस्तुओं के एक सेट को फ़िल्टर करना संभव बनाते हैं।

यह प्रश्नों में एफ () अभिव्यक्तियों का उपयोग करने में प्रलेखित है।

एनोटेशन के साथ F() का उपयोग करना

F() का उपयोग अंकगणित के साथ विभिन्न क्षेत्रों को जोड़कर अपने मॉडल पर गतिशील क्षेत्र बनाने के लिए किया जा सकता है:

company = Company.objects.annotate(
    chairs_needed=F('num_employees') - F('num_chairs'))

यदि आपके द्वारा संयोजित किए जा रहे फ़ील्ड विभिन्न प्रकार के हैं, तो आपको Django को बताना होगा कि किस प्रकार का फ़ील्ड वापस किया जाएगा। चूंकि F() सीधे output_field समर्थन नहीं करता है, output_field आपको अभिव्यक्ति output_field साथ ExpressionWrapper को लपेटने की आवश्यकता होगी:

from django.db.models import DateTimeField, ExpressionWrapper, F

Ticket.objects.annotate(
    expires=ExpressionWrapper(
        F('active_at') + F('duration'), output_field=DateTimeField()))

ForeignKey जैसे रिलेशनल फ़ील्ड्स का संदर्भ देते समय, F() मॉडल उदाहरण के बजाय प्राथमिक कुंजी मान लौटाता है:

>> car = Company.objects.annotate(built_by=F('manufacturer'))[0]
>> car.manufacturer
<Manufacturer: Toyota>
>> car.built_by
3

शून्य मानों को सॉर्ट करने के लिए F() का उपयोग करना

किसी फ़ील्ड के शून्य मानों के क्रम को नियंत्रित करने के लिए Expression.asc() या desc() लिए F() और nulls_first या nulls_last कीवर्ड तर्क का उपयोग करें। डिफ़ॉल्ट रूप से, ऑर्डर आपके डेटाबेस पर निर्भर करता है।

उदाहरण के लिए, जिन कंपनियों से संपर्क नहीं किया गया है last_contacted लिए ( last_contacted null है) उन कंपनियों से संपर्क करने के बाद:

from django.db.models import F
Company.object.order_by(F('last_contacted').desc(nulls_last=True))

Func() भाव

Func() भाव उन सभी अभिव्यक्तियों का आधार प्रकार है जिनमें डेटाबेस फ़ंक्शन जैसे COALESCE और LOWER , या SUM जैसे समुच्चय शामिल हैं। वे सीधे इस्तेमाल किया जा सकता है:

from django.db.models import F, Func

queryset.annotate(field_lower=Func(F('field'), function='LOWER'))

या उनका उपयोग डेटाबेस कार्यों की लाइब्रेरी बनाने के लिए किया जा सकता है:

class Lower(Func):
    function = 'LOWER'

queryset.annotate(field_lower=Lower('field'))

लेकिन दोनों ही मामलों में परिणाम होगा जहां प्रत्येक मॉडल को एक अतिरिक्त विशेषता field_lower साथ एनोटेट किया जाता है, जो मोटे तौर पर निम्न SQL से निर्मित होता है:

SELECT
    ...
    LOWER("db_table"."field") as "field_lower"

अंतर्निहित डेटाबेस फ़ंक्शन की सूची के लिए डेटाबेस फ़ंक्शंस देखें।

Func एपीआई इस प्रकार है:

class Func(*expressions, **extra) [source]
function

उत्पन्न होने वाले फ़ंक्शन का वर्णन करने वाला एक वर्ग गुण। विशेष रूप से, function को template भीतर function प्लेसहोल्डर के रूप में इंटरपोल किया जाएगा। किसी के लिए चूक।

template

प्रारूप स्ट्रिंग के रूप में एक वर्ग विशेषता, जो इस फ़ंक्शन के लिए उत्पन्न SQL का वर्णन करता है। '%(function)s(%(expressions)s)' लिए डिफ़ॉल्ट।

यदि आप एसक्यूएल का निर्माण कर रहे हैं जैसे strftime('%W', 'date') और क्वेरी में शाब्दिक % वर्ण की आवश्यकता है, तो इसे template विशेषता में ( %%%% ) चौगुना करें क्योंकि स्ट्रिंग दो बार प्रक्षेपित होती है: एक बार के दौरान as_sql() और एक बार SQL इंटरपोलेशन में टेम्पलेट पैरामीटर डेटाबेस कर्सर में क्वेरी मापदंडों के साथ।

arg_joiner

एक वर्ग विशेषता जो expressions की सूची में शामिल होने के लिए उपयोग किए जाने वाले चरित्र को दर्शाता है। ', ' लिए चूक।

arity

एक वर्ग विशेषता जो फ़ंक्शन को स्वीकार करने वाले तर्कों की संख्या को दर्शाता है। यदि यह विशेषता सेट है और फ़ंक्शन को अलग-अलग संख्याओं के साथ कॉल किया जाता है, तो TypeError को उठाया जाएगा। किसी के लिए चूक।

as_sql(compiler, connection, function=None, template=None, arg_joiner=None, **extra_context) [source]

डेटाबेस फ़ंक्शन के लिए SQL उत्पन्न करता है।

as_vendor() मेथड्स में function , template , arg_joiner और किसी अन्य **extra_context पैरामीटर्स का उपयोग SQL को आवश्यकतानुसार करने के लिए करना चाहिए। उदाहरण के लिए:

class ConcatPair(Func):
    ...
    function = 'CONCAT'
    ...

    def as_mysql(self, compiler, connection):
        return super().as_sql(
            compiler, connection,
            function='CONCAT_WS',
            template="%(function)s('', %(expressions)s)",
        )

SQL इंजेक्शन भेद्यता से बचने के लिए, extra_context में अविश्वासित उपयोगकर्ता इनपुट नहीं होना चाहिए क्योंकि ये मान क्वेरी पैरामीटर के रूप में पारित होने के बजाय SQL स्ट्रिंग में प्रक्षेपित होते हैं, जहां डेटाबेस ड्राइवर उनसे बच जाएगा।

*expressions तर्क स्थिति संबंधी अभिव्यक्तियों की एक सूची है जिसे फ़ंक्शन पर लागू किया जाएगा। अभिव्यक्तियों को स्ट्रिंग्स में परिवर्तित किया जाएगा, arg_joiner साथ एक साथ arg_joiner , और फिर expressions प्लेसहोल्डर के रूप में template में arg_joiner किया गया।

स्थिति संबंधी तर्क अभिव्यक्ति या पायथन मूल्य हो सकते हैं। स्ट्रिंग्स को कॉलम संदर्भ माना जाता है और इसे F() अभिव्यक्तियों में लपेटा जाएगा जबकि अन्य मान Value() एक्सप्रेशन में लिपटे होंगे।

**extra kwargs key=value जोड़े हैं जिन्हें template विशेषता में प्रक्षेपित किया जा सकता है। SQL इंजेक्शन भेद्यता से बचने के लिए, extra में अविशिष्ट उपयोगकर्ता इनपुट शामिल नहीं होना चाहिए क्योंकि ये मान क्वेरी पैरामीटर के रूप में पारित होने के बजाय SQL स्ट्रिंग में प्रक्षेपित होते हैं, जहां डेटाबेस ड्राइवर उनसे बच जाएगा।

function , template और arg_joiner कीवर्ड का उपयोग आपकी अपनी कक्षा को परिभाषित किए बिना उसी नाम की विशेषताओं को बदलने के लिए किया जा सकता है। output_field का उपयोग अपेक्षित रिटर्न प्रकार को परिभाषित करने के लिए किया जा सकता है।

Aggregate() भाव

एक एग्रीगेट एक्सप्रेशन एक फंक () एक्सप्रेशन का एक विशेष केस होता है, जो कि GROUP BY क्लॉज के लिए आवश्यक क्वेरी की सूचना देता है। Sum() और Count() , जैसे कुल कार्य , Aggregate() से विरासत में Aggregate()

चूंकि Aggregate अभिव्यक्ति और रैप एक्सप्रेशन हैं, आप कुछ जटिल संगणनाओं का प्रतिनिधित्व कर सकते हैं:

from django.db.models import Count

Company.objects.annotate(
    managers_required=(Count('num_employees') / 4) + Count('num_managers'))

Aggregate एपीआई इस प्रकार है:

class Aggregate(expression, output_field=None, filter=None, **extra) [source]
template

प्रारूप स्ट्रिंग के रूप में एक वर्ग विशेषता, जो इस एग्रीगेट के लिए उत्पन्न SQL का वर्णन करता है। '%(function)s( %(expressions)s )' लिए डिफ़ॉल्ट।

function

एक वर्ग विशेषता जो कुल फ़ंक्शन का वर्णन करेगी जो उत्पन्न होगी। विशेष रूप से, function को template भीतर function प्लेसहोल्डर के रूप में इंटरपोल किया जाएगा। किसी के लिए चूक।

window_compatible
Django 2.0 में नया:

Window में सोर्स एक्सप्रेशन के रूप में ज्यादातर एग्रेसिव फंक्शन्स का इस्तेमाल किया जा सकता है।

expression तर्क मॉडल पर एक फ़ील्ड का नाम हो सकता है, या कोई अन्य अभिव्यक्ति। यह एक स्ट्रिंग में परिवर्तित हो जाएगा और template भीतर expressions प्लेसहोल्डर के रूप में उपयोग किया जाएगा।

output_field तर्क के लिए एक मॉडल फ़ील्ड उदाहरण की आवश्यकता होती है, जैसे IntegerField() या BooleanField() , जिसमें डेटाबेस से पुनर्प्राप्त करने के बाद Django मान को लोड करेगा। आमतौर पर मॉडल के क्षेत्र को max_digits किसी भी तर्क की आवश्यकता नहीं होती है क्योंकि डेटा सत्यापन ( max_digits , max_digits , आदि) से संबंधित किसी भी तर्क को अभिव्यक्ति के आउटपुट मूल्य पर लागू नहीं किया जाएगा।

ध्यान दें कि output_field केवल तभी आवश्यक है जब Django यह निर्धारित करने में असमर्थ हो कि परिणाम किस प्रकार का होना चाहिए। फ़ील्ड प्रकारों को मिश्रित करने वाले जटिल भावों को वांछित output_field को परिभाषित करना चाहिए। उदाहरण के लिए, एक IntegerField() और एक FloatField() एक साथ output_field=FloatField() संभवतः output_field=FloatField() परिभाषित किया जाना चाहिए।

filter तर्क एक Q object लेता है जो कि एकत्रित पंक्तियों को फ़िल्टर करने के लिए उपयोग किया जाता है। उदाहरण उपयोग के लिए एनोटेशन पर सशर्त एकत्रीकरण और फ़िल्टरिंग देखें।

**extra kwargs key=value जोड़े हैं जिन्हें template विशेषता में प्रक्षेपित किया जा सकता है।

Django 2.0 में बदला:

filter तर्क जोड़ा गया था।

अपने खुद के अलग कार्य बनाना

अपना खुद का समुच्चय बनाना बेहद आसान है। कम से कम, आपको function को परिभाषित करने की आवश्यकता है, लेकिन आप उत्पन्न होने वाले SQL को पूरी तरह से अनुकूलित भी कर सकते हैं। यहाँ एक संक्षिप्त उदाहरण है:

from django.db.models import Aggregate

class Count(Aggregate):
    # supports COUNT(distinct field)
    function = 'COUNT'
    template = '%(function)s(%(distinct)s%(expressions)s)'

    def __init__(self, expression, distinct=False, **extra):
        super().__init__(
            expression,
            distinct='DISTINCT ' if distinct else '',
            output_field=IntegerField(),
            **extra
        )

Value() भाव

class Value(value, output_field=None) [source]

एक Value() ऑब्जेक्ट एक अभिव्यक्ति के सबसे छोटे संभव घटक का प्रतिनिधित्व करता है: एक साधारण मूल्य। जब आपको किसी अभिव्यक्ति के भीतर पूर्णांक, बूलियन या स्ट्रिंग के मूल्य का प्रतिनिधित्व करने की आवश्यकता होती है, तो आप उस मान को मान के भीतर लपेट सकते हैं Value()

आपको शायद ही कभी Value() का उपयोग करने की आवश्यकता होगी। जब आप अभिव्यक्ति F('field') + 1 लिखते हैं, तो Django स्पष्ट रूप से 1 को एक Value() में लपेटता है, जिससे सरल मानों को अधिक जटिल अभिव्यक्तियों में उपयोग किया जा सकता है। जब आप किसी स्ट्रिंग को किसी एक्सप्रेशन में पास करना चाहते हैं तो आपको Value() का उपयोग करना होगा। अधिकांश अभिव्यक्तियाँ एक स्ट्रिंग तर्क को एक क्षेत्र के नाम के रूप में व्याख्या करती हैं, जैसे Lower('name')

value तर्क अभिव्यक्ति में शामिल किए जाने वाले मूल्य का वर्णन करता है, जैसे कि 1 , True , या None । Django जानता है कि इन अजगर मूल्यों को अपने संबंधित डेटाबेस प्रकार में कैसे परिवर्तित किया जाए।

output_field तर्क को IntegerField() या BooleanField() तरह एक मॉडल फ़ील्ड उदाहरण होना चाहिए, जिसमें डेटाबेस से पुनर्प्राप्त करने के बाद Django मूल्य को लोड करेगा। आमतौर पर मॉडल के क्षेत्र को max_digits किसी भी तर्क की आवश्यकता नहीं होती है क्योंकि डेटा सत्यापन ( max_digits , max_digits , आदि) से संबंधित किसी भी तर्क को अभिव्यक्ति के आउटपुट मूल्य पर लागू नहीं किया जाएगा।

ExpressionWrapper() भाव

class ExpressionWrapper(expression, output_field) [source]

ExpressionWrapper बस एक और अभिव्यक्ति को output_field और output_field तक पहुँच प्रदान करता है, जैसे कि output_field , जो अन्य अभिव्यक्तियों पर उपलब्ध नहीं हो सकता है। ExpressionWrapper आवश्यक है जब एनोटेशन के साथ एफ () का उपयोग करते हुए विभिन्न प्रकार के साथ F() अभिव्यक्ति पर अंकगणित का उपयोग किया जाए।

सशर्त अभिव्यक्ति

सशर्त अभिव्यक्तियाँ आपको उपयोग करने की अनुमति देती हैं if ... क्वेरी में ... else तर्क। Django मूल रूप से SQL CASE अभिव्यक्तियों का समर्थन करता है। अधिक जानकारी के लिए सशर्त अभिव्यक्तियाँ देखें।

Subquery() भाव

class Subquery(queryset, output_field=None) [source]

आप सबक्वेरी QuerySet का उपयोग करते हुए एक QuerySet एक स्पष्ट QuerySet जोड़ सकते हैं।

उदाहरण के लिए, प्रत्येक पोस्ट को उस पोस्ट पर नवीनतम टिप्पणी के लेखक के ईमेल पते के साथ एनोटेट करें:

>>> from django.db.models import OuterRef, Subquery
>>> newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')
>>> Post.objects.annotate(newest_commenter_email=Subquery(newest.values('email')[:1]))

PostgreSQL पर, SQL जैसा दिखता है:

SELECT "post"."id", (
    SELECT U0."email"
    FROM "comment" U0
    WHERE U0."post_id" = ("post"."id")
    ORDER BY U0."created_at" DESC LIMIT 1
) AS "newest_commenter_email" FROM "post"

ध्यान दें

इस खंड के उदाहरणों को यह दिखाने के लिए डिज़ाइन किया गया है कि कैसे Django को एक सबक्वेरी निष्पादित करने के लिए मजबूर किया जाए। कुछ मामलों में समतुल्य क्वेरी को लिखना संभव हो सकता है जो समान कार्य को अधिक स्पष्ट या कुशलता से करता है।

बाहरी क्वेरी से स्तंभों को संदर्भित करना

class OuterRef(field) [source]

बाहरी क्वेरी से किसी फ़ील्ड को संदर्भित करने के लिए किसी OuterRef में एक OuterRef होने पर OuterRef उपयोग करें। यह एक F अभिव्यक्ति की तरह काम करता है सिवाय इसके कि चेक देखने के लिए कि क्या यह एक वैध फ़ील्ड को संदर्भित करता है जब तक कि बाहरी क्वेरीसेट हल न हो जाए।

OuterRef उदाहरणों का उपयोग OuterRef के नेस्टेड उदाहरणों के साथ संयोजन में किया जा सकता है OuterRef ऐसे OuterRef को संदर्भित किया जाए जो तत्काल अभिभावक नहीं है। उदाहरण के लिए, इस क्वेरी को सही ढंग से हल करने के लिए Subquery इंस्टेंस की एक नेस्टेड जोड़ी के भीतर होना चाहिए:

>>> Book.objects.filter(author=OuterRef(OuterRef('pk')))

एक स्तंभ के लिए एक उपश्रेणी को सीमित करना

ऐसे समय होते हैं जब एक कॉलम को एक __in से लौटाया जाना चाहिए, उदाहरण के लिए, एक __in का उपयोग करने के लिए __in लुकअप के लक्ष्य के रूप में। अंतिम दिन के भीतर प्रकाशित पदों के लिए सभी टिप्पणियाँ वापस करने के लिए:

>>> from datetime import timedelta
>>> from django.utils import timezone
>>> one_day_ago = timezone.now() - timedelta(days=1)
>>> posts = Post.objects.filter(published_at__gte=one_day_ago)
>>> Comment.objects.filter(post__in=Subquery(posts.values('pk')))

इस मामले में, उपश्रेणी को केवल एक कॉलम वापस करने के लिए values() उपयोग करना चाहिए: पोस्ट की प्राथमिक कुंजी।

उपकुंजी को एक पंक्ति में सीमित करना

एक उपश्रेणी को कई पंक्तियों को वापस करने से रोकने के लिए, क्वेरी के एक स्लाइस ( [:1] ) का उपयोग किया जाता है:

>>> subquery = Subquery(newest.values('email')[:1])
>>> Post.objects.annotate(newest_commenter_email=subquery)

इस मामले में, उपश्रेणी को केवल एक कॉलम और एक पंक्ति में लौटना चाहिए: सबसे हाल ही में बनाई गई टिप्पणी का ईमेल पता।

(स्लाइस के बजाय get() का उपयोग करना विफल हो जाएगा क्योंकि OuterRef को तब तक हल नहीं किया जा सकता है जब तक कि OuterRef के भीतर OuterRef उपयोग नहीं किया जाता है।)

Exists() उपश्रेणियाँ

class Exists(queryset) [source]

Exists एक Subquery सबक्लास है जो SQL EXISTS स्टेटमेंट का उपयोग करता है। कई मामलों में यह एक सबक्वेरी से बेहतर प्रदर्शन करेगा क्योंकि डेटाबेस पहले मैचिंग रो पाए जाने पर सबक्वेरी के मूल्यांकन को रोकने में सक्षम होता है।

उदाहरण के लिए, प्रत्येक पोस्ट को अंतिम दिन के भीतर टिप्पणी करना है या नहीं, इसके बारे में टिप्पणी करने के लिए:

>>> from django.db.models import Exists, OuterRef
>>> from datetime import timedelta
>>> from django.utils import timezone
>>> one_day_ago = timezone.now() - timedelta(days=1)
>>> recent_comments = Comment.objects.filter(
...     post=OuterRef('pk'),
...     created_at__gte=one_day_ago,
... )
>>> Post.objects.annotate(recent_comment=Exists(recent_comments))

PostgreSQL पर, SQL जैसा दिखता है:

SELECT "post"."id", "post"."published_at", EXISTS(
    SELECT U0."id", U0."post_id", U0."email", U0."created_at"
    FROM "comment" U0
    WHERE (
        U0."created_at" >= YYYY-MM-DD HH:MM:SS AND
        U0."post_id" = ("post"."id")
    )
) AS "recent_comment" FROM "post"

किसी स्तंभ को संदर्भित करने के लिए Exists को बाध्य करना अनावश्यक है, क्योंकि कॉलम को छोड़ दिया जाता है और बूलियन परिणाम वापस किया जाता है। इसी तरह, ऑर्डर करने के बाद से SQL EXISTS सबक्वेरी में महत्वहीन है और केवल प्रदर्शन को नीचा EXISTS , यह स्वचालित रूप से हटा दिया जाता है।

आप ~Exists() साथ NOT EXISTS का उपयोग करके क्वेरी कर सकते हैं।

एक Subquery अभिव्यक्ति पर फ़िल्टरिंग

यह संभव नहीं है कि सीधे Subquery और Subquery का उपयोग करके फ़िल्टर करें, जैसे:

>>> Post.objects.filter(Exists(recent_comments))
...
TypeError: 'Exists' object is not iterable

पहले क्वेरी को एनोटेट करके और फिर उस एनोटेशन के आधार पर फ़िल्टर करके आपको एक सबक्वेरी एक्सप्रेशन पर फ़िल्टर करना होगा:

>>> Post.objects.annotate(
...     recent_comment=Exists(recent_comments),
... ).filter(recent_comment=True)

एक Subquery अभिव्यक्ति के भीतर समुच्चय का उपयोग करना

Subquery उपयोग एक Subquery भीतर किया जा सकता है, लेकिन उन्हें उप-समूह को सही करने के लिए filter() , values() और annotate() विशिष्ट संयोजन की आवश्यकता होती है।

मान लें कि दोनों मॉडल में एक length फ़ील्ड है, ऐसे पोस्ट खोजने के लिए जहां पोस्ट की लंबाई सभी संयुक्त टिप्पणियों की कुल लंबाई से अधिक है:

>>> from django.db.models import OuterRef, Subquery, Sum
>>> comments = Comment.objects.filter(post=OuterRef('pk')).order_by().values('post')
>>> total_comments = comments.annotate(total=Sum('length')).values('total')
>>> Post.objects.filter(length__gt=Subquery(total_comments))

आरंभिक filter(...) प्रासंगिक मानकों के अधीनता को सीमित करता है। order_by() Comment मॉडल पर डिफ़ॉल्ट ordering (यदि कोई हो) को हटा देता है। values('post') Post द्वारा टिप्पणियों को एकत्र करता है। अंत में, annotate(...) एकत्रीकरण करता है। इन क्वेरी विधियों को लागू करने का क्रम महत्वपूर्ण है। इस मामले में, चूंकि उपश्रेणी एक एकल स्तंभ तक सीमित होनी चाहिए, इसलिए values('total') की आवश्यकता होती है।

यह Subquery भीतर एकत्रीकरण करने का एकमात्र तरीका है, क्योंकि aggregate() का उपयोग करके OuterRef का मूल्यांकन करने का प्रयास किया जाता है (और अगर कोई OuterRef , तो इसे हल करना संभव नहीं होगा)।

कच्चे एसक्यूएल भाव

class RawSQL(sql, params, output_field=None) [source]

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

>>> from django.db.models.expressions import RawSQL
>>> queryset.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))

ये अतिरिक्त लुकअप भिन्न डेटाबेस इंजन के लिए पोर्टेबल नहीं हो सकते हैं (क्योंकि आप स्पष्ट रूप से SQL कोड लिख रहे हैं) और DRY सिद्धांत का उल्लंघन करते हैं, इसलिए यदि संभव हो तो आपको इनसे बचना चाहिए।

चेतावनी

SQL इंजेक्शन हमलों से बचाने के लिए, आपको किसी भी पैरामीटर से बचना चाहिए जिसे उपयोगकर्ता params का उपयोग करके नियंत्रित कर सकता है। params एक आवश्यक तर्क है जो आपको यह स्वीकार करने के लिए बाध्य करता है कि आप अपने SQL को उपयोगकर्ता द्वारा प्रदान किए गए डेटा के साथ प्रक्षेपित नहीं कर रहे हैं।

आपको SQL स्ट्रिंग में प्लेसहोल्डर्स को भी उद्धृत नहीं करना चाहिए। %s के उद्धरणों के कारण यह उदाहरण SQL इंजेक्शन के लिए असुरक्षित है:

RawSQL("select col from sometable where othercol = '%s'")  # unsafe!

आप और अधिक पढ़ सकते हैं कि Django की SQL इंजेक्शन सुरक्षा कैसे काम करती है।

विंडो फ़ंक्शन

Django 2.0 में नया:

विंडो फ़ंक्शंस विभाजन पर फ़ंक्शंस लागू करने का एक तरीका प्रदान करते हैं। एक सामान्य एकत्रीकरण फ़ंक्शन के विपरीत, जो समूह द्वारा परिभाषित प्रत्येक सेट के लिए अंतिम परिणाम की गणना करता है, विंडो फ़ंक्शंस frames और विभाजन पर काम करते हैं, और प्रत्येक पंक्ति के लिए परिणाम की गणना करते हैं।

आप एक ही क्वेरी में कई विंडो निर्दिष्ट कर सकते हैं जो कि Django ORM में एक QuerySet.annotate() कॉल में कई अभिव्यक्तियों को शामिल करने के बराबर होगी। ओआरएम नामित खिड़कियों का उपयोग नहीं करता है, इसके बजाय वे चयनित कॉलम का हिस्सा हैं।

class Window(expression, partition_by=None, order_by=None, frame=None, output_field=None) [source]
filterable

False करने के लिए चूक। SQL मानक WHERE संदर्भित करता है विंडो कार्य को क्लॉज में और Django एक अपवाद पैदा करता है जब एक QuerySet निर्माण होता है जो ऐसा करेगा।

template

%(expression)s OVER (%(window)s)' लिए डिफ़ॉल्ट %(expression)s OVER (%(window)s)' । यदि केवल expression तर्क प्रदान किया जाता है, तो विंडो क्लॉज रिक्त होगा।

Window क्लास OVER क्लॉज के लिए मुख्य अभिव्यक्ति है।

expression तर्क या तो एक विंडो फ़ंक्शन , एक कुल फ़ंक्शन या एक अभिव्यक्ति है जो एक विंडो क्लॉज में संगत है।

partition_by तर्क अभिव्यक्तियों की एक सूची है (कॉलम नामों को एक F -ोबिज में लपेटा जाना चाहिए) जो पंक्तियों के विभाजन को नियंत्रित करते हैं। पार्टिशनिंग नैरो, परिणाम सेट की गणना करने के लिए किन पंक्तियों का उपयोग करता है।

output_field को एक तर्क के रूप में या अभिव्यक्ति द्वारा निर्दिष्ट किया जाता है।

order_by तर्क अभिव्यक्ति के एक क्रम को स्वीकार करता है, जिस पर आप Expression.asc() और desc() कह सकते हैं। आदेश उस क्रम को नियंत्रित करता है जिसमें अभिव्यक्ति लागू होती है। उदाहरण के लिए, यदि आप एक विभाजन में पंक्तियों पर योग करते हैं, तो पहला परिणाम सिर्फ पहली पंक्ति का मूल्य है, दूसरा पहली और दूसरी पंक्ति का योग है।

frame पैरामीटर निर्दिष्ट करता है कि गणना में किन अन्य पंक्तियों का उपयोग किया जाना चाहिए। विवरण के लिए frames देखें।

उदाहरण के लिए, एक ही शैली और रिलीज़ वर्ष में एक ही स्टूडियो द्वारा फिल्मों के लिए औसत रेटिंग के साथ प्रत्येक फिल्म को एनोटेट करना:

>>> from django.db.models import Avg, F, Window
>>> from django.db.models.functions import ExtractYear
>>> Movie.objects.annotate(
>>>     avg_rating=Window(
>>>         expression=Avg('rating'),
>>>         partition_by=[F('studio'), F('genre')],
>>>         order_by=ExtractYear('released').asc(),
>>>     ),
>>> )

इससे यह जांचना आसान हो जाता है कि कोई फिल्म अपने साथियों से बेहतर या खराब है।

आप एक ही विंडो यानी एक ही पार्टीशन और फ्रेम पर कई एक्सप्रेशन लागू करना चाहते हैं। उदाहरण के लिए, आप पिछले उदाहरण को संशोधित कर सकते हैं ताकि एक ही क्वेरी में तीन विंडो फ़ंक्शंस का उपयोग करके प्रत्येक फिल्म के समूह (एक ही स्टूडियो, शैली, और रिलीज़ वर्ष) में सर्वश्रेष्ठ और सबसे खराब रेटिंग शामिल कर सकें। पिछले उदाहरण से विभाजन और आदेश को पुनरावृत्ति को कम करने के लिए एक शब्दकोश में निकाला जाता है:

>>> from django.db.models import Avg, F, Max, Min, Window
>>> from django.db.models.functions import ExtractYear
>>> window = {
>>>    'partition_by': [F('studio'), F('genre')],
>>>    'order_by': ExtractYear('released').asc(),
>>> }
>>> Movie.objects.annotate(
>>>     avg_rating=Window(
>>>         expression=Avg('rating'), **window,
>>>     ),
>>>     best=Window(
>>>         expression=Max('rating'), **window,
>>>     ),
>>>     worst=Window(
>>>         expression=Min('rating'), **window,
>>>     ),
>>> )

Django के बिल्ट-इन डेटाबेस बैकएंड्स में, MySQL 8.0.2+, PostgreSQL और Oracle समर्थन विंडो एक्सप्रेशन हैं। विभिन्न डेटाबेस के बीच अलग-अलग विंडो अभिव्यक्ति सुविधाओं के लिए समर्थन भिन्न होता है। उदाहरण के लिए, Expression.asc() और desc() विकल्पों का समर्थन नहीं किया जा सकता है। आवश्यकतानुसार अपने डेटाबेस के लिए प्रलेखन से परामर्श करें।

फ्रेम्स

एक खिड़की के फ्रेम के लिए, आप पंक्तियों की एक श्रेणी-आधारित अनुक्रम या पंक्तियों का एक साधारण अनुक्रम चुन सकते हैं।

class ValueRange(start=None, end=None) [source]
frame_type

यह विशेषता 'RANGE' सेट है।

PostgreSQL के पास ValueRange लिए सीमित समर्थन है और केवल मानक प्रारंभ और अंत बिंदुओं के उपयोग का समर्थन करता है, जैसे कि CURRENT ROW और UNBOUNDED FOLLOWING

class RowRange(start=None, end=None) [source]
frame_type

यह विशेषता 'ROWS' सेट है।

दोनों वर्ग टेम्पलेट के साथ एसक्यूएल लौटाते हैं:

%(frame_type)s BETWEEN %(start)s AND %(end)s

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

किसी फ़्रेम के लिए डिफ़ॉल्ट शुरुआती बिंदु UNBOUNDED PRECEDING जो विभाजन की पहली पंक्ति है। अंत बिंदु को हमेशा ORM द्वारा उत्पन्न SQL में स्पष्ट रूप से शामिल किया गया है और डिफ़ॉल्ट रूप से UNBOUNDED FOLLOWING द्वारा किया गया है। डिफ़ॉल्ट फ्रेम में विभाजन से लेकर अंतिम पंक्ति तक की सभी पंक्तियाँ शामिल होती हैं।

start और end तर्क के लिए स्वीकृत मान None , पूर्णांक या शून्य है। N preceding में परिणाम start लिए एक नकारात्मक पूर्णांक, जबकि None UNBOUNDED PRECEDING उपज None UNBOUNDED PRECEDINGstart और end दोनों के start , शून्य CURRENT ROW लौटाएगा। सकारात्मक पूर्णांक end लिए स्वीकार किए जाते हैं।

CURRENT ROW में क्या अंतर है, इसमें अंतर है। जब ROWS मोड में निर्दिष्ट किया जाता है, तो फ्रेम वर्तमान पंक्ति के साथ शुरू या समाप्त होता है। RANGE मोड में निर्दिष्ट होने पर, फ्रेम ऑर्डरिंग क्लॉज के अनुसार पहले या अंतिम पीयर पर शुरू या समाप्त होता है। इस प्रकार, RANGE CURRENT ROW उन पंक्तियों के लिए अभिव्यक्ति का मूल्यांकन करता है, जिनके पास ऑर्डरिंग द्वारा निर्दिष्ट समान मूल्य है। क्योंकि टेम्पलेट में start और end दोनों बिंदु शामिल हैं, यह इसके साथ व्यक्त किया जा सकता है:

ValueRange(start=0, end=0)

यदि एक फिल्म के "साथियों" को उसी वर्ष एक ही शैली में एक ही स्टूडियो द्वारा रिलीज़ की गई फिल्मों के रूप में वर्णित किया जाता है, तो यह RowRange उदाहरण प्रत्येक फिल्म को मूवी की दो पूर्व और दो निम्न साथियों की औसत रेटिंग के साथ प्रदर्शित करता है:

>>> from django.db.models import Avg, F, RowRange, Window
>>> from django.db.models.functions import ExtractYear
>>> Movie.objects.annotate(
>>>     avg_rating=Window(
>>>         expression=Avg('rating'),
>>>         partition_by=[F('studio'), F('genre')],
>>>         order_by=ExtractYear('released').asc(),
>>>         frame=RowRange(start=-2, end=2),
>>>     ),
>>> )

यदि डेटाबेस इसका समर्थन करता है, तो आप विभाजन में एक अभिव्यक्ति के मूल्यों के आधार पर प्रारंभ और अंत बिंदु निर्दिष्ट कर सकते हैं। यदि Movie मॉडल का released फ़ील्ड प्रत्येक फिल्मों के रिलीज़ महीने को संग्रहीत करता है, तो यह ValueRange उदाहरण प्रत्येक मूवी के बाद बारह महीने पहले और बारह महीने के बीच रिलीज़ की गई मूवी के साथियों की औसत रेटिंग के साथ प्रत्येक मूवी को एनोटेट करता है।

>>> from django.db.models import Avg, ExpressionList, F, ValueRange, Window
>>> Movie.objects.annotate(
>>>     avg_rating=Window(
>>>         expression=Avg('rating'),
>>>         partition_by=[F('studio'), F('genre')],
>>>         order_by=F('released').asc(),
>>>         frame=ValueRange(start=-12, end=12),
>>>     ),
>>> )

तकनीकी जानकारी

नीचे आपको तकनीकी कार्यान्वयन विवरण मिलेगा जो पुस्तकालय लेखकों के लिए उपयोगी हो सकता है। तकनीकी एपीआई और नीचे दिए गए उदाहरण जेनेरिक क्वेरी एक्सप्रेशन बनाने में मदद करेंगे जो कि Django द्वारा प्रदान की गई कार्यक्षमता को बढ़ा सकते हैं।

अभिव्यक्ति एपीआई

क्वेरी अभिव्यक्तियाँ क्वेरी एक्सप्रेशन API को कार्यान्वित करती हैं, लेकिन नीचे सूचीबद्ध कई अतिरिक्त विधियों और विशेषताओं को भी उजागर करती हैं। सभी क्वेरी एक्सप्रेशन Expression() या एक प्रासंगिक उपवर्ग से प्राप्त होने चाहिए।

जब कोई क्वेरी एक्सप्रेशन किसी अन्य एक्सप्रेशन को लपेटता है, तो वह लिपटे एक्सप्रेशन पर उपयुक्त तरीकों को कॉल करने के लिए जिम्मेदार है।

class Expression [source]
contains_aggregate

Django बताता है कि इस अभिव्यक्ति में एक समुच्चय है और यह कि GROUP BY खंड को क्वेरी में जोड़ा जाना चाहिए।

contains_over_clause
Django 2.0 में नया:

Django बताता है कि इस अभिव्यक्ति में एक Window अभिव्यक्ति है। इसका उपयोग, उदाहरण के लिए, डेटा को संशोधित करने वाले प्रश्नों में विंडो फ़ंक्शन अभिव्यक्तियों को हटाने के लिए किया जाता है।

filterable
Django 2.0 में नया:

Django बताता है कि इस अभिव्यक्ति को filter() में संदर्भित किया जा सकता है। True अवहेलना।

window_compatible
Django 2.0 में नया:

Django बताता है कि इस अभिव्यक्ति का उपयोग Window में स्रोत अभिव्यक्ति के रूप में किया जा सकता है। False करने के लिए चूक।

resolve_expression(query=None, allow_joins=True, reuse=None, summarize=False, for_save=False)

क्वेरी में जोड़े जाने से पहले अभिव्यक्ति के किसी भी पूर्व-प्रसंस्करण या सत्यापन को करने का मौका प्रदान करता है। किसी भी नेस्टेड एक्सप्रेशंस पर resolve_expression() भी कहा जाना चाहिए। self copy() एक copy() किसी भी आवश्यक परिवर्तनों के साथ वापस आनी चाहिए।

query बैकएंड क्वेरी कार्यान्वयन है।

allow_joins एक बूलियन है जो क्वेरी में joins के उपयोग की अनुमति देता है या इनकार करता है।

reuse बहु-जुड़ने वाले परिदृश्यों के लिए पुन: प्रयोज्य योगों का एक समूह है।

summarize एक बूलियन है, जब True , संकेत करता है कि गणना की जा रही क्वेरी एक टर्मिनल कुल क्वेरी है।

get_source_expressions()

आंतरिक भावों की एक क्रमबद्ध सूची देता है। उदाहरण के लिए:

>>> Sum(F('foo')).get_source_expressions()
[F('foo')]
set_source_expressions(expressions)

अभिव्यक्तियों की एक सूची लेता है और उन्हें ऐसे संग्रहीत करता है जो get_source_expressions() उन्हें वापस कर सकते हैं।

relabeled_clone(change_map)

का कोई क्लोन (प्रतिलिपि) लौटाता है self , जिसमें किसी भी स्तंभ के अलाब रिलेबल होते हैं। जब उप-वर्ग बनाए जाते हैं, तो स्तंभ उपनाम बदल दिया जाता है। relabeled_clone() किसी भी नेस्टेड अभिव्यक्तियों पर भी कॉल किया जाना चाहिए और क्लोन को सौंपा जाना चाहिए।

change_map नए उपनामों के लिए पुराने उपनामों की मैपिंग एक शब्दकोश है।

उदाहरण:

def relabeled_clone(self, change_map):
    clone = copy.copy(self)
    clone.expression = self.expression.relabeled_clone(change_map)
    return clone
convert_value(value, expression, connection)

एक हुक value अधिक उपयुक्त प्रकार में अभिव्यक्ति की अनुमति देता है ।

get_group_by_cols()

इस अभिव्यक्ति द्वारा कॉलम संदर्भों की सूची को वापस करने के लिए जिम्मेदार। get_group_by_cols() किसी भी नेस्टेड अभिव्यक्ति पर बुलाया जाना चाहिए। F() ऑब्जेक्ट्स, विशेष रूप से, एक कॉलम का एक संदर्भ रखते हैं।

asc(nulls_first=False, nulls_last=False)

आरोही क्रम में क्रमबद्ध होने के लिए तैयार अभिव्यक्ति लौटाता है।

nulls_first और nulls_last परिभाषित करें कि शून्य मान कैसे क्रमबद्ध हैं। उदाहरण उपयोग के लिए अशक्त मानों को सॉर्ट करने के लिए F () का उपयोग करना देखें ।

desc(nulls_first=False, nulls_last=False)

अवरोही क्रम में क्रमबद्ध होने के लिए तैयार अभिव्यक्ति लौटाता है।

nulls_first और nulls_last परिभाषित करें कि शून्य मान कैसे क्रमबद्ध हैं। उदाहरण उपयोग के लिए अशक्त मानों को सॉर्ट करने के लिए F () का उपयोग करना देखें ।

reverse_ordering()

रिटर्न self एक के भीतर सॉर्ट क्रम को उल्टा करने के लिए आवश्यक कोई भी संशोधन के साथ order_by कॉल। एक उदाहरण के रूप में, एक अभिव्यक्ति को लागू करने के NULLS LAST लिए अपने मूल्य बदल जाएगा NULLS FIRST । संशोधन केवल उन भावों के लिए आवश्यक हैं जो क्रमबद्ध क्रम को लागू करते हैं OrderBy । जब reverse() क्वेरीसेट पर कॉल किया जाता है तो यह विधि कहलाती है।

अपनी खुद की क्वेरी अभिव्यक्ति लिखना

आप अपनी स्वयं की क्वेरी अभिव्यक्ति कक्षाओं का उपयोग कर सकते हैं, और अन्य क्वेरी अभिव्यक्तियों के साथ एकीकृत कर सकते हैं। COALESCE अंतर्निहित फंक () अभिव्यक्तियों का उपयोग किए बिना, SQL फ़ंक्शन के कार्यान्वयन को लिखकर एक उदाहरण के माध्यम से चलो ।

COALESCE एसक्यूएल समारोह कॉलम या मानों की सूची लेने के रूप में परिभाषित किया गया है। यह पहला कॉलम या मान लौटाएगा जो नहीं है NULL

हम SQL पीढ़ी और __init__() कुछ विशेषताओं को सेट करने के लिए एक विधि के लिए उपयोग किए जाने वाले टेम्पलेट को परिभाषित करके शुरू करेंगे :

import copy
from django.db.models import Expression

class Coalesce(Expression):
    template = 'COALESCE( %(expressions)s )'

    def __init__(self, expressions, output_field):
      super().__init__(output_field=output_field)
      if len(expressions) < 2:
          raise ValueError('expressions must have at least 2 elements')
      for expression in expressions:
          if not hasattr(expression, 'resolve_expression'):
              raise TypeError('%r is not an Expression' % expression)
      self.expressions = expressions

हम मापदंडों पर कुछ बुनियादी सत्यापन करते हैं, जिसमें कम से कम 2 कॉलम या मूल्यों की आवश्यकता होती है, और यह सुनिश्चित करना कि वे अभिव्यक्ति हैं। हम output_field यहाँ आवश्यकता है ताकि Django जानता है कि किस प्रकार के मॉडल क्षेत्र को अंतिम परिणाम प्रदान करना है।

अब हम पूर्व-प्रसंस्करण और सत्यापन को लागू करते हैं। चूँकि इस बिंदु पर हमारी अपनी मान्यता नहीं है, हम सिर्फ नेस्टेड अभिव्यक्तियों को सौंपते हैं:

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
    c = self.copy()
    c.is_summary = summarize
    for pos, expression in enumerate(self.expressions):
        c.expressions[pos] = expression.resolve_expression(query, allow_joins, reuse, summarize, for_save)
    return c

अगला, हम SQL बनाने के लिए जिम्मेदार विधि लिखते हैं:

def as_sql(self, compiler, connection, template=None):
    sql_expressions, sql_params = [], []
    for expression in self.expressions:
        sql, params = compiler.compile(expression)
        sql_expressions.append(sql)
        sql_params.extend(params)
    template = template or self.template
    data = {'expressions': ','.join(sql_expressions)}
    return template % data, params

def as_oracle(self, compiler, connection):
    """
    Example of vendor specific handling (Oracle in this case).
    Let's make the function name lowercase.
    """
    return self.as_sql(compiler, connection, template='coalesce( %(expressions)s )')

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

हम विधि expressions का उपयोग करके प्रत्येक के लिए SQL उत्पन्न करते हैं compiler.compile() , और परिणाम को अल्पविराम से एक साथ जोड़ते हैं। फिर टेम्पलेट हमारे डेटा से भर जाता है और एसक्यूएल और पैरामीटर वापस आ जाते हैं।

हमने एक कस्टम कार्यान्वयन भी परिभाषित किया है जो Oracle बैकएंड के लिए विशिष्ट है। as_oracle() समारोह के बजाय कहा जाएगा as_sql() अगर ओरेकल बैकएंड उपयोग में है।

अंत में, हम बाकी विधियों को लागू करते हैं जो हमारी क्वेरी अभिव्यक्ति को अन्य क्वेरी अभिव्यक्तियों के साथ अच्छा खेलने की अनुमति देते हैं:

def get_source_expressions(self):
    return self.expressions

def set_source_expressions(self, expressions):
    self.expressions = expressions

आइए देखें कि यह कैसे काम करता है:

>>> from django.db.models import F, Value, CharField
>>> qs = Company.objects.annotate(
...    tagline=Coalesce([
...        F('motto'),
...        F('ticker_name'),
...        F('description'),
...        Value('No Tagline')
...        ], output_field=CharField()))
>>> for c in qs:
...     print("%s: %s" % (c.name, c.tagline))
...
Google: Do No Evil
Apple: AAPL
Yahoo: Internet Company
Django Software Foundation: No Tagline

एसक्यूएल इंजेक्शन से परहेज

के बाद से एक Func के लिए की कीवर्ड तर्क __init__() ( **extra ) और as_sql() ( **extra_context ) के बजाय क्वेरी पैरामीटर (जहां डेटाबेस ड्राइवर उन्हें बचने के हैं) के रूप में पारित कर दिया एसक्यूएल स्ट्रिंग में अंतर्वेशित रहे हैं, वे अविश्वसनीय उपयोगकर्ता इनपुट नहीं होनी चाहिए।

उदाहरण के लिए, यदि substring उपयोगकर्ता प्रदान किया गया है, तो यह फ़ंक्शन SQL इंजेक्शन के लिए असुरक्षित है:

from django.db.models import Func

class Position(Func):
    function = 'POSITION'
    template = "%(function)s('%(substring)s' in %(expressions)s)"

    def __init__(self, expression, substring):
        # substring=substring is a SQL injection vulnerability!
        super().__init__(expression, substring=substring)

यह फ़ंक्शन बिना किसी पैरामीटर के SQL स्ट्रिंग उत्पन्न करता है। चूंकि substring इसे super().__init__() एक कीवर्ड तर्क के रूप में पारित किया गया है, इसलिए इसे डेटाबेस को भेजे जाने से पहले SQL स्ट्रिंग में इंटरपोल किया गया है।

यहाँ एक सही लिखा गया है:

class Position(Func):
    function = 'POSITION'
    arg_joiner = ' IN '

    def __init__(self, expression, substring):
        super().__init__(substring, expression)

इसके substring बजाय एक स्थानिक तर्क के रूप में पारित होने के बाद, इसे डेटाबेस क्वेरी में एक पैरामीटर के रूप में पारित किया जाएगा।

तृतीय-पक्ष डेटाबेस बैकेंड में समर्थन जोड़ना

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

मान लीजिए कि हम जो एसक्यूएल का उपयोग करता है माइक्रोसॉफ्ट के एसक्यूएल सर्वर के लिए एक बैकएंड लिख रहे हैं LEN के बजाय LENGTH के लिए Length कार्य करते हैं। हम बंदर as_sqlserver() को Length क्लास में बुलाए गए एक नए तरीके से पैच करेंगे :

from django.db.models.functions import Length

def sqlserver_length(self, compiler, connection):
    return self.as_sql(compiler, connection, function='LEN')

Length.as_sqlserver = sqlserver_length

के template पैरामीटर का उपयोग करके आप SQL को कस्टमाइज़ भी कर सकते हैं as_sql()

हम का उपयोग करें as_sqlserver() क्योंकि django.db.connection.vendor रिटर्न sqlserver बैकएंड के लिए।

थर्ड-पार्टी बैकएंड __init__.py बैकएंड पैकेज के शीर्ष स्तर expressions.py फ़ाइल में या शीर्ष स्तर से आयात किए गए शीर्ष स्तर फ़ाइल (या पैकेज) में अपने कार्यों को पंजीकृत कर सकते हैं __init__.py

उपयोगकर्ता परियोजनाओं के लिए बैकएंड को पैच करना चाहते हैं जो वे उपयोग कर रहे हैं, इस कोड को एक AppConfig.ready() विधि में रहना चाहिए ।

Original text