2010-03-28 17 views
9

मैं अपनी वेबसाइट पर हर जगह Django Paginator का उपयोग कर रहा हूं और इसे और अधिक सुविधाजनक बनाने के लिए एक विशेष टेम्पलेट टैग भी लिखा है। लेकिन अब मुझे एक राज्य मिला, जहां मुझे एक जटिल कस्टम कच्चे एसक्यूएल क्वेरी बनाने की जरूरत है, कि LIMIT के बिना लगभग 100 के रिकॉर्ड वापस आ जाएंगे।Django: Paginator + कच्चे एसक्यूएल क्वेरी

मैं कस्टम क्वेरी के साथ Django Pagintor का उपयोग कैसे कर सकता हूं?

मेरी समस्या का

सरलीकृत उदाहरण:

मेरे मॉडल:

class PersonManager(models.Manager): 

    def complicated_list(self): 

     from django.db import connection 

     #Real query is much more complex   
     cursor.execute("""SELECT * FROM `myapp_person`"""); 

     result_list = [] 

     for row in cursor.fetchall(): 
      result_list.append(row[0]); 

     return result_list 


class Person(models.Model): 
    name  = models.CharField(max_length=255); 
    surname = models.CharField(max_length=255);  
    age  = models.IntegerField(); 

    objects = PersonManager(); 

तरह से मैं Django ORM साथ pagintation का उपयोग करें:

all_objects = Person.objects.all(); 

paginator = Paginator(all_objects, 10); 

try: 
    page = int(request.GET.get('page', '1')) 
except ValueError: 
    page = 1 

try: 
    persons = paginator.page(page) 
except (EmptyPage, InvalidPage): 
    persons = paginator.page(paginator.num_pages) 

इस तरह, Django, बहुत होशियार हो और इसे निष्पादित करते समय LIMIT क्वेरी में जोड़ता है। लेकिन जब मैं कस्टम प्रबंधक का उपयोग करें:

all_objects = Person.objects.complicated_list(); 

सभी डेटा चयनित है, और उसके बाद ही अजगर सूची कटा हुआ है, जो बहुत धीमी है। मैं अपना कस्टम मैनेजर कैसे बना सकता हूं जैसा कि एक में बनाया गया है?

+0

पायथन में, जब भी आप अपनी व्यक्तिगत कक्षा में चाहें तो आपको व्हाइटस्पेस का उपयोग नहीं करना चाहिए। – Gereltod

उत्तर

8

विशेष रूप से पेजिनेटर के स्रोत कोड, page() function पर देखकर, मुझे लगता है कि यह आपकी तरफ slicing लागू करने का एकमात्र मामला है, और एसक्यूएल क्वेरी में प्रासंगिक LIMIT क्लॉज में इसका अनुवाद करना है। तुम भी कुछ कैशिंग जोड़ने के लिए तो हो सकता है आप क्या कर सकते हैं कुछ और की जरूरत हो सकती है, लेकिन इस क्वेरीसमूह तरह देखने के लिए शुरू होता है,:

  • आप देखें MyView [आपकी क्वेरी] के रूप में बनाने का उपयोग कर डेटाबेस दृश्य बना सकते हैं;
  • उस दृश्य के लिए Django मॉडल जोड़ने के लिए, साथ Meta: managed=False
  • उपयोग अपने क्वेरीसमूहों टुकड़ा करने की क्रिया सहित किसी भी अन्य मॉडल, की तरह मॉडल है कि - इसका मतलब यह paginator साथ प्रयोग करने के लिए पूरी तरह से उपयुक्त है

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

+0

वाह, यह अच्छा है :) उत्तर के लिए धन्यवाद, लेकिन मुझे लगता है कि मैं गलत दिशाओं में देख रहा हूं। मैंने एक और सवाल पूछा है: http://stackoverflow.com/questions/2532686/django-getting-the-list-of-related-records-for-a-list-of-objects Guiess कस्टम प्रबंधक नहीं हैं यहाँ सबसे अच्छी बात है। –

2

मैं Django 1.1 के बारे में पता नहीं है, लेकिन यदि आप 1.2 के लिए इंतजार कर सकते हैं (जो कि लंबे समय से अब और नहीं होना चाहिए) आप this article में और development documentation में वर्णित के रूप objects.raw() का उपयोग कर सकते।

अन्यथा, यदि आप क्वेरी बहुत जटिल नहीं हैं, तो शायद extra clause का उपयोग करना पर्याप्त है।

+0

एक उपयोगी टिप के लिए धन्यवाद। लेकिन मुझे लगता है कि यह मेरी स्थिति में मदद नहीं करता –

+0

