grenadejumper
grenadejumper

Reputation: 108

Saving results of django model manager to database

I want to present general statistics about the instances of the class "ModelClass" on a web page for my users. Lets say there are some thousands ModelClass-objects, and that there are a lot of statistics that I need to calculate. I have figured out that I can do this with model managers, and here is a (very) simplified example:

class ModelClassCustomManager(models.Manager):
    def get_query_set(self):
        return super(ModelClassCustomManager, self).get_query_set().filter(is_complete = True)


class ModelClass(models.Model):
    is_complete = models.BooleanField(default = False)
    ...
    objects = models.Manager()
    complete_objects = ModelClassCustomManager()

My concern is that this drains a lot of resources if this is calculated when users view the page. I would therefore like to calculate it only when I change or create new ModelClass objects since this is the only time the statistics really change. I guess this can be done by overriding the ModelClass save()-method.

What is the best way to save these results? Should I create another django-model for holding the calculated statistics, or is there another way of storing this information?

EDIT: Thanks to pastylegs for a good answer. Doing it this way, however causes some minor bugs, and I figured I'll explain them here, in case anyone else runs into this issue.

First of all, I incorrectly put the imports in the ModelClass at the top, so I got a circular dependency, which gave some weird results. To save yourself some frustration, put them where pastylegs did. Secondly, in order to overwrite the previous calculations of statistics (and not create a new one every time), just replace

if sender is ModelClass and instance is not Null:
    count = ModelClass.objects.all().count()
    stat = Stat(name='some_name', value=count).save()

with this:

if sender is ModelClass:
    count = Match.objects.all().count()
    try:
        stat = Stat.objects.get(key="Total")
        stat.update(key="Total", value=count) #Update statistic
    except: 
        Stat(key="Total", value=count).save() #Create new

Upvotes: 0

Views: 896

Answers (1)

Timmy O'Mahony
Timmy O'Mahony

Reputation: 53971

I'd create an app called statistics (or similar) that has a simple model to hold key,value pairs:

class Stat(models.Model):
    key = models.CharField(..)
    value = models.CharField(...)

then use a signal on your own model to run a function every time a user saves/updates a instance of your model. In models.py:

class ModelClass(models.Model):
    ...

from django.db.models.signals import post_save
from myapp.signal import updates_stats
post_save.connect(updates_stats, sender=ModelClass)

and then create the receiver function to calculate what ever statistics you need in signals.py

from myapp.models import ModelClass
from stats.models import Stat
def update_stats(sender, instance, signal, *args, **kwargs):
    if sender is ModelClass and instance is not Null:
        count = ModelClass.objects.all().count()
        stat = Stat(name='some_name', value=count).save()

This is a very simple and basic approach and only outlines how you can make use of signals to perform calculations but it is a good start. Ideally you should try to calculate these values outside of the request/response cycle (especially if they are big caculations) as it will hang your server and potentially time out your requests, so you should consider using a queuing/task system like Celery to perform the calculation in the background

Upvotes: 3

Related Questions