Модель Django — Смешивание полей модели и атрибутов экземпляра в классе

#python #django #django-models #django-queryset

Вопрос:

Не уверен, что я нахожусь на совершенно неправильном пути, но мне нужна какая-то временная переменная/поле времени выполнения в моей модели Django. В настоящее время у меня есть это:

 class BioChemicalProperties(models.Model):
    mw = models.FloatField(blank=True, null=True)
    iep = models.FloatField(blank=True, null=True)
    exco = models.FloatField(blank=True, null=True)
    molar_exco = models.FloatField(blank=True, null=True)
    e01 = models.FloatField(blank=True, null=True)

    def get_protein(self):
        aa_seq = self.ab.get_lc_protein()
        aa_seq  = self.ab.get_hc_protein()
        return aa_seq

    def calc_mw(self, aa_seq=None):
        if not aa_seq:
            aa_seq = self.get_protein()
        analysed_seq = ProteinAnalysis(aa_seq)
        self.mw = analysed_seq.molecular_weight()   18.015

    def calc_exco(self, aa_seq=None):
        if not aa_seq:
            aa_seq = self.get_protein()
        analysed_seq = ProteinAnalysis(aa_seq)
        self.exco = analysed_seq.molar_extinction_coefficient()[1]
    
    #...more methods here...

    def calc_all_bioprops(self, aa_seq=None):
        if not aa_seq:
            aa_seq = self.get_protein()
        self.calc_mw(aa_seq)
        self.calc_iep(aa_seq)
        self.calc_molexco(aa_seq)
        self.calc_e01(aa_seq)

 

aa_seq Переменная является временным значением, которое не должно храниться в базе данных. Тем не менее, чтобы не пересчитывать значение для каждого метода при использовании нескольких методов, я хочу при необходимости указать его в качестве параметра. Я вижу, что указание параметра aa_seq в качестве параметра в каждом методе является избыточным и также не объектно-ориентированным. Теперь я задаюсь вопросом, хорошо ли (и возможно ли вообще) хранить его как свойство класса, например:

 class BioChemicalProperties(models.Model):
    mw = models.FloatField(blank=True, null=True)
    iep = models.FloatField(blank=True, null=True)
    exco = models.FloatField(blank=True, null=True)
    molar_exco = models.FloatField(blank=True, null=True)
    e01 = models.FloatField(blank=True, null=True)
    aa_seq = ""

    def calc_mw(self):
        if not self.aa_seq:
            self.aa_seq = self.get_protein()
        analysed_seq = ProteinAnalysis(self.aa_seq)
        self.mw = analysed_seq.molecular_weight()   18.015


 

Однако я не нашел подобных примеров, в которых поля модели и немодельные поля были mixed…is для этого есть причина?

Ответ №1:

Ваша идея хороша, но ваша номенклатура запутана.

Нет абсолютно ничего плохого в том, чтобы сохранить его в качестве атрибута экземпляра. Это не поле, и то, что оно находится на модели, является случайным совпадением. Модели-это просто частные случаи классов.

Вот пример того, как атрибуты, не относящиеся к полю, определяются в Model подклассе в самих документах Django.

Комментарии:

1. Спасибо вам за ваш ответ. Я реализовал его, и он действительно работает так, как ожидалось.

2. ИМО это действительно (я тоже проголосовал), но обычно атрибуты, не относящиеся к полю, используются для чего-то вроде констант (например, для выбора в примере). И при таком подходе можно было бы установить значение на что-то другое, вместо того, чтобы всегда быть уверенным, что это вычисляемое поле с использованием property или cached_property (без задатчика). Также не потребуется ли для этого постоянная проверка того, было ли рассчитано это значение, так что куча if not self.aa_seq: self.aa_seq = calculate() ?

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

Ответ №2:

Похоже, что-то cached_property может сделать:

 from django.utils.functional import cached_property


class BioChemicalProperties(models.Model):
    ...
    @cached_property
    def aa_seq(self):
        return self.ab.get_lc_protein()   self.ab.get_hc_protein()
 

Затем вы можете просто использовать self.aa_seq то, что у вас есть в настоящее время, и вы можете удалить if not aa_seq проверки, так как они больше не нужны. Кроме того, это приведет к кэшированию вычисленного значения aa_seq в течение всего срока службы экземпляра без сохранения чего-либо в БД.

Комментарии:

1. Привет @BrianD, спасибо за ваше предложение. Хотя первоначальный простой подход к использованию атрибута экземпляра, похоже, уже сделал свое дело, свойство cached_property было для меня новым, и это выглядит как интересный подход для других вариантов использования!

2. Все хорошо! Я поддержал ответ @Адама Барнса, но при таком подходе имейте в виду, что вам всегда придется проверять, был ли aa_seq он уже рассчитан ( if not aa.seq ). Также можно было бы установить для него другое значение за пределами экземпляра, что потенциально может привести к поведению, которого вы не ожидаете (например instance.aa_seq = somerandomvalue ). Но решать вам