आप अभी भी कच्चे प्रश्न परिणामों के लिए गिनती नहीं कर सकते हैं। ऐसा लगता है कि आपको वास्तव में पेजिनेटर के साथ काम करने के लिए सूची (object.raw()) करना है। उस जानकारी के लिए http://stackoverflow.com/questions/2317452/django-count-rawqueryset के लिए धन्यवाद। – Josh

1

एक RawPaginator वर्ग कि Paginator ओवरराइड करता है कच्चे प्रश्नों के साथ काम करने मैंने बनाया है। इसमें एक अतिरिक्त तर्क होता है, count, जो आपकी क्वेरी की कुल संख्या है। यह object_list टुकड़ा नहीं करता है क्योंकि आपको OFFSET और LIMIT के माध्यम से अपनी कच्ची क्वेरी में अंकन करना होगा।

from django.core.paginator import Paginator 

class RawPaginator(Paginator): 
    def __init__(self, object_list, per_page, count, **kwargs): 
     super().__init__(object_list, per_page, **kwargs) 
     self.raw_count = count 

    def _get_count(self): 
     return self.raw_count 
    count = property(_get_count) 

    def page(self, number): 
     number = self.validate_number(number) 
     return self._get_page(self.object_list, number, self) 
+0

बहुत अच्छा समाधान। धन्यवाद। –

+0

जब आप नई पेजिनेटर क्लास को कॉल करते हैं, तो तर्क के रूप में दिए जाने पर 'मान' क्या होगा? –

0

मैं भी एक PaginatedRawQuerySet जो मैंने लिखा प्लग करने के लिए (कृपया एक अल्फा संस्करण के रूप में इस पर विचार करें) चाहता था। यह एक कच्चे क्वेरीसेट के लिए slicing क्षमता जोड़ता है। कृपया to this answer देखें - जिसे मैंने एक और प्रश्न के लिए एक समान प्रश्न के साथ लिखा - यह समझने के लिए कि यह कैसे काम करता है (विशेष रूप से "अंत में सावधानी का एक शब्द" खंड)।

from django.db import models 
from django.db.models import sql 
from django.db.models.query import RawQuerySet 


class PaginatedRawQuerySet(RawQuerySet): 
    def __init__(self, raw_query, **kwargs): 
     super(PaginatedRawQuerySet, self).__init__(raw_query, **kwargs) 
     self.original_raw_query = raw_query 
     self._result_cache = None 

    def __getitem__(self, k): 
     """ 
     Retrieves an item or slice from the set of results. 
     """ 
     if not isinstance(k, (slice, int,)): 
      raise TypeError 
     assert ((not isinstance(k, slice) and (k >= 0)) or 
       (isinstance(k, slice) and (k.start is None or k.start >= 0) and 
       (k.stop is None or k.stop >= 0))), \ 
      "Negative indexing is not supported." 

     if self._result_cache is not None: 
      return self._result_cache[k] 

     if isinstance(k, slice): 
      qs = self._clone() 
      if k.start is not None: 
       start = int(k.start) 
      else: 
       start = None 
      if k.stop is not None: 
       stop = int(k.stop) 
      else: 
       stop = None 
      qs.set_limits(start, stop) 
      return qs 

     qs = self._clone() 
     qs.set_limits(k, k + 1) 
     return list(qs)[0] 

    def __iter__(self): 
     self._fetch_all() 
     return iter(self._result_cache) 

    def count(self): 
     if self._result_cache is not None: 
      return len(self._result_cache) 

     return self.model.objects.count() 

    def set_limits(self, start, stop): 
     limit_offset = '' 

     new_params = tuple() 
     if start is None: 
      start = 0 
     elif start > 0: 
      new_params += (start,) 
      limit_offset = ' OFFSET %s' 
     if stop is not None: 
      new_params = (stop - start,) + new_params 
      limit_offset = 'LIMIT %s' + limit_offset 

     self.params = self.params + new_params 
     self.raw_query = self.original_raw_query + limit_offset 
     self.query = sql.RawQuery(sql=self.raw_query, using=self.db, params=self.params) 

    def _fetch_all(self): 
     if self._result_cache is None: 
      self._result_cache = list(super().__iter__()) 

    def __repr__(self): 
     return '<%s: %s>' % (self.__class__.__name__, self.model.__name__) 

    def __len__(self): 
     self._fetch_all() 
     return len(self._result_cache) 

    def _clone(self): 
     clone = self.__class__(raw_query=self.raw_query, model=self.model, using=self._db, hints=self._hints, 
           query=self.query, params=self.params, translations=self.translations) 
     return clone 
संबंधित मुद्दे