Reputation: 4577
I'm making a todo list in django. I have a model with some properties:
class Action(models.Model):
name = models.CharField("Action Name", max_length=200, unique = True)
slug = models.SlugField(max_length=100, blank=True)
complete = models.BooleanField(default=False, verbose_name="Complete?")
effort = models.IntegerField("Effort Level", choices = LEVELS, default = 3)
importance = models.IntegerField("Importance", choices = LEVELS, default = 3)
enjoyment = models.IntegerField("Enjoyment", choices = LEVELS, default = 3)
days_to_expiration = models.IntegerField("Days to Expiration", choices = DAYS_TO_EXPIRATION, default = 7)
reset_date = models.DateField("Date of Action Reset", blank=True, null=True)
I want to sort based on priority. I define priority as:
priority = (1 + (today's date - reset_date) / days_to_expiration) * importance
This just scales up importance based on how "overdue" the task is.
Now when I pull a list of incomplete actions, I can calculate this priority number for each action item, but that doesn't seem efficient since I will end up calculating the same numbers multiple times. I could define a new field in the model:
priority = models.DecimalField("Priority", max_digits=4, decimal_places=2, blank = True, null = True)
and schedule a function to run once a day to calculate the new priority per task.
But I want to know if it's possible to program the priority calculation directly into the model. So the priority field in the Action model is necessarily by design a function of importance and days_to_expiration. It would be updated dynamically using the values of importance, days_to_expiration, and today's date. That way, priority would always be calculated automatically when I need it. Is this a possibility?
Upvotes: 0
Views: 1885
Reputation: 46
I know its old , but I've faced a similar issue and solve it by overriding the default Manager of the model and overriding its get_queryset method then annotate the field in the database level .
something like
class ActionManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
priority=ExpressionWrapper(
(1 + (datetime.now().date() - F('reset_date')) / F('days_to_expiration')) * F('importance'),
output_field=models.DecimalField()
)
)
haven't test it but you should know the context
Upvotes: 1
Reputation: 239250
You could do something like:
class MyModel(models.Model):
...
@property
def priority(self):
return (1 + (date.today() - self.reset_date) / self.days_to_expiration) * self.importance
Then, you can access it like any other attribute on your model
priority = my_model.priority
Upvotes: 1
Reputation: 37177
What you want is called denormalization; priority
is a denormalized field. You can probably hack around with signals, or extend save
methods, or maybe use manager methods in some clever way to achieve this, but a nice alternative is to use django-denorm. The tutorial goes through a situation very similar to yours.
Upvotes: 2