Hello, I wasn’t sure if this should go here or in django internals. It’s about using django, but I’m getting into the arcane api’s writing your own Fields.
Environment: Django 3.2, Python 3.8, Postgres
My model has several fields that are derived from math on other fields(ie: score
, events
, avg_score
where avg_score is score /event). My normal approach is to add properties as needed. In this case, we need to sort by these fields and paginate in the database. My solution was to add .annotate()
to all of my queries. That works well enough, but it’s a lot of duplicated, wordy code. So I moved those annotate
’s to a custom manager. Now I have this:
class ScoreboardManager(models.Manager):
"""
This adds calculated fields as annotations, so that they can be searched and sorted
"""
derived_fields = {
'avg_score': ExpressionWrapper(
models.F('score') / models.F('events'),
output_field=models.DecimalField(max_digits=9, decimal_places=2)
)
}
def get_queryset(self):
return super().get_queryset().annotate(**self.synthesized_fields)
class Scoreboard(models.Model):
score = models.PositiveIntegerField(null=False,validators=[MinValueValidator(1)])
events = models.PositiveIntegerField(null=False, validators=[MinValueValidator(1)])
objects = ScoreboardManager()
This is much better. Except that I can’t help but feel that I would rather the derived_fields look like Fields in the model.
Like this:
class Scoreboard(models.Model):
score = models.PositiveIntegerField(null=False,validators=[MinValueValidator(1)])
events = models.PositiveIntegerField(null=False, validators=[MinValueValidator(1)])
avg_score = DerivedField(models.F('games_played') / models.F('players_active'), typefield = DecimalField(max_digits=9, decimal_places=2))
This seemed simple enough at first. I figured that contribute_to_class
could replace the all the managers on the class with subclasses that had the appropriate magic. Now I am starting to doubt that plan. A look at django.db.models.options has made me realize that the managers attr is not at all simple. Additionally I see possible issues with definition order.
I’m hoping that a django internals wrangler has some pointer or ideas about how to proceed. If I get this where I like it might make a library out of it and share.
(Note: I also considered the django-computedfields. That won’t work for our use case. It used pre-save hooks to do it’s work and we are loading the table using bulk_create
which skips the hooks. )