-1

Je travaille sur une grande page avec différentes statistiques de produits. J'y utilise plusieurs cartes, tableaux etc. Il faut par exemple 5 secondes pour charger la page.Le chargement de la page est très lent - comment optimiser correctement?

C'est une très simplifiée models.py

EDIT

Par exemple, OccurencesTable contient cette ligne:

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

Donc, pour chaque ligne, il doit être exécuté requête à la base de données qui appelle le dernier scan. Une meilleure approche serait de précharger tous les objets Scan.

DU PRODUIT

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) 

OCCURRENCE

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'} 

PRIX

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') 

Par exemple rendu tableau occurences prend presque 2 secondes selon django-debug-toolbar. J'essaie de l'optimiser en utilisant select_related et prefetch_related mais c'est toujours lent.

C'est à cause de différentes méthodes que j'appelle a les mêmes requêtes et ces requêtes sont appelées plusieurs fois.

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', } 

ne peux pas comprendre comment optimiser les méthodes de modèles Occurence et Produit. As tu des idées?

Répondre

2

Avec code comme ceci, vous devriez être heureux avec le moment que vous obtenez

@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) 

Ce code récupère une table entière par l'appel à get_last_scans(), vous triez ce résultat à l'intérieur du code python! Les bases de données ont une fonctionnalité de tri intégrée très rapide. S'il vous plaît utilisez-le.

Il y a beaucoup d'autres fonctions comme celle-ci dans ce code. Vous devrez réparer chacun d'eux. Faites le filtrage et le tri dans la base de données. Pas dans votre code python.

+0

Oui, cela a une solution simple (j'ai oublié de le changer), mais cela n'est pas utilisé dans la table, Le problème principal est d'optimiser les méthodes pour le modèle Occurence, désolé d'y laisser cette méthode. –

+0

Veuillez poster une nouvelle question avec la vue spécifique, le modèle et le modèle qui pose problème. En d'autres termes, créez un [MCVE] (http://stackoverflow.com/help/mcve) – e4c5

+0

Ok, j'ai créé un MCVE et testé. Il faut environ 3 secondes pour charger la page et environ 2 secondes pour afficher les modèles. Voici le sujet: http://stackoverflow.com/questions/41510364/optimize-django-page-load –