Statistics
| Branch: | Tag: | Revision:

root / snf-django-lib / snf_django / lib / db / managers.py @ 0f66865f

History | View | Annotate | Download (3.7 kB)

1 ea1e5d9f Giorgos Korfiatis
# Copyright 2012, 2013 GRNET S.A. All rights reserved.
2 ee45eb81 Giorgos Korfiatis
#
3 ee45eb81 Giorgos Korfiatis
# Redistribution and use in source and binary forms, with or without
4 ee45eb81 Giorgos Korfiatis
# modification, are permitted provided that the following conditions
5 ee45eb81 Giorgos Korfiatis
# are met:
6 ee45eb81 Giorgos Korfiatis
#
7 ee45eb81 Giorgos Korfiatis
#   1. Redistributions of source code must retain the above copyright
8 ee45eb81 Giorgos Korfiatis
#      notice, this list of conditions and the following disclaimer.
9 ee45eb81 Giorgos Korfiatis
#
10 ee45eb81 Giorgos Korfiatis
#  2. Redistributions in binary form must reproduce the above copyright
11 ee45eb81 Giorgos Korfiatis
#     notice, this list of conditions and the following disclaimer in the
12 ee45eb81 Giorgos Korfiatis
#     documentation and/or other materials provided with the distribution.
13 ee45eb81 Giorgos Korfiatis
#
14 ee45eb81 Giorgos Korfiatis
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 ee45eb81 Giorgos Korfiatis
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ee45eb81 Giorgos Korfiatis
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ee45eb81 Giorgos Korfiatis
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 ee45eb81 Giorgos Korfiatis
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 ee45eb81 Giorgos Korfiatis
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 ee45eb81 Giorgos Korfiatis
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 ee45eb81 Giorgos Korfiatis
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 ee45eb81 Giorgos Korfiatis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 ee45eb81 Giorgos Korfiatis
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 ee45eb81 Giorgos Korfiatis
# SUCH DAMAGE.
25 ee45eb81 Giorgos Korfiatis
#
26 ee45eb81 Giorgos Korfiatis
# The views and conclusions contained in the software and documentation are
27 ee45eb81 Giorgos Korfiatis
# those of the authors and should not be interpreted as representing official
28 ee45eb81 Giorgos Korfiatis
# policies, either expressed or implied, of GRNET S.A.
29 ee45eb81 Giorgos Korfiatis
30 ee45eb81 Giorgos Korfiatis
from django.db import connections
31 ee45eb81 Giorgos Korfiatis
from django.db.models import Manager
32 1fd14d3d Giorgos Korfiatis
from django.db.models.query import QuerySet, EmptyQuerySet
33 1fd14d3d Giorgos Korfiatis
from django.db.models.sql.datastructures import EmptyResultSet
34 ee45eb81 Giorgos Korfiatis
35 ee45eb81 Giorgos Korfiatis
class ForUpdateManager(Manager):
36 ee45eb81 Giorgos Korfiatis
    """ Model manager implementing SELECT .. FOR UPDATE statement
37 ee45eb81 Giorgos Korfiatis

38 ee45eb81 Giorgos Korfiatis
        This manager implements select_for_update() method in order to use
39 ee45eb81 Giorgos Korfiatis
        row-level locking in the database and guarantee exclusive access, since
40 ee45eb81 Giorgos Korfiatis
        this method is only implemented in Django>=1.4.
41 ee45eb81 Giorgos Korfiatis

42 ee45eb81 Giorgos Korfiatis
        Non-blocking reads are not implemented, and each query including a row
43 ee45eb81 Giorgos Korfiatis
        that is locked by another transaction will block until the lock is
44 ee45eb81 Giorgos Korfiatis
        released. Also care must be taken in order to avoid deadlocks or retry
45 ee45eb81 Giorgos Korfiatis
        transactions that abort due to deadlocks.
46 ee45eb81 Giorgos Korfiatis

47 ee45eb81 Giorgos Korfiatis
        Example:
48 ea1e5d9f Giorgos Korfiatis
            networks = Network.objects.filter(public=True).select_for_update()
49 ee45eb81 Giorgos Korfiatis

50 ee45eb81 Giorgos Korfiatis
    """
51 ee45eb81 Giorgos Korfiatis
52 ea1e5d9f Giorgos Korfiatis
    def get_query_set(self):
53 ea1e5d9f Giorgos Korfiatis
        return ForUpdateQuerySet(self.model, using=self._db)
54 ee45eb81 Giorgos Korfiatis
55 ea1e5d9f Giorgos Korfiatis
    def get_for_update(self, *args, **kwargs):
56 ea1e5d9f Giorgos Korfiatis
        query = for_update(self.filter(*args, **kwargs))
57 ee45eb81 Giorgos Korfiatis
        query = list(query)
58 ee45eb81 Giorgos Korfiatis
        num = len(query)
59 ee45eb81 Giorgos Korfiatis
        if num == 1:
60 ee45eb81 Giorgos Korfiatis
            return query[0]
61 ee45eb81 Giorgos Korfiatis
        if not num:
62 ee45eb81 Giorgos Korfiatis
            raise self.model.DoesNotExist(
63 ee45eb81 Giorgos Korfiatis
                    "%s matching query does not exist. "
64 ee45eb81 Giorgos Korfiatis
                    "Lookup parameters were %s" %
65 ee45eb81 Giorgos Korfiatis
                    (self.model._meta.object_name, kwargs))
66 ee45eb81 Giorgos Korfiatis
        raise self.model.MultipleObjectsReturned(
67 ee45eb81 Giorgos Korfiatis
            "get() returned more than one %s -- it returned %s! "
68 ee45eb81 Giorgos Korfiatis
            "Lookup parameters were %s" %
69 ee45eb81 Giorgos Korfiatis
            (self.model._meta.object_name, num, kwargs))
70 ee45eb81 Giorgos Korfiatis
71 ea1e5d9f Giorgos Korfiatis
72 ea1e5d9f Giorgos Korfiatis
class ForUpdateQuerySet(QuerySet):
73 ea1e5d9f Giorgos Korfiatis
74 ea1e5d9f Giorgos Korfiatis
    def select_for_update(self):
75 ea1e5d9f Giorgos Korfiatis
        return for_update(self)
76 ee45eb81 Giorgos Korfiatis
77 ee45eb81 Giorgos Korfiatis
78 ee45eb81 Giorgos Korfiatis
def for_update(query):
79 ee45eb81 Giorgos Korfiatis
    """ Rewrite query using SELECT .. FOR UPDATE.
80 ee45eb81 Giorgos Korfiatis

81 ee45eb81 Giorgos Korfiatis
    """
82 ee45eb81 Giorgos Korfiatis
    if 'sqlite' in connections[query.db].settings_dict['ENGINE'].lower():
83 ee45eb81 Giorgos Korfiatis
        # SQLite  does not support FOR UPDATE
84 ee45eb81 Giorgos Korfiatis
        return query
85 1fd14d3d Giorgos Korfiatis
    try:
86 1fd14d3d Giorgos Korfiatis
        sql, params = query.query.get_compiler(query.db).as_sql()
87 1fd14d3d Giorgos Korfiatis
    except EmptyResultSet:
88 1fd14d3d Giorgos Korfiatis
        return EmptyQuerySet()
89 ee45eb81 Giorgos Korfiatis
    return query.model._default_manager.raw(sql.rstrip() + ' FOR UPDATE',
90 ee45eb81 Giorgos Korfiatis
                                            params)