Можно ли сделать вызов API внутри функции в model.py, почему мое приложение так сильно замедлилось после этого? - PullRequest
1 голос
/ 25 апреля 2019

Я создаю приложение криптовалюты, я заполняю некоторые таблицы криптографическими данными, чтобы получить некоторые из этих данных, мне нужно вычислить значения API против значений базы данных.Я сделал это путем создания функций и вызова API в файле models.py, но это сильно замедлило работу моего приложения.Я делаю что-то неправильно?Есть ли лучший способ, которым я мог бы написать это?

models.py ниже.Значительно замедлился с момента добавления таблицы 4 свойств

class Transaction(models.Model):
    currency = models.CharField(max_length=20)
    amount = models.IntegerField()
    total_price = models.DecimalField(max_digits=8, decimal_places=2)
    date_purchased = models.DateTimeField()
    note = models.TextField(default="")
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    amount_per_coin = models.DecimalField(max_digits=8, decimal_places=2, editable=False)

    def save(self, *args, **kwargs):
        self.amount_per_coin = self.total_price / self.amount
        super(Transaction, self).save(*args, **kwargs)

    def __str__(self):
        return str(self.pk)+','+self.currency + ', '+str(self.amount)

    def get_absolute_url(self):
        return reverse('transaction-detail', kwargs={'pk': self.pk})

    @property
    def coin_value(self):
        try:
            current_price = requests.get("https://min-api.cryptocompare.com/data/price?fsym="+self.currency+"&tsyms=EUR")
            price = json.loads(current_price.content)
            return price["EUR"]
        except:
            return 0


    @property
    def total_value(self):
        value = self.coin_value * self.amount
        return round(value, 2)

    @property
    def profit_loss(self):
        value = float(self.total_value) - float(self.total_price)
        return round(value, 2)

    @property
    def profit_loss_percent(self):
        value = ((float(self.total_value) - float(self.total_price))/self.total_value)*100
        return round(value, 1)

, к которой я добавляю


 {% for transaction in transactions %}
            <tr>
                <td>{{transaction.currency}}</td>
                <td>{{transaction.amount}}</td>
                <td>{{transaction.amount_per_coin}}</td>
                <td>{{transaction.total_price}}</td>
                <td>{{transaction.coin_value}}</td>
                <td>{{transaction.total_value}}</td>
                <td>{{transaction.date_purchased|date:"j N Y"}}</td>
                <td>{{transaction.profit_loss}}</td>
                <td>{{transaction.profit_loss_percent}} %</td>
                <td><a href="{% url 'transaction-detail' transaction.id %}">View</a></td>
            </tr>
            {% endfor %}

1 Ответ

1 голос
/ 26 апреля 2019

Сетевые подключения к другому сервису всегда будут относительно медленными.Поэтому вам нужно найти способы избежать слишком большого числа таких запросов.

Обычный метод - некоторое время кэшировать результаты.Даже короткий кэш с диапазоном в несколько секунд может очень помочь, если вы обрабатываете много транзакций, которые используют небольшое количество валют.Прямо сейчас вы создаете новый запрос для каждого свойства, к которому обращаетесь, поэтому рендеринг вашей таблицы вызывает четыре запроса на строку транзакции, отображаемую .

Django уже имеет поддержку для кэширования данных ;ниже будет использоваться кэш, настроенный в CACHES = {..., 'currencies': {...},}, если он существует, но в противном случае используется конфигурация 'default'.Настройка конфигурации выделенного кэша позволяет вам установить такие вещи, как время ожидания только для данных о валюте.

Если у вас есть ключ API Cryptocompare, задайте его в CRYPTOCOMPARE_API_KEY в файле settings.py:

import requests
from django.conf import settings
from django.core.cache import caches, InvalidCacheBackendError

APIURL = 'https://min-api.cryptocompare.com/data/price'

class CurrencyPrices:
    def __init__(self):
        self.session = requests.Session()  # re-use connections
        api_key = getattr(settings, 'CRYPTOCOMPARE_API_KEY', None)
        if api_key is not None:
            self.session.headers['Authorization'] = f'Apikey {api_key}'
        try:
            self.cache = caches['currencies']
            self.key_prefix = ''
        except InvalidCacheBackendError:
            self.cache = caches['default']
            self.key_prefix = 'currency_cache_'

    def get_currency_price(self, currency):
        key = f'{self.key_prefix}{currency}'
        value = self.cache.get(key)
        if value is None:
            params = {"fsym": currency, "tsyms": "EUR"}
            response = self.session.get(APIURL, params=params)
            value = response.json()['EUR']
            self.cache.set(key, value)
        return value

_currency_prices = CurrencyPrices()  # single instance per process
get_currency_price = _currency_prices.get_currency_price

затем импортируйте get_currency_price и используйте его для получения ваших значений:

@property
def coin_value(self):
    return get_currency_price(self.currency)

Вот пример конфигурации кэша, которая кэширует ответы в локальной памяти в течение 10 секунд:

CACHES = {
    'currencies': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'currencies',
        'TIMEOUT': 10,
    }
}

Проверьте, какие другие параметры могут потребоваться .

...