Skip to content

Instantly share code, notes, and snippets.

@melvyn-sopacua
Created February 14, 2017 00:42
Show Gist options
  • Save melvyn-sopacua/1b4e4585ab15ffc176798c083470cf04 to your computer and use it in GitHub Desktop.
Save melvyn-sopacua/1b4e4585ab15ffc176798c083470cf04 to your computer and use it in GitHub Desktop.
Calculated fields example
from django.db import models
from django.db.models.signals import post_init
from django.db.models.fields import NOT_PROVIDED
from django.core import validators as v
class Substance(models.Model):
name = models.CharField(max_length=32, primary_key=True)
class MixtureIngredient(models.Model):
substance = models.ForeignKey(
Substance, on_delete=models.CASCADE
)
mixture = models.ForeignKey(
'Mixture', on_delete=models.CASCADE
)
proportion = models.PositiveSmallIntegerField(
validators=[
v.MinValueValidator(1),
v.MaxValueValidator(100),
]
)
class CalculatedPercentageField(models.PositiveSmallIntegerField):
pct_model = None
pct_field_name = None
def __init__(self, pct_model: models.Model, pct_field_name='percentage',
*args, **kwargs):
self.pct_model = pct_model
self.pct_field_name = pct_field_name
super().__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super(
CalculatedPercentageField, self
).deconstruct()
kwargs['pct_model'] = self.pct_model
kwargs['pct_field_name'] = self.pct_field_name
return name, path, args, kwargs
def update_total(self, instance, **kwargs):
manager = self.pct_model._meta.default_manager
total = 0
for value_dict in manager.values(self.pct_field_name):
total += value_dict[self.pct_field_name]
setattr(instance, self.name, total)
def contribute_to_class(self, cls, name, private_only=False,
virtual_only=NOT_PROVIDED):
super(CalculatedPercentageField, self).contribute_to_class(
cls, name, private_only, virtual_only
)
# noinspection PyProtectedMember
if not cls._meta.abstract:
post_init.connect(self.update_total, sender=cls)
class Mixture(models.Model):
name = models.CharField(max_length=63, unique=True)
ingredients = models.ManyToManyField(Substance, through=MixtureIngredient)
total_proportion = CalculatedPercentageField(
MixtureIngredient, 'proportion'
)
from django.test import TestCase
from .models import Substance, Mixture, MixtureIngredient
class MixtureTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.water = Substance.objects.create(name='Water')
cls.sugar = Substance.objects.create(name='Sugar')
cls.earl_grey = Substance.objects.create(name='Earl Grey')
def test_mixture(self):
tea = Mixture.objects.create(name='tea', total_proportion=0)
MixtureIngredient.objects.create(
substance=self.water, mixture=tea, proportion=80
)
MixtureIngredient.objects.create(
substance=self.earl_grey, mixture=tea, proportion=5
)
actual = Mixture.objects.get(name='tea')
self.assertEqual(actual.total_proportion, 85)
MixtureIngredient.objects.create(
substance=self.earl_grey, mixture=tea, proportion=15
)
actual2 = Mixture.objects.get(name='tea')
self.assertEqual(actual2.total_proportion, 100)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment