django - How to aggregate python tasks for certain period and execute each task only once -


consider generic app give users points each time good

i have model

class gooddeed(models.model):      user = models.forgeinkey(customuser)      points = models.integerfield(default=0, blank=true)  class customuser(models.model):      points = models.integerfield(default=0, blank=true)      rank = models.integerfield(blank=true, null=true) 

now each time new deed added total points of user recalculated , each time user's points recalculated ranking of users (against other users) recalculated well.

this horrible, want aggregate ranking update requests , execute once every few minutes. there python library can this?

a cron job option update ranking of users every few minutes regardless of whether update needed or not.

edit: based on @the django ninja's suggestion wrote decorator , sharing it

usage:

    class customuser(models.model):          @cached_model_property         def points(self):             del self.rank  # removed cached rank value             return sum(self.good_deeds.values_list("points", flat=true))          @cached_model_property(readonly=false)         def rank(self):             user_list = []             user in user.onjects.all().only("points")                 user_list.push((user.points, user))             # sort list             user_list.sort(reverse=true)             # update ranking             rank = 0             my_rank = none             points, user in user_list:                 rank += 1                 if user.pk == self.pk:                     # don't update instance's rank yet,                     # updated when function return                     my_rank = rank                     continue                 # save new rank in cache                 user.rank = rank             # if forget return (not none) caching assumed invalid             return my_rank 

effect

>>> user.points # call points() method, cache result, return >>> user.points # return cached result without calling points() method >>> del user.points >>> user.points # call points() method, cache result, return  >>> user.rank # 1. calculate , save (in cache) rank of users except current user # 2. cache current user's rank , return >>> user.rank # return cached user's rank >>> del user.points >>> user.rank # recalculate user's points , ranking values  >>> user.points = 9 # readonly property exception 

cached rank value user id 20 stored in cache key called 'user.20.rank'

decorator code

def cached_model_property(f=none, **kwargs):     """     cached_model_property decorator model functions takes no arguments     function converted property support caching out of box     sample usage:      class team(models.model):          @cached_model_property         def points(self):             # complex db queries             return result          @cached_model_property(readonly=false)         def editable_points(self):             # result             return result      try     team = team.objects.first()     team.points  <-- complex db queries happen, result returned     team.points  <-- time result returned cache (points function not called @ all!     del team.points <-- points value has been removed cache     team.points  <-- complex db queries happen, result returned      set readlonly parameter false make property writeable     team.editable_points = 88     in case assigned value replace value stored in cache     team.editable_points     returns 88     """      readonly = kwargs.get("readonly", true)      def func(f):         def _get_cache_key(obj):             model_name = obj.__class__.__name__             method_name = f.__name__             return "%s.%s.%s" % (model_name, obj.pk, method_name)          def getx(obj):             """             decorator can convert function **doesn't** take arguments cached property             :param obj: model object instance (python provide default class members)             :return: cached value if present otherwise call actual method              note:             decorator doesn't work if model function return none.             function called long return none, no caching!             """             # try cache key method             cache_key = _get_cache_key(obj)             result = cache.get(cache_key)             # if not cached, call actual method , cache result             if result none:                 result = f(obj)                 cache.set(cache_key, result)             return result          def delx(obj):             """             remove property cache             :param obj:             :return: none             """             cache_key = _get_cache_key(obj)             # remove key cache             cache.delete(cache_key)          def setx(obj, value):             """             set cache value of property             :param obj:             :return: none             """             cache_key = _get_cache_key(obj)             # remove key cache             cache.set(cache_key, value)          if readonly:             return property(fget=getx, fdel=delx)         else:             return property(fget=getx, fset=setx, fdel=delx)      # f (= class method) passed when using @cached_model_property     if f:         return func(f)     # f not passed when using @cached_model_property(readonly=true) or @cached_model_property()     return f 

a mentionned above, ranking task seems heavy duty stuff on database.

i go basic cache invalidation solution, if user rank updated cache should invalidated :

from django.core.cache import cache  def the_function_that_update_user_rank()     # updating rank of user     # when rank updated set our cache key (global-rank-update-needed) true     cache.set('global-rank-update-needed', 'true') 

then manage.py command should called every x seconds cron job, command check cache if 'global-rank-update-needed' true , update global rank :

from django.core.cache import cache  class updateglobalrank(basecommand):     = 'closes specified poll voting'       def handle(self, *args, **options):         if cache.get('global-rank-update-needed') == 'true':             # here update rank stuff             print 'updating global rank'         cache.set('global-rank-update-needed', 'false')                          

following approach global rank updated if user's rank changed

hope helps.


Comments