Last active
December 26, 2015 14:29
-
-
Save renskiy/7166189 to your computer and use it in GitHub Desktop.
select_for_update() for Django < 1.4. Tested on Django 1.3.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Adds `select_for_update()` method to Django QuerySet (Django < 1.4). | |
Usage: | |
class MyModel(models.Model): | |
if not hasattr(models.Manager, 'select_for_update'): | |
objects = ForUpdateManager() | |
# model definition | |
""" | |
__author__ = "Renskiy <[email protected]>" | |
from functools import wraps | |
from django.conf import settings | |
from django.db import models | |
from django.db.models.query import QuerySet | |
from django.db.models.sql import Query | |
def for_update(as_sql): | |
""" | |
Decorator: for_update | |
Used to decorate `as_sql` method of Django SQLCompiler, | |
adds " FOR UPDATE" to the end of result of decorated method. | |
""" | |
@wraps(for_update) | |
def _wrapper(compiler, *args, **kwargs): | |
sql, params = as_sql(compiler, *args, **kwargs) | |
if getattr(compiler.query, 'select_for_update', False): | |
sql += ' FOR UPDATE' | |
return sql, params | |
return _wrapper | |
class ForUpdateQuery(Query): | |
""" | |
Class: ForUpdateQuery | |
Query which adds support of `select_for_update()`. | |
""" | |
def __init__(self, *args, **kwargs): | |
super(ForUpdateQuery, self).__init__(*args, **kwargs) | |
self.select_for_update = False | |
def get_compiler(self, *args, **kwargs): | |
compiler = super(ForUpdateQuery, self).get_compiler(*args, **kwargs) | |
as_sql = getattr(compiler.__class__, 'as_sql') | |
if as_sql.__name__ != 'for_update': | |
# decorate class method only if it's not decorated yet | |
decorated_as_sql = for_update(as_sql) | |
setattr(compiler.__class__, 'as_sql', decorated_as_sql) | |
return compiler | |
def clone(self, *args, **kwargs): | |
kwargs.update(select_for_update=self.select_for_update) | |
return super(ForUpdateQuery, self).clone(*args, **kwargs) | |
class ForUpdateQuerySet(QuerySet): | |
""" | |
Class: ForUpdateQuerySet | |
Special query class for replacing Django's standard one, adds select_for_update() method | |
""" | |
def select_for_update(self): | |
db_engine = settings.DATABASES.get(self.db, {}).get('ENGINE', '').lower() | |
if 'sqlite' in db_engine: # sqlite doesn't support "FOR UPDATE" | |
return self | |
query_set = self._clone() | |
query_set.query._for_write = True | |
query_set.query.select_for_update = True | |
return query_set | |
class ForUpdateManager(models.Manager): | |
""" | |
Class: ForUpdateManager | |
Special model manager for replacing Django's standard one, adds select_for_update() method | |
""" | |
def get_query_set(self): | |
query = ForUpdateQuery(self.model) | |
return ForUpdateQuerySet(self.model, using=self._db, query=query) | |
def select_for_update(self): | |
return self.get_query_set().select_for_update() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment