Django में कई फिल्टर () का पीछा करते हुए, यह एक बग है?


103

मैंने हमेशा यह माना कि Django में कई फ़िल्टर () कॉल का पीछा करना हमेशा एक ही कॉल में उन्हें इकट्ठा करने के समान था।

# Equivalent
Model.objects.filter(foo=1).filter(bar=2)
Model.objects.filter(foo=1,bar=2)

लेकिन मैं अपने कोड में एक जटिल क्वेरीसेट में चला गया, जहां यह मामला नहीं है

class Inventory(models.Model):
    book = models.ForeignKey(Book)

class Profile(models.Model):
    user = models.OneToOneField(auth.models.User)
    vacation = models.BooleanField()
    country = models.CharField(max_length=30)

# Not Equivalent!
Book.objects.filter(inventory__user__profile__vacation=False).filter(inventory__user__profile__country='BR')
Book.objects.filter(inventory__user__profile__vacation=False, inventory__user__profile__country='BR')

उत्पन्न SQL है

SELECT "library_book"."id", "library_book"."asin", "library_book"."added", "library_book"."updated" FROM "library_book" INNER JOIN "library_inventory" ON ("library_book"."id" = "library_inventory"."book_id") INNER JOIN "auth_user" ON ("library_inventory"."user_id" = "auth_user"."id") INNER JOIN "library_profile" ON ("auth_user"."id" = "library_profile"."user_id") INNER JOIN "library_inventory" T5 ON ("library_book"."id" = T5."book_id") INNER JOIN "auth_user" T6 ON (T5."user_id" = T6."id") INNER JOIN "library_profile" T7 ON (T6."id" = T7."user_id") WHERE ("library_profile"."vacation" = False  AND T7."country" = BR )
SELECT "library_book"."id", "library_book"."asin", "library_book"."added", "library_book"."updated" FROM "library_book" INNER JOIN "library_inventory" ON ("library_book"."id" = "library_inventory"."book_id") INNER JOIN "auth_user" ON ("library_inventory"."user_id" = "auth_user"."id") INNER JOIN "library_profile" ON ("auth_user"."id" = "library_profile"."user_id") WHERE ("library_profile"."vacation" = False  AND "library_profile"."country" = BR )

जंजीर filter()कॉल के साथ पहली क्वेरी इन्वेंटरी मॉडल को दो स्थितियों के बीच प्रभावी रूप से दो बार एक ओर बनाने में शामिल होती है, जबकि दूसरी क्वेरीसेट एंड्स दो स्थितियों को एक साथ जोड़ती है। मैं उम्मीद कर रहा था कि पहली क्वेरी भी होगी और दो स्थितियाँ भी। क्या यह अपेक्षित व्यवहार है या यह Django में एक बग है?

संबंधित प्रश्न का उत्तर क्या Django में ".filter () फ़िल्टर () फ़िल्टर () ..." का उपयोग करने के लिए एक नकारात्मक पहलू है? संकेत दिया गया है कि दो क्वेरी समतुल्य होनी चाहिए।

जवाबों:


117

जिस तरह से मैं यह समझता हूं कि वे डिजाइन द्वारा अलग-अलग हैं (और मैं निश्चित रूप से सुधार के लिए खुला हूं): filter(A, B)पहले ए के अनुसार फिल्टर करेगा और फिर बी के अनुसार सबफिल्टर, जबकि filter(A).filter(B)ए और 'संभावित' से मेल खाने वाली एक पंक्ति लौटाएगा। पंक्ति जो बी से मेल खाती है।

यहाँ उदाहरण देखें:

https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships

विशेष रूप से:

एक फिल्टर () कॉल के अंदर सब कुछ उन सभी आवश्यकताओं को पूरा करने वाले आइटम को फ़िल्टर करने के लिए एक साथ लागू किया जाता है। क्रमिक फ़िल्टर () कॉल आगे वस्तुओं के सेट को प्रतिबंधित करते हैं

...

