-1

私は、製品の統計情報の異なる大きなページを作成しています。私は複数のチャート、テーブルなどを使用しています。ページをロードするには5秒かかります。ページの読み込みが非常に遅い - 最適化の仕方は?

これは、例えば、非常に簡略化されたmodels.py

EDIT

ある、OccurencesTableこの行が含まれています

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

そこで行ごとに、クエリが実行されなければなりません最後のスキャンを呼び出すデータベースに転送します。より良いアプローチはすべての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'} 

価格

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

は、あなたは、あなたがPythonコード内のその結果をソートしている)(このコードは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) 

取得したタイミングでワクワクする必要があります!データベースはソート機能が非常に速く組み込まれています。それを使用してください。

このコードには他にもたくさんの機能があります。それぞれを修正する必要があります。データベースでフィルタリングとソートを行います。あなたのPythonコードではありません。

+0

はい、これは簡単な解決策です(変更するのを忘れました)が、これはテーブルでは使用されません。主な問題は、このメソッドを残して申し訳ありませんが、Occurenceモデルのメソッドを最適化することです。 –

+0

問題の原因となっている特定のビュー、モデル、テンプレートで新しい質問を投稿してください。つまり、[MCVE](http://stackoverflow.com/help/mcve)を作成してください。 – e4c5

+0

私はMCVEを作成し、テストしました。ページの読み込みには約3秒、テンプレートのレンダリングには約2秒かかります。ここにトピックです:http://stackoverflow.com/questions/41510364/optimize-django-page-load –

関連する問題