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
Post a Comment