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

Я создаю приложение для криптовалюты, я заполняю некоторые таблицы криптографическими данными, чтобы получить некоторые из этих данных, мне нужно вычислить значения 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 %}

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
48
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

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

Если у вас есть ключ Cryptocompare API, установите его в 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,
    }
}

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

Другие вопросы по теме