python
  • django
  • django-models
  • django-database
  • sql-optimization
  • 2017-01-06 1 views -1 likes 
    -1

    다른 제품 통계가있는 큰 페이지에서 작업하고 있습니다. 여러 차트, 표 등을 사용합니다. 예를 들어 페이지를로드하는 데 5 초가 걸립니다. 각 행에 대해 그래서페이지로드가 매우 느립니다. 제대로 최적화하는 방법은 무엇입니까?

    last_scan_time = tables.columns.TemplateColumn("""{{ record.get_last_scan.datetime }}""",accessor='get_last_scan.datetime', verbose_name='Last scan time') 
    

    , 쿼리가 실행해야합니다 :

    이 편집 예를 들어

    OccurencesTable이 행을 포함

    매우 단순화 된 models.py입니다 마지막 스캔을 호출 한 데이터베이스로 더 나은 방법은 모든 Scan 개체를 미리로드하는 것입니다.

    PRODUCT

    class Product(models.Model): 
        user = models.ForeignKey(User, null=False, blank=False, related_name='products') 
        name = models.CharField(max_length=200) 
    
        def get_occurences(self): 
         return self.occurences.prefetch_related("scans__price__currency") 
    
        def get_occurences_count(self): 
         return self.occurences.all().count() 
    
        def get_all_scans(self): 
         return [item for sublist in [x.scans.all() for x in self.get_occurences()] for item in sublist] 
    
        def get_all_valid_scans(self): 
         return [item for sublist in [x.get_valid_scans() for x in self.get_occurences()] for item in sublist] 
    
        def get_last_scans(self): 
         scans = [] 
         for occ in self.get_occurences(): 
          scan = occ.get_last_scan() 
          if scan: 
           scans.append(scan) 
         return scans 
    
        # @property 
        #def last_scan_time(self): 
         #scans = sorted([x for x in self.get_last_scans() if x.datetime],key = lambda x: x.datetime, reverse=True) 
         #Scan.objects.filter(occurence__product=self,price__amount__isnull=False) 
         #return str(scans[0].datetime) 
    
        def get_last_min_scan(self): 
    
         sorted_last_scans = [x for x in self.get_last_scans() if x.valid] 
         sorted_last_scans.sort(key=lambda x: x.price.eur_price) 
         return sorted_last_scans[0] if sorted_last_scans else None 
    
        def get_last_min_price(self): 
         last_scan = self.get_last_min_scan() 
         if last_scan: 
          return last_scan.price.eur_price 
         return None 
    
        def get_active_occurences(self): 
         return self.get_occurences().filter(active=True) 
    

    선두로부터

    class Occurence(models.Model): 
        product = models.ForeignKey(Product, related_name='occurences', on_delete=models.CASCADE) 
    
        _last_scan = models.OneToOneField('Scan',null=True,blank=True,related_name='+') 
    
    
        currency = models.ForeignKey('Currency',related_name='occurences') 
    
        def get_last_scan(self): 
         try: 
          last = self.scans.select_related("price__amount").order_by('datetime').last() 
         except: 
          last = None 
         return last 
    
        def get_last_valid_scan(self): 
         try: 
          last = self.scans.exclude(price__isnull=True).order_by('-datetime').first() 
         except: 
          last = None 
         return last 
    
        def get_second_last_valid_scan(self): 
         scans = self.scans.exclude(price__isnull=True).order_by('-datetime').select_related("price") 
         if scans.count()>=2: 
          return scans[1] 
         return None 
    
        def get_valid_scans(self): 
         return self.scans.all().exclude(price__isnull=True) 
    
        def get_min_scan(self): 
         scan = self.get_valid_scans().order_by('price__amount').first() 
         if scan: 
          return scan 
         return None 
    
        """ STATS METHODS """ 
    
        def stats_get_difference_for_two_last_scans(self): 
         second_last_valid_scan = self.get_second_last_valid_scan() 
         if second_last_valid_scan: 
          difference_in_percent = math_ops.round_eur(decimal.Decimal(-1 * (100 - self.get_last_valid_scan().price.eur_price/second_last_valid_scan.price.eur_price * 100), 2)) 
         else: 
          difference_in_percent = decimal.Decimal(0) 
         return {'percent':difference_in_percent, 
           'xml_tag':'<two_last_scans_difference_in_percent>', 
           'xml_close_tag':'</two_last_scans_difference_in_percent>', 
           'label':'Last scans diff'} 
    
        def stats_get_min_price(self): 
         scan = self.get_min_scan() 
         if scan: 
          price = scan.price.eur_price 
         else: 
          price = None 
         return {'price': price, 
           'xml_tag': '<min_price>', 
           'xml_close_tag': '</min_price>', 
           'label': 'Min'} 
    
        def stats_get_avg_price(self): 
         prices = [x.price for x in self.scans.all() if x.price] 
         if prices: 
          price = math_ops.round_eur(decimal.Decimal(sum([x.eur_price for x in prices])/len(prices), 2)) 
         else: 
          price = None 
    
         preferred_currency = self.product.user.userprofile.preferred_currency 
    
         if preferred_currency: 
          if preferred_currency.shortcut == 'czk': 
           amount = Exchange.eur_to_czk(price) 
           pref_currency_string = '{} CZK'.format(amount) 
           pref_currency_amount = amount 
          else: 
           amount = price 
           pref_currency_string = u'{} €'.format(amount) 
           pref_currency_amount = amount 
         else: 
          if self.currency.shortcut == 'czk': 
           amount = Exchange.eur_to_czk(price) 
           pref_currency_string = '{} CZK'.format(amount) 
           pref_currency_amount = amount 
          else: 
           amount = price 
           pref_currency_string = u'{} €'.format(amount) 
           pref_currency_amount = amount 
    
    
         return {'price': price, 
           'pref_currency_string':pref_currency_string, 
           'pref_currency_amount':pref_currency_amount, 
           'xml_tag': '<average_price>', 
           'xml_close_tag': '</average_price>', 
           'label': 'AVG'} 
    

    PRICE 테이블 렌더링 예

    class Price(models.Model): 
        currency = models.ForeignKey('Currency',related_name='prices') 
        amount = models.DecimalField(max_digits=10,decimal_places=2) 
    
    
        def __unicode__(self): 
         return u'{} {}'.format(self.amount,self.currency) 
    
        def to_eur(self): 
         if self.currency.shortcut=='eur': 
          return self.amount 
         elif self.currency.shortcut=='czk': 
          return Exchange.objects.first().czk_to_eur(self.amount) 
    
        def to_czk(self): 
         if self.currency.shortcut == 'czk': 
          return self.amount 
         elif self.currency.shortcut == 'eur': 
          return Exchange.objects.first().eur_to_czk(self.amount) 
    
        @property 
        def eur_price(self): 
         if self.currency.shortcut=='eur': 
          return self.amount 
         elif self.currency.shortcut=='czk': 
          return self.to_eur() 
    
        @property 
        def czk_price(self): 
         cents = decimal.Decimal('01') 
         if self.currency.shortcut == 'czk': 
          return (self.amount).quantize(cents, decimal.ROUND_HALF_UP) 
         elif self.currency.shortcut == 'eur': 
          return self.to_czk() 
    
        @property 
        def pref_currency_amount(self): 
         pref_currency = self.scan.occurence.product.user.userprofile.preferred_currency 
         if pref_currency: 
          if pref_currency.shortcut == 'czk': 
           return self.czk_price 
          else: return self.eur_price 
         return self.amount 
    
        @property 
        def pref_currency_string(self): 
         pref_currency = self.scan.occurence.product.user.userprofile.preferred_currency 
         # return pref_currency.shortcut 
         if pref_currency: 
          if pref_currency.shortcut.lower() == 'czk': 
           return u'{} {}'.format(self.czk_price, pref_currency.shortcut) 
          else: 
           return u'{} {}'.format(self.eur_price, pref_currency.special_sign) 
         return u'{} {}'.format(self.amount,self.currency.special_sign) 
    
    
        def get_price(self,currency): 
         if currency=='eur': 
          return self.eur_price 
         elif currency=='czk': 
          return self.czk_price 
    
        def get_exchanged_price_string(self): 
         if self.currency.shortcut=='czk': 
          return u'{} {}'.format(Exchange.czk_to_eur(self.amount),u'€') 
         else: 
          return u'{} {}'.format(Exchange.eur_to_czk(self.amount),'CZK') 
    
        def get_original_price_string(self): 
         if self.currency.shortcut=='czk': 
          return u'{} {}'.format(self.amount,u'€') 
         else: 
          return u'{} {}'.format(Exchange.eur_to_czk(self.amount),'CZK') 
    

    django-debug-toolbar에 따라 occurences이 거의 2 초 걸립니다. select_relatedprefetch_related을 사용하여 최적화하려고하지만 여전히 느립니다.

    내가 다른 메소드를 호출하기 때문에 동일한 쿼리가 있고 그 쿼리는 여러 번 호출됩니다.

    class OccurencesTable(tables.Table): 
        site = tables.columns.TemplateColumn("""<a href="{{ record.url }}">{{ record.site.name }}</a>""",accessor='site.name', verbose_name=u'Site') 
        avg_price = tables.columns.TemplateColumn("""{{ record.stats_get_avg_price.pref_currency_string }}""",accessor='stats_get_avg_price.price', verbose_name='AVG price') 
        last_scan_price = tables.columns.TemplateColumn("""{{ record.get_last_scan.price.pref_currency_string }} """,accessor='get_last_scan.price.amount', verbose_name='Last scan price') 
        last_scan_time = tables.columns.TemplateColumn("""{{ record.get_last_scan.datetime }}""",accessor='get_last_scan.datetime', verbose_name='Last scan time') 
        difference = tables.columns.TemplateColumn("""{% load static %}{% with diff=record.stats_get_difference_for_two_last_scans.percent %} 
           {% if diff > 0 %}+{% endif %}{{ diff }} % <img style="height: 15px" src="{% if diff < 0 %}{% static "img/icons/arrow-trend-minus.png" %}{% elif diff == 0 %}{% static "img/icons/arrow-trend-normal.png" %}{% else %}{% static "img/icons/arrow-trend-plus.png" %}{% endif %}"> 
           {% endwith %}""",verbose_name='Difference') 
    
        class Meta: 
         model = Occurence 
         fields = ('id', 'site', 'last_scan_time','last_scan_price', 'difference', 'avg_price') 
         attrs = {'id': 'id_occurences_table', 
           'class': 'table', } 
    

    모델 선두로부터제품 방법을 최적화하는 방법을 알아낼 수 없습니다. 아이디어가 있습니까? 같은 코드로

    답변

    2

    , 당신은 다음 파이썬 코드 안에 그 결과를 정렬하는) (이 코드는 get_last_scans에 전화하여 전체 테이블을 검색하는

    @property 
    def last_scan_time(self): 
        scans = sorted([x for x in self.get_last_scans() if x.datetime],key = lambda x: x.datetime, reverse=True) 
        Scan.objects.filter(occurence__product=self,price__amount__isnull=False) 
        return str(scans[0].datetime) 
    

    얻을 타이밍에 감격해야합니다! 데이터베이스는 매우 빠르게 정렬 기능을 내장하고 있습니다. 그것을 사용하십시오.

    이 코드에는 다른 기능이 많이 있습니다. 당신은 그들 각각을 고쳐야 할 것입니다. 데이터베이스에서 필터링 및 정렬을 수행하십시오. 아니 귀하의 파이썬 코드입니다.

    +0

    예,이 솔루션은 간단합니다 (변경하는 것을 잊어 버렸습니다).하지만이 문제는 테이블에서 사용되지 않습니다. 주된 문제는 Occurence 모델의 메소드를 최적화하는 것입니다. 죄송합니다. –

    +0

    문제를 일으키는 특정보기, 모델 및 서식 파일을 사용하여 새 질문을 게시하십시오. 즉, [MCVE] (http://stackoverflow.com/help/mcve)를 만드십시오. – e4c5

    +0

    좋아, MCVE를 만들고 테스트했습니다. 페이지를로드하는 데 약 3 초가 걸리고 템플릿을 렌더링하는 데 약 2 초가 걸립니다. 다음 주제가 있습니다 : http://stackoverflow.com/questions/41510364/optimize-django-page-load –

    관련 문제