Author: jpellerin
Date: 2006-07-18 21:28:24 -0500 (Tue, 18 Jul 2006)
New Revision: 3361

Modified:
   django/branches/multiple-db-support/django/db/models/manager.py
Log:
[multi-db] Added ConnectionInfoDescriptor and attached it as db attribute in 
Manager class.

Modified: django/branches/multiple-db-support/django/db/models/manager.py
===================================================================
--- django/branches/multiple-db-support/django/db/models/manager.py     
2006-07-19 02:09:26 UTC (rev 3360)
+++ django/branches/multiple-db-support/django/db/models/manager.py     
2006-07-19 02:28:24 UTC (rev 3361)
@@ -1,11 +1,20 @@
+from django import core
 from django.utils.functional import curry
-from django.db import backend, connection
+from django.core.exceptions import ImproperlyConfigured
+from django.db import connections, _default
 from django.db.models.query import QuerySet
 from django.dispatch import dispatcher
 from django.db.models import signals, get_apps, get_models
 from django.db.models.fields import FieldDoesNotExist
 from django.utils.datastructures import SortedDict
 
+try:
+    # Only exists in Python 2.4+
+    from threading import local
+except ImportError:
+    # Import copy of _thread_local.py from Python 2.4
+    from django.utils._threading_local import local
+
 # Size of each "chunk" for get_iterator calls.
 # Larger values are slightly faster at the expense of more storage space.
 GET_ITERATOR_CHUNK_SIZE = 100
@@ -25,10 +34,79 @@
         cls.add_to_class('objects', Manager())
 dispatcher.connect(ensure_default_manager, signal=signals.class_prepared)
 
+class ConnectionInfoDescriptor(object):
+    """Descriptor used to access database connection information from a
+    manager or other connection holder. Keeps a thread-local cache of
+    connections per instance, and always returns the same connection for an
+    instance in particular thread during a particular request.
+
+    Any object that includes an attribute ``model`` that holds a model class
+    can use this descriptor to manage connections.
+    """
+    
+    def __init__(self):
+        self.cnx = local()
+        self.cnx.cache = {}
+        
+    def __get__(self, instance, type=None):
+        if instance is None:
+            raise AttributeError, \
+                "ConnectionInfo is accessible only through an instance"
+        instance_connection = self.cnx.cache.get(instance, None)
+        if instance_connection is None:
+            instance_connection = self.get_connection(instance)
+            def reset():
+                self.reset(instance)
+            dispatcher.connect(reset, signal=core.signals.request_finished)
+            self.cnx.cache[instance] = instance_connection
+        return instance_connection
+
+    def __set__(self, instance, value):
+        self.cnx.cache[instance] = instance_connection
+
+    def __delete__(self, instance):
+        self.reset(instance)
+
+    def get_connection(self, instance):
+        from django.conf import settings
+        app = instance.model._meta.app_label
+        model = instance.model.__name__
+        app_model = "%s.%s" % (app, model)
+
+        # Quick exit if no OTHER_DATABASES defined
+        if (not hasattr(settings, 'OTHER_DATABASES')
+            or not settings.OTHER_DATABASES):
+            return connections[_default]
+        # Look in MODELS for the best match: app_label.Model. If that isn't
+        # found, take just app_label. If nothing is found, use the default
+        maybe = None
+        for name, db_def in settings.OTHER_DATABASES.items():
+            if not 'MODELS' in db_def:
+                continue
+            mods = db_def['MODELS']
+            # Can't get a better match than this
+            if app_model in mods:
+                return connections[name]
+            elif app in mods:
+                if maybe is not None:
+                    raise ImproperlyConfigured, \
+                        "App %s appears in more than one OTHER_DATABASES " \
+                        "setting (%s and %s)" % (maybe, name)
+                maybe = name
+        if maybe:
+            return connections[name]
+        # No named connection for this model; use the default
+        return connections[_default]            
+            
+    def reset(self, instance):
+        self.cnx.cache[instance] = None
+
+
 class Manager(object):
     # Tracks each time a Manager instance is created. Used to retain order.
     creation_counter = 0
-
+    db = ConnectionInfoDescriptor()
+    
     def __init__(self):
         super(Manager, self).__init__()
         # Increase the creation counter, and save our local copy.
@@ -44,7 +122,7 @@
             or self.creation_counter < model._default_manager.creation_counter 
\
             or model._default_manager.model != model:
             model._default_manager = self
-
+        
     #######################
     # PROXIES TO QUERYSET #
     #######################
@@ -119,8 +197,7 @@
         such as foreign key constraints for tables that don't exist at
         install time.)
         """
-        creator = self.model._meta.connection_info.get_creation_module()
-        builder = creator.builder            
+        builder = self.db.get_creation_module().builder
         run, pending = builder.get_create_table(self.model)
         run += builder.get_create_indexes(self.model)
         if initial_data:
@@ -158,9 +235,8 @@
     def get_table_list(self):
         """Get list of tables accessible via my model's connection.
         """
-        info = self.model._meta.connection_info
-        builder = info.get_creation_module.builder()
-        return builder.get_table_list(info)
+        builder = self.db.get_creation_module().builder
+        return builder.get_table_list(self.db)
     
 class ManagerDescriptor(object):
     # This class ensures managers aren't accessible via model instances.
@@ -172,3 +248,4 @@
         if instance != None:
             raise AttributeError, "Manager isn't accessible via %s instances" 
% type.__name__
         return self.manager
+


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates
-~----------~----~----~----~------~----~------~--~---

Reply via email to