python - सूची में * हर * आइटम के लिए Django फ़िल्टर क्वेरीसेट __in



filter django-queryset (5)

अगर हम इसे गतिशील रूप से करना चाहते हैं, तो उदाहरण का पालन करें:

tag_ids = [t1.id, t2.id]
qs = Photo.objects.all()

for tag_id in tag_ids:
    qs = qs.filter(tag__id=tag_id)    

print qs

मान लें कि मेरे पास निम्नलिखित मॉडल हैं

class Photo(models.Model):
    tags = models.ManyToManyField(Tag)

class Tag(models.Model):
    name = models.CharField(max_length=50)

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

मैंने कोशिश की:

Photo.objects.filter(tags__name__in=categories)

लेकिन यह श्रेणियों में किसी भी आइटम से मेल खाता है, सभी आइटम नहीं।

तो यदि श्रेणियां ['छुट्टी', 'गर्मी'] होंगी, तो मैं फोटो को छुट्टियों और ग्रीष्मकालीन टैग दोनों के साथ चाहता हूं।

क्या यह हासिल किया जा सकता है?


एक और दृष्टिकोण जो काम करता है, हालांकि केवल PostgreSQL, django.contrib.postgres.fields.ArrayField का उपयोग कर django.contrib.postgres.fields.ArrayField :

उदाहरण docs से कॉपी किया गया:

>>> Post.objects.create(name='First post', tags=['thoughts', 'django'])
>>> Post.objects.create(name='Second post', tags=['thoughts'])
>>> Post.objects.create(name='Third post', tags=['tutorial', 'django'])

>>> Post.objects.filter(tags__contains=['thoughts'])
<QuerySet [<Post: First post>, <Post: Second post>]>

>>> Post.objects.filter(tags__contains=['django'])
<QuerySet [<Post: First post>, <Post: Third post>]>

>>> Post.objects.filter(tags__contains=['django', 'thoughts'])
<QuerySet [<Post: First post>]>

ArrayField में कुछ और शक्तिशाली विशेषताएं हैं जैसे overlap और इंडेक्स ट्रांसफॉर्म


यह Django ORM और कुछ पायथन जादू का उपयोग कर गतिशील क्वेरी पीढ़ी द्वारा भी किया जा सकता है :)

from operator import and_
from django.db.models import Q

categories = ['holiday', 'summer']
res = Photo.filter(reduce(and_, [Q(tags__name=c) for c in categories]))

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

res = Photo.filter(Q(tags__name='holiday') & Q(tags__name='summer'))

सारांश:

प्रत्येक विकल्प के लिए .filter() जोड़ने के लिए टिप्पणियों में jpic और sgallen द्वारा सुझाए गए अनुसार एक विकल्प है। प्रत्येक अतिरिक्त filter अधिक जुड़ता है, जो श्रेणियों के छोटे सेट के लिए कोई समस्या नहीं होनी चाहिए।

aggregation approach । यह क्वेरी श्रेणियों के एक बड़े समूह के लिए कम और शायद तेज होगी।

आपके पास कस्टम प्रश्नों का उपयोग करने का विकल्प भी है।

कुछ उदाहरण

परीक्षण व्यवस्था:

class Photo(models.Model):
    tags = models.ManyToManyField('Tag')

class Tag(models.Model):
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.name

In [2]: t1 = Tag.objects.create(name='holiday')
In [3]: t2 = Tag.objects.create(name='summer')
In [4]: p = Photo.objects.create()
In [5]: p.tags.add(t1)
In [6]: p.tags.add(t2)
In [7]: p.tags.all()
Out[7]: [<Tag: holiday>, <Tag: summer>]

docs.djangoproject.com/en/dev/topics/db/queries/… दृष्टिकोण का उपयोग करना:

In [8]: Photo.objects.filter(tags=t1).filter(tags=t2)
Out[8]: [<Photo: Photo object>]

परिणाम का परिणाम:

In [17]: print Photo.objects.filter(tags=t1).filter(tags=t2).query
SELECT "test_photo"."id"
FROM "test_photo"
INNER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id")
INNER JOIN "test_photo_tags" T4 ON ("test_photo"."id" = T4."photo_id")
WHERE ("test_photo_tags"."tag_id" = 3  AND T4."tag_id" = 4 )

ध्यान दें कि प्रत्येक filter क्वेरी में और JOINS जोड़ता है।

aggregation approach का उपयोग करना:

In [29]: from django.db.models import Count
In [30]: Photo.objects.filter(tags__in=[t1, t2]).annotate(num_tags=Count('tags')).filter(num_tags=2)
Out[30]: [<Photo: Photo object>]

परिणाम का परिणाम:

