Package: django-getorcreateplus
Django GetOrCreatePlus is a set of queryset mixins to create custom querysets that can:Cache results of get_or_create() or update_or_create()Always use get_or_create() when get() calls are madeAllow non-atomic get_or_create() to avoid nested transaction pointsQuick start# models.pyfromdjango.dbimportmodelsfromdjango.core.cacheimportcachesfromgetorcreateplusimportCachedGetOrCreateMixin,AlwaysGetOrCreateMixin,NonAtomicGetOrCreateMixin# sample for CachedGetOrCreateMixin (see below for changes to be made in settings.py)classCachedQuerySet(CachedGetOrCreateMixin,models.QuerySet):passclassCachedManager(models.manager.BaseManager.from_queryset(CachedQuerySet)):use_for_related_fields=TrueclassCachedImmutableModel(models.Model):foo=models.CharField(max_length=8)bar=models.IntegerField(null=True)objects=CachedManager()obj1,_=CachedImmutableModel.objects.get_or_create(foo='FooBar')# fetches from dbobj2,_=CachedImmutableModel.objects.get_or_create(foo='FooBar')# hits cacheclassCachedMutableObject(models.Model):foo=models.CharField(max_length=8)bar=models.IntegerField(null=True)objects=CachedManager()defsave(self,**kwargs):cache=caches[self._meta.model_name]cache.delete(self.pk)returnsuper(CachedMutableObject,self).save(**kwargs)obj1,_=CachedMutableModel.objects.get_or_create(foo='FooBar')# fetches from dbobj1.bar=1obj1.save()# invalidate object cacheobj2,_=CachedMutableModel.objects.get_or_create(foo='FooBar')# fetches from dbobj3,_=CachedMutableModel.objects.get_or_create(foo='FooBar')# hits cache# sample for NonAtomicGetOrCreateMixinclassNonAtomicQuerySet(NonAtomicGetOrCreateMixin,models.QuerySet):passclassNonAtomicManager(models.manager.BaseManager.from_queryset(NonAtomicQuerySet)):use_for_related_fields=TrueclassParentQuerySet(NonAtomicQuerySet):defcreate(self,**kwargs):children=kwargs.pop('children',[])parent=super(ParentQuerySet,self).create(**kwargs)forchildinchildren:parent.children.get_or_create(**child)returnparentclassParentManager(models.manager.BaseManager.from_queryset(ParentQuerySet)):use_for_related_fields=TrueclassParentModel(models.Model):foo=models.CharField(max_length=8)objects=ParentManager()classChildModel(models.Model):parent=models.ForeignKey(Parent,related_name='children')bar=models.CharField(max_length=8)objects=NonAtomicManager()fromdjango.dbimporttransactionwithtransaction.atomic():parent,_=ParentModel.objects.get_or_create(foo='Foo',defaults={children:[{bar:'Bar'},{bar:'Baz'}]})# samples for combining mixins CachedGetOrCreateMixin, AlwaysGetOrCreateMixin, NonAtomicGetOrCreateMixinclassNonAtomicAlwaysQuerySet(NonAtomicGetOrCreateMixin,AlwaysGetOrCreateMixin,models.QuerySet):passclassAlwaysCachedQuerySet(AlwaysGetOrCreateMixin,CachedGetOrCreateMixin,models.QuerySet):passclassPlusQuerySet(CachedGetOrCreateMixin,AlwaysGetOrCreateMixin,NonAtomicGetOrCreateMixin,models.QuerySet):passCachedGetOrCreateMixinuses Django caches. The keys are cached to thedefaultcache, and the objects are cached using aliasmodel._meta.model_name.NOTE:If you have models by the same name in different apps both using
CachedGetOrCreateMixin, this will fail.# settings.py# assuming use of django-connection-url (shameless self-plug)importconnection_urlCACHES={'default':connection_url.config('locmem:///'),'cachedimmutablemodel':connection_url.config('REDIS_URL'),'cachedmutablemodel':connection_url.config('MEMCACHED_URL'),}