我正在开发一个包含不同产品统计信息的大页面。我在那里使用多个图表,表格等。加载页面需要5秒钟。
这是一个非常简化的 models.py
修改
例如,OccurencesTable
包含此行:
last_scan_time = tables.columns.TemplateColumn("""{{ record.get_last_scan.datetime }}""",accessor='get_last_scan.datetime', verbose_name='Last scan time')
因此,对于每一行,必须对调用上次扫描的数据库执行查询。更好的方法是预加载所有Scan
个对象。
产品
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)
OCCURENCE
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')
例如,根据occurences
,渲染表django-debug-toolbar
大约需要2秒。我正在尝试使用select_related
和prefetch_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', }
无法弄清楚如何优化模型 Occurence 和 Product 的方法。你有什么想法吗?
答案 0 :(得分:2)
使用这样的代码,您应该对获得的时间感到兴奋
@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)
此代码通过调用get_last_scans()检索整个表,然后在python代码中对结果进行排序!数据库具有非常快速的内置排序功能。请使用它。
此代码中还有很多其他类似的功能。你将不得不修复它们。在数据库中进行过滤和排序。不在你的python代码中。