In [32]: print Photo.objects.filter(tags__in=[t1, t2]).annotate(num_tags=Count('tags')).filter(num_tags=2).query
SELECT "test_photo"."id", COUNT("test_photo_tags"."tag_id") AS "num_tags"
FROM "test_photo"
LEFT OUTER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id")
WHERE ("test_photo_tags"."tag_id" IN (3, 4))
GROUP BY "test_photo"."id", "test_photo"."id"
HAVING COUNT("test_photo_tags"."tag_id") = 2

AND एड Q ऑब्जेक्ट्स काम नहीं करेंगे:

In [9]: from django.db.models import Q
In [10]: Photo.objects.filter(Q(tags__name='holiday') & Q(tags__name='summer'))
Out[10]: []
In [11]: from operator import and_
In [12]: Photo.objects.filter(reduce(and_, [Q(tags__name='holiday'), Q(tags__name='summer')]))
Out[12]: []

परिणाम का परिणाम:

In [25]: print Photo.objects.filter(Q(tags__name='holiday') & Q(tags__name='summer')).query
SELECT "test_photo"."id"
FROM "test_photo"
INNER JOIN "test_photo_tags" ON ("test_photo"."id" = "test_photo_tags"."photo_id")
INNER JOIN "test_tag" ON ("test_photo_tags"."tag_id" = "test_tag"."id")
WHERE ("test_tag"."name" = holiday  AND "test_tag"."name" = summer )

>>> ["foo", "bar", "baz"].index("bar")
1

संदर्भ: डेटा संरचना> सूचियों पर अधिक

चेतावनी का पालन करें

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

>>> print(list.index.__doc__)
L.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.

सूची की लंबाई में रैखिक समय-जटिलता

एक index कॉल index हर तत्व को क्रम में जांचता है, जब तक कि यह एक मैच न मिल जाए। यदि आपकी सूची लंबी है, और आप यह नहीं जानते कि सूची में कहां होता है, तो यह खोज एक बाधा बन सकती है। उस स्थिति में, आपको एक अलग डेटा संरचना पर विचार करना चाहिए। ध्यान दें कि यदि आप मोटे तौर पर मैच को कहां पाते हैं, तो आप index को संकेत दे सकते हैं। उदाहरण के लिए, इस स्निपेट में, l.index(999_999, 999_990, 1_000_000) सीधे l.index(999_999) तुलना में तीव्रता के लगभग पांच ऑर्डर हैं, क्योंकि पूर्व को केवल 10 प्रविष्टियां l.index(999_999) , जबकि बाद में दस लाख खोजें:

>>> import timeit
>>> timeit.timeit('l.index(999_999)', setup='l = list(range(0, 1_000_000))', number=1000)
9.356267921015387
>>> timeit.timeit('l.index(999_999, 999_990, 1_000_000)', setup='l = list(range(0, 1_000_000))', number=1000)
0.0004404920036904514

केवल अपने तर्क के पहले मैच की अनुक्रमणिका लौटाता है

index को index माध्यम से सूची में कॉल करने तक कॉल करने के लिए एक कॉल मिलती है, और वहां रुक जाती है। यदि आपको अधिक मैचों के सूचकांक की आवश्यकता होने की उम्मीद है, तो आपको एक सूची समझ, या जनरेटर अभिव्यक्ति का उपयोग करना चाहिए।

>>> [1, 1].index(1)
0
>>> [i for i, e in enumerate([1, 2, 1]) if e == 1]
[0, 2]
>>> g = (i for i, e in enumerate([1, 2, 1]) if e == 1)
>>> next(g)
0
>>> next(g)
2

अधिकतर स्थान जहां मैंने एक बार index उपयोग किया होगा, अब मैं एक सूची समझ या जनरेटर अभिव्यक्ति का उपयोग करता हूं क्योंकि वे अधिक सामान्य हैं। तो यदि आप index तक पहुंचने पर विचार कर रहे हैं, तो इन उत्कृष्ट पायथन सुविधाओं पर नज़र डालें।

अगर तत्व सूची में मौजूद नहीं है तो फेंकता है

अगर आइटम मौजूद नहीं है तो index लिए एक ValueError में कॉल करें।

>>> [1, 1].index(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 2 is not in list

यदि आइटम सूची में मौजूद नहीं हो सकता है, तो आपको भी चाहिए

  1. item in my_list (साफ, पठनीय दृष्टिकोण) item in my_list साथ पहले इसकी जांच करें, या
  2. index कॉल को try/except ब्लॉक try/except जो ValueError पकड़ता है (शायद तेज़, कम से कम जब खोज की सूची लंबी होती है, और आइटम आमतौर पर मौजूद होता है।)




python django filter django-queryset