2017-05-03 20 views
8

के साथ सरल सबक्वायरी मैं एक बहुत ही सरल सबक्वायरी बनाने की कोशिश कर रहा हूं जो OuterRef का उपयोग करता है (व्यावहारिक उद्देश्यों के लिए नहीं, केवल इसे काम करने के लिए), लेकिन एक ही त्रुटि में चलते रहें।OuterRef

पदों/models.py

from django.db import models 

class Tag(models.Model): 
    name = models.CharField(max_length=120) 
    def __str__(self): 
     return self.name 

class Post(models.Model): 
    title = models.CharField(max_length=120) 
    tags = models.ManyToManyField(Tag) 
    def __str__(self): 
     return self.title 

manage.py खोल कोड

>>> from django.db.models import OuterRef, Subquery 
>>> from posts.models import Tag, Post 
>>> tag1 = Tag.objects.create(name='tag1') 
>>> post1 = Post.objects.create(title='post1') 
>>> post1.tags.add(tag1) 
>>> Tag.objects.filter(post=post1.pk) 
<QuerySet [<Tag: tag1>]> 
>>> tags_list = Tag.objects.filter(post=OuterRef('pk')) 
>>> Post.objects.annotate(count=Subquery(tags_list.count())) 

अंतिम दो पंक्तियों मुझे प्रत्येक पोस्ट वस्तु के लिए टैग की संख्या देना चाहिए। और यहाँ मैं एक ही त्रुटि प्राप्त हो रही:

ValueError: This queryset contains a reference to an outer query and may only be used in a subquery. 

उत्तर

17

अपने उदाहरण के साथ समस्याओं में से एक यह है कि आप एक सबक्वेरी के रूप में queryset.count() उपयोग नहीं कर सकते, क्योंकि .count() की कोशिश करता क्वेरीसमूह का मूल्यांकन करने और गिनती वापस जाने के लिए है।

तो कोई व्यक्ति सोच सकता है कि सही दृष्टिकोण Count() का उपयोग करना होगा। इस तरह हो सकता है कि कुछ: दो कारणों के लिए

Post.objects.annotate(
    count=Count(Tag.objects.filter(post=OuterRef('pk'))) 
) 

यह अभ्यस्त काम:

  1. Tag क्वेरीसमूह, सभी Tag क्षेत्रों का चयन करता है, जबकि Count केवल एक ही मैदान पर भरोसा कर सकते हैं। इस प्रकार: Tag.objects.filter(post=OuterRef('pk')).only('pk') की आवश्यकता है (tag.pk पर गिनती का चयन करने के लिए)।

  2. Count ही नहीं एक Subquery वर्ग, Count एक Aggregate है। तो Count द्वारा उत्पन्न अभिव्यक्ति को Subquery के रूप में पहचाना नहीं गया है, हम Subquery का उपयोग कर इसे ठीक कर सकते हैं।

और अंतिम संस्करण होगा:

Post.objects.annotate(
    count=Count(Subquery(Tag.objects.filter(post=OuterRef('pk')).only('pk'))) 
) 

हालांकि अगर आप का निरीक्षण क्वेरी उत्पादन किया जा रहा

SELECT 
    "tests_post"."id", 
    "tests_post"."title", 
    COUNT((SELECT U0."id" 
      FROM "tests_tag" U0 
      INNER JOIN "tests_post_tags" U1 ON (U0."id" = U1."tag_id") 
      WHERE U1."post_id" = ("tests_post"."id")) 
    ) AS "count" 
FROM "tests_post" 
GROUP BY 
    "tests_post"."id", 
    "tests_post"."title" 

आप देख सकते हैं कि हम एक GROUP BY खंड की है। ऐसा इसलिए है क्योंकि गणना कुल है, अभी यह परिणाम को प्रभावित नहीं करती है, लेकिन कुछ अन्य मामलों में यह हो सकती है। यही कारण है कि docs थोड़ा अलग दृष्टिकोण है, जहां एकत्रीकरण values + annotate + values

Post.objects.annotate(
    count=Subquery(
     Tag.objects.filter(post=OuterRef('pk')) 
      .values('post') 
      .annotate(count=Count('pk')) 
      .values('count') 
    ) 
) 

की एक विशिष्ट संयोजन के माध्यम से subquery में ले जाया जाता सुझाव है अंत में यह होगा उत्पादन:

SELECT 
    "tests_post"."id", 
    "tests_post"."title", 
    (SELECT COUNT(U0."id") AS "count" 
      FROM "tests_tag" U0 
      INNER JOIN "tests_post_tags" U1 ON (U0."id" = U1."tag_id") 
      WHERE U1."post_id" = ("tests_post"."id") 
      GROUP BY U1."post_id" 
    ) AS "count" 
FROM "tests_post" 
+0

धन्यवाद, कि काम किया! हालांकि, जब मैं टैग फ़िल्टर में 'pk__in = [1,2] 'जोड़ता हूं, तो मुझे' django.core.exceptions.FieldError मिलता है: अभिव्यक्ति में मिश्रित प्रकार होते हैं। आपको output_field' सेट करना होगा। – mjuk

+1

आप 'queryset.query' को प्रिंट करने का प्रयास कर सकते हैं और इसे वापस देखने के लिए सीधे अपने' आरडीबीएमएस' में निष्पादित कर सकते हैं। मुझे लगता है कि कुछ पंक्तियों के लिए 'गणना' 0 के बजाय 'NULL' वापस कर सकती है। आप यह पुष्टि करने का प्रयास कर सकते हैं कि पंक्तियों को अस्थायी रूप से बहिष्कृत करें, यानी '.filter (count__gte = 1)'।हालांकि, 'सबक्वायरी' एक दूसरा तर्क स्वीकार करता है, जो 'output_field' है, आप इसे सेट करने का प्रयास कर सकते हैं: 'output_field = fields.IntegerField()' – Todor

+0

धन्यवाद, यह मुझे बहुत जरूरी है। – mjuk

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^