Django 减少页面 SQL 查询(代码优化)

Django Reduce Page SQL Queries (Code Optimization)

我有 2 个页面越来越慢,其中一个在涉及超过 10 个对象时完全无法使用。使用 django-debug-toolbar 包后我知道问题是什么了。

ListView page: 23971.48 ms (182 queries including 180 similar and 178 duplicates )

我对优化数据库查询还很陌生,到目前为止一切正常。然而,正如我之前所说,如果我有超过 10 个对象(在本例中记录的交易),事情就会开始崩溃。我相信一个重要的解决方案是计算这些统计数据,然后将它们保存为字段。这是正确的方法,根本不是,还是只是触及表面?

models.py(你可以看出有更多的函数调用更多的函数)

class Trade(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
    ...

    # P/L Value = Sum of all buys * ( Entry Cost Per Unit (CPU) - Exit Cost Per Unit (CPU) )
    def get_profit_loss_value_or_None(self):
        if self.get_exit_cpu() > 0:
            if self.type == 'Long':
                result = self.get_entries().aggregate(
                get_profit_loss_value=Sum('amount', output_field=models.FloatField()
                                               ) * (self.get_exit_cpu() - self.get_entry_cpu()))['get_profit_loss_value']
                return 0 if result is None else result
            elif self.type == 'Short':
                result = self.get_entries().aggregate(
                get_profit_loss_value=Sum('amount', output_field=models.FloatField()
                                               ) * -1 * (self.get_exit_cpu() - self.get_entry_cpu()))['get_profit_loss_value']
                return 0 if result is None else result
        else:
            return 0

    #Fight unexpected None errors 
    def get_profit_loss_value(self):
        ret = self.get_profit_loss_value_or_None()
        return 0 if ret is None else ret


    # profit_loss_value_fees = profit_loss_value - fees
    def get_profit_loss_value_fees(self):
        result = self.get_profit_loss_value() - self.get_fees()
        return result

views.py

class TradeListView(LoginRequiredMixin, ListView):
    model = Trade

    def get_queryset(self):
        return Trade.objects.filter(user=self.request.user).order_by('created')

trade_list.html

<!-- Content Row -->

<div class="row">
    {% for trade in object_list reversed %}
    <div class="col-xs-12 col-sm-6 col-lg-4">
        <div class="card mb-3 shadow-sm">
            {% if trade.image %}
            <a href="{{ trade.image.url }}" target="_blank"><img src="{{ trade.image.url }}" class="img-fluid card-img-top" alt="Responsive image"></a>
            {% else %}
            <a target="_blank"><img src="https://tradejournal.s3.amazonaws.com/media/no-image-available-icon.jpg" class="img-fluid card-img-top" alt="Responsive image"></a>
            {% endif %}

            <div class="card-body">
                <h4>
                    <a href="{% url 'trade-detail' trade.id %}" class="card-title card-link">
                        {% if trade.status == "cl" %}
                            {% if trade.get_profit_loss_value_fees > 0 %}
                            <font color="green">${{ trade.get_profit_loss_value_fees|floatformat:2 }}</font>
                            {% elif trade.get_profit_loss_value_fees == 0 %}
                            <font color="#5a5c69">${{ trade.get_profit_loss_value_fees|floatformat:2 }}</font>
                            {% elif trade.get_profit_loss_value_fees < 0 %}
                            <font color="red">${{ trade.get_profit_loss_value_fees|floatformat:2 }}</font>
                            {% endif %}
                        {% else %}
                        <font color="orange">{{  trade.get_status_display }}</font>
                        {% endif %}
                    </a>
                </h4>
                <p>
                    <a href="{% url 'trade-detail' trade.id %}" class="card-text card-link">
                        {{ trade.asset }} | {{ trade.symbol | upper }} | {{ trade.type }}
                    </a>
                </p>
                <p class="card-text">
                    <small>
                        <a href=" {% url 'trade-detail' trade.id %} " class="card-link text-muted"><i class="fas fa-binoculars"></i> View</a>
                        {% if user.is_authenticated and trade.user == user %}
                        <a href=" {% url 'trade-update' trade.id %} " class="card-link text-muted"><i class="fas fa-edit"></i> Edit</a>
                        <a href=" {% url 'trade-delete' trade.id %} " class="card-link text-muted"><i class="fas fa-trash"></i> Delete</a>
                        {% endif %}
                    </small>
                </p>   
            </div>

        </div>
    </div>
    {% empty %}
    <p class="lead">No trades yet</p>
    {% endfor %}

</div>
{% endblock content %}

小边想:

我一直在考虑将这个项目的一部分开源,因为我从社区得到了很多帮助。它可以帮助未来的编码人员在同一个地方,并帮助 post 更详细地解决未来的问题。对此有什么想法吗?尤其是如何以正确的方式去做。

如果有人想知道我到目前为止所做的是将计算保存为字段并在每次编辑交易时更新这些 "autofields"。我还有 运行 一个脚本来更新所有现有交易。

models.py(交易内class)

#AUTOMATED FIELDS
WIN = 'win'
LOSS = 'loss'
SCRATCH = 'scratch'

RESULT_CHOICES = [
    (WIN, 'win'),
    (LOSS, 'loss'),
    (SCRATCH, 'scratch'),
]
max_amount = models.FloatField(null=True)
fees = models.FloatField(null=True)
entry_cpu = models.FloatField(null=True)
exit_cpu = models.FloatField(null=True)
position_size = models.FloatField(null=True)
profit_loss_percent = models.FloatField(null=True)
profit_loss_value = models.FloatField(null=True)
profit_loss_value_fees = models.FloatField(null=True)
trade_result = models.CharField(max_length=7, choices=RESULT_CHOICES, null=True)

def save(self):
    self.max_amount = self.get_max_amount()
    self.fees = self.get_fees()
    self.entry_cpu = self.get_entry_cpu()
    self.exit_cpu = self.get_exit_cpu()
    self.position_size = self.get_position_size()
    self.profit_loss_percent = self.get_profit_loss_percent()
    self.profit_loss_value = self.get_profit_loss_value()
    self.profit_loss_value_fees = self.get_profit_loss_value_fees()
    self.trade_result = self.get_trade_result()
    if not self.id:
        self.created = timezone.now()
    self.modified = timezone.now()
    return super(Trade, self).save()