इस दूसरे उदाहरण में (फिल्टर (ए) .फिल्टर (बी)), पहले फ़िल्टर ने क्वेरीसेट को (ए) तक सीमित कर दिया था। दूसरे फ़िल्टर ने ब्लॉग के सेट को उन लोगों के लिए और सीमित कर दिया जो कि (बी) भी हैं। दूसरी फ़िल्टर द्वारा चुनी गई प्रविष्टियाँ प्रथम फ़िल्टर में प्रविष्टियों के समान हो सकती हैं या नहीं भी हो सकती हैं। `


18
यह व्यवहार, हालांकि प्रलेखित है, कम से कम विस्मय के सिद्धांत का उल्लंघन लगता है। एक से अधिक फ़िल्टर () और एक साथ जब फ़ील्ड एक ही मॉडल पर होते हैं, लेकिन तब या साथ में जब रिश्तों को फैलाते हैं।
गर्डमब

3
मेरा मानना ​​है कि आपके पास पहले पैराग्राफ में गलत तरीका है - फ़िल्टर (ए, बी) AND स्थिति ('लेनन' और 2008 डॉक्स में) है, जबकि फ़िल्टर (ए) .filter (बी) OR स्थिति है ( 'लेनन' या 2008)। जब आप प्रश्न में उत्पन्न प्रश्नों को देखते हैं तो यह समझ में आता है - .filter (A) .filter (B) केस दो बार जुड़ता है, जिसके परिणामस्वरूप OR हो जाता है।
सैम

17
फ़िल्टर (A, B) AND फ़िल्टर है (A) .filter (B) OR है
WeizhongTu

3
तो further restrictमतलब है less restrictive?
बोह

7
यह उत्तर गलत है। यह "या" नहीं है। यह वाक्य "दूसरे फ़िल्टर ने ब्लॉग के सेट को उन लोगों के लिए प्रतिबंधित कर दिया है जो (बी) भी हैं।" स्पष्ट रूप से उल्लेख है कि "भी (बी) हैं।" यदि आप इस विशिष्ट उदाहरण में OR के समान व्यवहार का निरीक्षण करते हैं, तो इसका मतलब यह नहीं है कि आप अपनी व्याख्या को सामान्य कर सकते हैं। कृपया "केविन 3112" और "जॉनी त्सांग" के उत्तर देखें। मेरा मानना ​​है कि वे सही उत्तर हैं।
1

66

फ़िल्टरिंग की ये दो शैली ज्यादातर मामलों में बराबर हैं, लेकिन जब फॉरेनकेई या कईटोमनीफिल्ड पर वस्तुओं के आधार पर क्वेरी की जाती है, तो वे थोड़ा अलग होते हैं।

प्रलेखन से उदाहरण ।

मॉडल
ब्लॉग टू एंट्री एक से कई संबंध हैं।

from django.db import models

class Blog(models.Model):
    ...

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    pub_date = models.DateField()
    ...

ऑब्जेक्ट्स
मानते हैं कि यहां कुछ ब्लॉग और एंट्री ऑब्जेक्ट हैं।
यहां छवि विवरण दर्ज करें

प्रश्नों

Blog.objects.filter(entry__headline_contains='Lennon', 
    entry__pub_date__year=2008)
Blog.objects.filter(entry__headline_contains='Lennon').filter(
    entry__pub_date__year=2008)  

पहली क्वेरी (एकल फ़िल्टर एक) के लिए, यह केवल ब्लॉग 1 से मेल खाता है।

दूसरी क्वेरी के लिए (एक फ़िल्टर किए गए फ़िल्टर), यह ब्लॉग 1 और ब्लॉग 2 को फ़िल्टर करता है।
पहला फ़िल्टर ब्लॉग 1, ब्लॉग 2 और ब्लॉग 5 के लिए क्वेरी को प्रतिबंधित करता है; दूसरा फ़िल्टर ब्लॉग 1 और ब्लॉग 2 के आगे ब्लॉग के सेट को प्रतिबंधित करता है।

और आपको इसका एहसास होना चाहिए

हम प्रत्येक फ़िल्टर स्टेटमेंट के साथ ब्लॉग आइटम फ़िल्टर कर रहे हैं, एंट्री आइटम नहीं।

इसलिए, यह समान नहीं है, क्योंकि ब्लॉग और एंट्री बहु-मूल्यवान रिश्ते हैं।

संदर्भ: https://docs.djangoproject.com/en/1.8/topics/db/queries/#spanning-multi-valued-relationships
अगर कुछ गड़बड़ है, तो कृपया मुझे सुधारें।

संपादित करें: 1.6 लिंक अब उपलब्ध नहीं हैं, क्योंकि v1.6 से v1.8 बदल गया है।


3
आप "मैच" और "फ़िल्टर आउट" के बीच मिश्रित होने लगते हैं। यदि आप "यह क्वेरी रिटर्न" करने के लिए अटक गए हैं, तो यह बहुत स्पष्ट होगा।
ऑरेंजडॉग

7

जैसा कि आप उत्पन्न SQL कथनों में देख सकते हैं कि अंतर "OR" नहीं है क्योंकि कुछ पर संदेह हो सकता है। यह कैसे WHERE और JOIN रखा गया है।

उदाहरण 1 (एक ही तालिका में शामिल):

(उदाहरण https://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships से )

Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)

यह आपको उन सभी ब्लॉगों को देगा जिसमें दोनों के साथ एक प्रविष्टि है (entry_ headline _contains = 'Lennon') और (entry__pub_date__year = 2008), जो आप इस क्वेरी से उम्मीद करेंगे। परिणाम: {entry.headline: 'लाइफ ऑफ़ लिनन' के साथ बुक करें। entry.pub_date: '2008'}

उदाहरण 2 (जंजीर)

Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

यह उदाहरण 1 से सभी परिणामों को कवर करेगा, लेकिन यह थोड़ा और परिणाम उत्पन्न करेगा। क्योंकि यह पहले सभी ब्लॉगों को (प्रविष्टि_ शीर्षक _contains = 'लेनन') और फिर परिणाम फ़िल्टर (प्रविष्टि__pub_date__year = 2008) से फ़िल्टर करता है।

अंतर यह है कि यह आपको परिणाम भी देगा जैसे: Book with {entry.headline: ' Lennon ', entry.pub_date: 2000}, {entry.headline: 'Bill', entry.pub_date: 2008 }

आपके मामले में

मुझे लगता है कि यह वही है जिसकी आपको आवश्यकता है:

Book.objects.filter(inventory__user__profile__vacation=False, inventory__user__profile__country='BR')

और यदि आप उपयोग करना चाहते हैं या कृपया पढ़ना चाहते हैं: https://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects


दूसरा उदाहरण वास्तव में सच नहीं है। सभी जंजीरों को फ़िल्टर किए गए ऑब्जेक्ट्स पर लागू किया जाता है, अर्थात वे क्वेरी में एक साथ एंडेड होते हैं।
Janne

मेरा मानना ​​है कि उदाहरण 2 सही है, और यह वास्तव में संदर्भित के रूप में आधिकारिक Django दस्तावेजों से लिया गया स्पष्टीकरण है। मैं सबसे अच्छा व्याख्याकार नहीं हो सकता और मैं उसके लिए क्षमा चाहता हूं। उदाहरण 1 एक प्रत्यक्ष है और जैसा कि आप सामान्य एसक्यूएल लेखन में उम्मीद करेंगे। उदाहरण 1 कुछ इस तरह से देता है: '' ब्लॉग का चयन करें प्रविष्टि प्रविष्टि करें, जहां प्रविष्टि। हेड_लाइन " लेनन " और प्रविष्टि.यियर == 2008 का उदाहरण 2 कुछ इस तरह से दिया गया हो: 'ब्लॉग का चयन करें प्रविष्टि में प्रवेश करें जहां प्रवेश करें। हेडर सूची " लेनन " यूनिअन का चयन करें ब्लॉग जॉइन की प्रविष्टि WHERE entry.head_list " Lennon " को पसंद करती है
जॉनी त्सांग

सर, आप काफी सही हैं। जल्दबाजी में मैंने इस तथ्य को याद किया कि हमारे फ़िल्टरिंग मानदंड एक-से-कई संबंधों की ओर इशारा कर रहे हैं, न कि ब्लॉग के लिए।
Janne

0

कभी-कभी आप इस तरह से एक साथ कई फ़िल्टर नहीं जोड़ना चाहते:

def your_dynamic_query_generator(self, event: Event):
    qs \
    .filter(shiftregistrations__event=event) \
    .filter(shiftregistrations__shifts=False)

और निम्न कोड वास्तव में सही चीज़ नहीं लौटाएगा।

def your_dynamic_query_generator(self, event: Event):
    return Q(shiftregistrations__event=event) & Q(shiftregistrations__shifts=False)

अब आप जो भी कर सकते हैं वह एनोटेशन काउंट-फिल्टर का उपयोग करना है।

इस मामले में हम सभी बदलावों को गिनते हैं जो एक निश्चित घटना से संबंधित है।

qs: EventQuerySet = qs.annotate(
    num_shifts=Count('shiftregistrations__shifts', filter=Q(shiftregistrations__event=event))
)

बाद में आप एनोटेशन द्वारा फ़िल्टर कर सकते हैं।

def your_dynamic_query_generator(self):
    return Q(num_shifts=0)

यह समाधान बड़े क्वेरीसेट पर भी सस्ता है।

उम्मीद है की यह मदद करेगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.