Skip to main content

Mixins to cache, force and/or use non-atomic Model.objects.get_or_create() calls.

Project description

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 made
  • Allow non-atomic get_or_create() to avoid nested transaction points

Quick start

# models.py

from django.db import models
from django.core.cache import caches
from getorcreateplus import CachedGetOrCreateMixin, AlwaysGetOrCreateMixin, NonAtomicGetOrCreateMixin


# sample for CachedGetOrCreateMixin (see below for changes to be made in settings.py)
class CachedQuerySet(CachedGetOrCreateMixin, models.QuerySet):
    pass

class CachedManager(models.manager.BaseManager.from_queryset(CachedQuerySet)):
    use_for_related_fields = True

class CachedImmutableModel(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 db
obj2, _ = CachedImmutableModel.objects.get_or_create(foo='FooBar') # hits cache


class CachedMutableObject(models.Model):
    foo = models.CharField(max_length=8)
    bar = models.IntegerField(null=True)

    objects = CachedManager()

    def save(self, **kwargs):
        cache = caches[self._meta.model_name]
        cache.delete(self.pk)
        return super(CachedMutableObject, self).save(**kwargs)


obj1, _ = CachedMutableModel.objects.get_or_create(foo='FooBar') # fetches from db
obj1.bar = 1
obj1.save() # invalidate object cache
obj2, _ = CachedMutableModel.objects.get_or_create(foo='FooBar') # fetches from db
obj3, _ = CachedMutableModel.objects.get_or_create(foo='FooBar') # hits cache


# sample for NonAtomicGetOrCreateMixin
class NonAtomicQuerySet(NonAtomicGetOrCreateMixin, models.QuerySet):
    pass

class NonAtomicManager(models.manager.BaseManager.from_queryset(NonAtomicQuerySet)):
    use_for_related_fields = True

class ParentQuerySet(NonAtomicQuerySet):
    def create(self, **kwargs):
       children = kwargs.pop('children', [])
       parent = super(ParentQuerySet, self).create(**kwargs)
       for child in children:
           parent.children.get_or_create(**child)
       return parent

class ParentManager(models.manager.BaseManager.from_queryset(ParentQuerySet)):
    use_for_related_fields = True

class ParentModel(models.Model):
    foo = models.CharField(max_length=8)

    objects = ParentManager()

class ChildModel(models.Model):
    parent = models.ForeignKey(Parent, related_name='children')
    bar = models.CharField(max_length=8)

    objects = NonAtomicManager()


from django.db import transaction

with transaction.atomic():
    parent, _ = ParentModel.objects.get_or_create(foo='Foo', defaults={children: [{bar: 'Bar'}, {bar: 'Baz'}]})


# samples for combining mixins CachedGetOrCreateMixin, AlwaysGetOrCreateMixin, NonAtomicGetOrCreateMixin
class NonAtomicAlwaysQuerySet(NonAtomicGetOrCreateMixin, AlwaysGetOrCreateMixin, models.QuerySet):
    pass

class AlwaysCachedQuerySet(AlwaysGetOrCreateMixin, CachedGetOrCreateMixin, models.QuerySet):
    pass

class PlusQuerySet(CachedGetOrCreateMixin, AlwaysGetOrCreateMixin, NonAtomicGetOrCreateMixin, models.QuerySet):
    pass

CachedGetOrCreateMixin uses Django caches. The keys are cached to the default cache, and the objects are cached using alias model._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)

import connection_url

CACHES = {
    'default': connection_url.config('locmem:///'),
    'cachedimmutablemodel': connection_url.config('REDIS_URL'),
    'cachedmutablemodel': connection_url.config('MEMCACHED_URL'),
}

Project details


Release history Release notifications

This version
History Node

0.1.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Filename, size & hash SHA256 hash help File type Python version Upload date
django_getorcreateplus-0.1.0-py2-none-any.whl (5.9 kB) Copy SHA256 hash SHA256 Wheel 2.7 Nov 22, 2016

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging CloudAMQP CloudAMQP RabbitMQ AWS AWS Cloud computing Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page