Ticket #5415: db_signal_r6979.patch

File db_signal_r6979.patch, 7.7 KB (added by gav, 8 years ago)

Changes to DB signals against r6979 in response to Malcolm's requests.

  • django/db/__init__.py

     
    11import os
    22from django.conf import settings
    3 from django.core import signals
     3from django.core import signals as core_signals
    44from django.core.exceptions import ImproperlyConfigured
    55from django.dispatch import dispatcher
    66from django.utils.functional import curry
     
    5454
    5555# Register an event that closes the database connection
    5656# when a Django request is finished.
    57 dispatcher.connect(connection.close, signal=signals.request_finished)
     57dispatcher.connect(connection.close, signal=core_signals.request_finished)
    5858
    5959# Register an event that resets connection.queries
    6060# when a Django request is started.
    6161def reset_queries():
    6262    connection.queries = []
    63 dispatcher.connect(reset_queries, signal=signals.request_started)
     63dispatcher.connect(reset_queries, signal=core_signals.request_started)
    6464
    6565# Register an event that rolls back the connection
    6666# when a Django request has an exception.
    6767def _rollback_on_exception():
    6868    from django.db import transaction
    6969    transaction.rollback_unless_managed()
    70 dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception)
     70dispatcher.connect(_rollback_on_exception, signal=core_signals.got_request_exception)
  • django/db/signals.py

     
     1query_execute = object()
  • django/db/backends/util.py

     
    11import datetime
     2from django.db import signals
     3from django.dispatch import dispatcher
    24import md5
    35from time import time
    46
     
    4143        else:
    4244            return getattr(self.cursor, attr)
    4345
     46    def __iter__(self):
     47        return iter(self.cursor)
     48
     49
     50class CursorSignalWrapper(object):
     51    def __init__(self, cursor, db):
     52        self.cursor = cursor
     53        self.db = db # Instance of a BaseDatabaseWrapper subclass
     54
     55    def execute(self, sql, params=()):
     56        start = time()
     57        try:
     58            return self.cursor.execute(sql, params)
     59        finally:
     60            stop = time()
     61            dispatcher.send(signal=signals.query_execute, sender=self, sql=sql, params=params, time=stop-start)
     62
     63    def executemany(self, sql, param_list):
     64        start = time()
     65        try:
     66            return self.cursor.executemany(sql, param_list)
     67        finally:
     68            stop = time()
     69            # XXX: Should this be a special signal that only gets called once for efficiency?
     70            for params in param_list:
     71                dispatcher.send(signal=signals.query_execute, sender=self, sql=sql, params=params, time=stop-start)
     72
     73    def __getattr__(self, attr):
     74        if attr in self.__dict__:
     75            return self.__dict__[attr]
     76        else:
     77            return getattr(self.cursor, attr)
     78
     79    def __iter__(self):
     80        return iter(self.cursor)
     81
     82
    4483###############################################
    4584# Converters from database (string) to Python #
    4685###############################################
  • django/db/backends/__init__.py

     
    1414        self.connection = None
    1515        self.queries = []
    1616        self.options = kwargs
     17        self.use_signal_cursor = None
    1718
    1819    def _commit(self):
    1920        if self.connection is not None:
     
    3233        from django.conf import settings
    3334        cursor = self._cursor(settings)
    3435        if settings.DEBUG:
    35             return self.make_debug_cursor(cursor)
     36            return self.make_signal_cursor(self.make_debug_cursor(cursor))
     37        elif self.use_signal_cursor:
     38            return self.make_signal_cursor(cursor)
    3639        return cursor
    3740
    3841    def make_debug_cursor(self, cursor):
    3942        from django.db.backends import util
    4043        return util.CursorDebugWrapper(cursor, self)
     44   
     45    def make_signal_cursor(self, cursor):
     46        from django.db.backends import util
     47        return util.CursorSignalWrapper(cursor, self)
    4148
     49
    4250class BaseDatabaseFeatures(object):
    4351    allows_group_by_ordinal = True
    4452    allows_unique_and_pk = True
  • tests/modeltests/select_related/models.py

     
    77the select-related behavior will traverse.
    88"""
    99
    10 from django.db import models
     10from django.db import models, signals
     11from django.dispatch import dispatcher
    1112
    1213# Who remembers high school biology?
    1314
     
    7576        obj.save()
    7677        parent = obj
    7778
     79class QueryCounter(object):
     80    counter = 0
     81
     82    @staticmethod
     83    def add():
     84        QueryCounter.counter += 1
     85       
     86    @staticmethod
     87    def clear():
     88        QueryCounter.counter = 0
     89
     90
     91def track_query(sender, *args, **kwargs):
     92    QueryCounter.add()
     93dispatcher.connect(track_query, signal=signals.query_execute, weak=False)
     94
    7895__test__ = {'API_TESTS':"""
    7996
    8097# Set up.
     
    92109
    93110# Normally, accessing FKs doesn't fill in related objects:
    94111>>> db.reset_queries()
     112>>> QueryCounter.clear()
    95113>>> fly = Species.objects.get(name="melanogaster")
    96114>>> fly.genus.family.order.klass.phylum.kingdom.domain
    97115<Domain: Eukaryota>
    98116>>> len(db.connection.queries)
    991178
     118>>> QueryCounter.counter
     1198
    100120
    101121# However, a select_related() call will fill in those related objects without any extra queries:
    102122>>> db.reset_queries()
     123>>> QueryCounter.clear()
    103124>>> person = Species.objects.select_related().get(name="sapiens")
    104125>>> person.genus.family.order.klass.phylum.kingdom.domain
    105126<Domain: Eukaryota>
    106127>>> len(db.connection.queries)
    1071281
     129>>> QueryCounter.counter
     1301
    108131
    109132# select_related() also of course applies to entire lists, not just items.
    110133# Without select_related()
    111134>>> db.reset_queries()
     135>>> QueryCounter.clear()
    112136>>> world = Species.objects.all()
    113137>>> [o.genus.family for o in world]
    114138[<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>]
    115139>>> len(db.connection.queries)
    1161409
     141>>> QueryCounter.counter
     1429
    117143
    118144# With select_related():
    119145>>> db.reset_queries()
     146>>> QueryCounter.clear()
    120147>>> world = Species.objects.all().select_related()
    121148>>> [o.genus.family for o in world]
    122149[<Family: Drosophilidae>, <Family: Hominidae>, <Family: Fabaceae>, <Family: Amanitacae>]
    123150>>> len(db.connection.queries)
    1241511
     152>>> QueryCounter.counter
     1531
    125154
    126155# The "depth" argument to select_related() will stop the descent at a particular level:
    127156>>> db.reset_queries()
     157>>> QueryCounter.clear()
    128158>>> pea = Species.objects.select_related(depth=1).get(name="sativum")
    129159>>> pea.genus.family.order.klass.phylum.kingdom.domain
    130160<Domain: Eukaryota>
     
    132162# Notice: one few query than above because of depth=1
    133163>>> len(db.connection.queries)
    1341647
     165>>> QueryCounter.counter
     1667
    135167
    136168>>> db.reset_queries()
     169>>> QueryCounter.clear()
    137170>>> pea = Species.objects.select_related(depth=5).get(name="sativum")
    138171>>> pea.genus.family.order.klass.phylum.kingdom.domain
    139172<Domain: Eukaryota>
    140173>>> len(db.connection.queries)
    1411743
     175>>> QueryCounter.counter
     1763
    142177
    143178>>> db.reset_queries()
     179>>> QueryCounter.clear()
    144180>>> world = Species.objects.all().select_related(depth=2)
    145181>>> [o.genus.family.order for o in world]
    146182[<Order: Diptera>, <Order: Primates>, <Order: Fabales>, <Order: Agaricales>]
    147183>>> len(db.connection.queries)
    1481845
     185>>> QueryCounter.counter
     1865
    149187
    150188# Reset DEBUG to where we found it.
    151189>>> settings.DEBUG = False
Back to Top