Boketto
Boketto

Reputation: 895

Django Model - Mix model fields and instance attributes in class

Not sure if I am on a completely wrong track, but I want some kind of a temporary runtime variable/field in my Django model. Currently I have this:

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)

The aa_seq variable is a temporary value that should not be stored in the database. Nevertheless, to not re-compute the value for each method when multiple methods are to be used, I want to optionally give it as a parameter. I see that giving the aa_seq as a parameter in each method is redundant and also not object-oriented. Now I wonder if it is a good idea (and possible at all) to store it as a class property like:

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


However, I haven't found any similar examples where model fields and non-model fields were mixed...is there a reason for that?

Upvotes: 2

Views: 440

Answers (2)

Adam Barnes
Adam Barnes

Reputation: 3223

Your idea's good, but your nomenclature is confused.

There's absolutely nothing wrong with storing it as an instance attribute. It's not a field, and that it's on a model is coincidental. Models are just special cases of classes.

Here is an example of non-field attributes being defined on a Model subclass in the Django docs themself.

Upvotes: 3

Brian Destura
Brian Destura

Reputation: 12068

Looks like something cached_property can do:

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()

You can then just use self.aa_seq like you currently have, and you can remove the if not aa_seq checks as it's not needed anymore. Additionally this will cache the computed value of aa_seq for the lifetime of the instance without storing anything to the DB.

Upvotes: 0

Related Questions