Reputation: 46
My model has a field that should change if it's within a date range.
It would look like this:
class Election(models.Model)
start_date = models.DateTimeField(verbose_name = 'Start Date')
end_date = models.DateTimeField(verbose_name = 'End date')
active = models.BooleanField(default=False)
def updateActive(self):
now = timezone.now()
if self.start_date < now and self.end_date > now:
self.active=True
else:
self.active=False
self.save()
RIght now, every time I query for this model, I call updateActive()
from my views.py
.
So, my question is: Is there a way to call updateActive()
every time I fetch an Election
object? Or keeping it constant updated?
Any idea is welcome.
Upvotes: 2
Views: 667
Reputation: 53744
The best method would be not to have the active
field at all in your model. The main reason is that when a value can be generated from a simple calculation, it should not be stored in the database. The second reason is that BooleanField cannot be effectively indexed and queries involving this field will be slow. Therefore you do not lose anything by doing the calculation instead of doing the field. The best way is to add a custom queryset like this:
class ElectionQuerySet(models.QuerySet):
def is_active(self):
return self.filter(start_date__lt=timezone.now()).filter(end_date__gt=timezone.now())
Now your model is really simple.
class Election(models.Model): start_date = models.DateTimeField(verbose_name = 'Start Date') end_date = models.DateTimeField(verbose_name = 'End date')
objects = ElectionQuerySet.as_manager()
Now your model is realy simple.
class Election(models.Model):
start_date = models.DateTimeField(verbose_name = 'Start Date')
end_date = models.DateTimeField(verbose_name = 'End date')
objects = ElectionQuerySet.as_manager()
Yes that's all. There is no need to update the database everytime you fetch an object! You can use a simple method to find out what's active or not
Election.objects.is_active()
The result from is_active is a queryset, and you can chain it as usual
Election.objects.is_active().filter(...)
if you want to check if an election is active in the template you can do :
class Election(models.Model):
def is_active()
if self.start_date < now and self.end_date > now:
return True
Upvotes: 2
Reputation: 26
You can write a custom manager for the model. While you can set the active attribute for the instances in the query itself, it doesn't seem correct from a design point of view(A get method shouldn't mutate the data it is querying). It makes sense to have an active attribute as you may want to invalidate a certain instance later manually. You could either update the active field using a background job, this way your manager would look like
class ElectionManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(active=True)
class Election(models.Model):
start_date = models.DateTimeField(verbose_name = 'Start Date')
end_date = models.DateTimeField(verbose_name = 'End date')
active = models.BooleanField(default=False)
elections = ElectionManager()
This way Election.elections.all() would return only active elections. If you want to filter out the query via a class method, then you can use list comprehension or generators to get the required queryset in the ElectionManager.get_queryset method.
Upvotes: 0