Django Создать класс из нескольких функций - PullRequest
0 голосов
/ 17 февраля 2020

В моем файле utils.py есть несколько функций, которые мои представления используют для вычисления торговой статистики. Ниже я добавил только 3, но есть 8, и скоро будет еще несколько. То, как я сейчас его настроил, работает, но очень неэффективно. Вы можете видеть, что каждая функция начинается с начала и вызывает другие функции, которые устанавливают аналогичные переменные.

Как правильно превратить это в класс?

Это даже правильный подход?


utils.py | несколько функций

def max_amount_function (pk):
    trade = get_object_or_404(Trade, pk=pk)
    entries_buy = Entry.objects.filter(trade=trade, entry_type="entry")
    max_amount_function = entries_buy.aggregate(max_amount_function=Sum('amount'))['max_amount_function']
    if max_amount_function is None:
        return 0
    else:
        return max_amount_function

def fees_function (pk):
    trade = get_object_or_404(Trade, pk=pk)
    entries = Entry.objects.filter(trade=trade)
    fees_function = entries.aggregate(fees_function=Sum('fee'))['fees_function']
    return fees_function

def entry_cpu_function (pk):
    if max_amount_function(pk) > 0:
        trade = get_object_or_404(Trade, pk=pk)
        entries_buy = Entry.objects.filter(trade=trade, entry_type="entry")
        entry_cpu_function = entries_buy.annotate(
            s=F('amount') * F('price')
        ).aggregate(
            entry_cpu_function=ExpressionWrapper(
                Sum(
                    Cast('s', output_field=models.FloatField())
                ) / Sum('amount'),
                output_field=models.FloatField()
            )
        )['entry_cpu_function']
        return entry_cpu_function
    else:
        return 0

utils.py | один класс

class TradeStats:
    trade = get_object_or_404(Trade)
    entries_buy = Entry.objects.filter(trade=trade, entry_type="entry")

    def __init__(self):
        pass

    def max_amount_functio(pk):
        pass

    def fees_function(pk):
        pass

    def entry_cpu_function(self):
        pass

Редактировать добавлен models.py, так как он может быть релевантным для записей, поскольку это другой класс.

models.py

class Trade(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
    status = models.CharField(max_length=2, choices=STATUS_CHOICES, default='op')
    broker = models.ForeignKey(Broker, on_delete=models.CASCADE, blank=True, null=True)
    asset = models.ForeignKey(Asset, on_delete=models.CASCADE, null=True)
    ...

class Entry(models.Model):

    ENTRY = 'entry'
    EXIT = 'exit'
    ENTRY_TYPE_CHOICES = [
        (ENTRY, 'Entry'),
        (EXIT, 'Exit'),

    trade = models.ForeignKey(Trade, on_delete=models.CASCADE)
    amount = models.FloatField(null=True)
    price = models.FloatField(null=True, blank=True)
    entry_type = models.CharField(max_length=5, choices=ENTRY_TYPE_CHOICES, default=ENTRY)
    ...

Ответы [ 2 ]

2 голосов
/ 17 февраля 2020

Rockstar уже опубликовала лучшее решение в этом контексте - вы работаете с моделью Django, и весь ваш код зависит от конкретного экземпляра модели c, поэтому добавление этих функций в качестве методов в вашу модель Trade очень obvuous решение.

При этом вопрос сам по себе (как превратить группу функций, полагающихся на одни и те же переменные в класс) также может быть связан с более обобщенным c ответом, который применим и к другим контекстам :

Во-первых, вы хотите исправить это определение класса, чтобы trades и entries_buy были экземплярами атрибутами, а не атрибутами классов - иначе они будут инициализированы только один раз во время импорта модуля и Вы получите устаревшие данные. Чтобы это работало, вам также необходимо добавить параметр pk в инициализатор класса:

class TradeStats:
    def __init__(self, pk):
        self.trade = get_object_or_404(Trade, pk=pk)
        self.entries_buy = Entry.objects.filter(trade=self.trade, entry_type="entry")

Затем вы хотите добавить self в качестве первого параметра ко всем вашим методам (иначе текущий экземпляр может не пропустите - и удалите теперь бесполезный параметр 'pk':

def max_amount_function(self):
    pass

def fees_function(self):
    pass

def entry_cpu_function(self):
    pass

Теперь вы можете сделать бэкпорт ваших реализаций методов, заменив соответствующие части доступом к self.trade и self.entries_buy, ie :

def max_amount_function(self):
    result = self.entries_buy.aggregate(max_amount_function=Sum('amount'))['max_amount_function']
    return 0 if result is None else result

et c ...

Кроме того, "xxx_yyy_function" очень плохое наименование. Как правило, вы хотите использовать существительные для объектов (переменных, значений) , имена классов и т. д. c) и глаголы для функций или методов, поэтому можно ожидать что-то вроде get_max_amount, get_fees и т. c.

Наконец, учитывая ваш вопрос (и что вы второй фрагмент выглядит так), я настоятельно рекомендую вам потратить некоторое время на официальный Python учебник - часть о классах , конечно, но вы можете узнать много других полезных вещей, делая весь учебник.

2 голосов
/ 17 февраля 2020

Вы можете переместить все эти функции в модель или в пользовательский QuerySet.

Если вы перейдете к модели, вы можете сделать что-то подобное. (Код может не работать, только для справки)

# MODEL
class Trade(models.Model):

    # Model fields here

    def get_entries(self):
        # NB: assuming you didn't specify a related_name
        # in the `Entry.trade` ForeignKey field. Else,
        # replace `entry_set` with said related_name.
        return self.entry_set.filter(entry_type="entry")


    def max_amount_function(self):
        max_amount_res = self.get_entries().aggregate(max_amount_function=Sum('amount'))['max_amount_function']
        if max_amount_res is None:
            return 0
        else:
            return max_amount_res

    def fees_function(self):
        return self.get_entries().aggregate(fees_function=Sum('fee'))['fees_function']

    def entry_cpu_function(self, ):

        if self.max_amount_function() <= 0:
            return 0

        entry_cpu_function = self.get_entries().annotate(
            s=F('amount') * F('price')
        ).aggregate(
            entry_cpu_function=ExpressionWrapper(
                Sum(
                    Cast('s', output_field=models.FloatField())
                ) / Sum('amount'),
                output_field=models.FloatField()
            )
        )['entry_cpu_function']
        return entry_cpu_function

Внутри views.py вы можете ссылаться на эти функции, как показано ниже -

trade = get_object_or_404(Trade, pk=pk)
trade.max_amount_function()
trade.fees_function()
trade.entry_cpu_function()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...