| import inspect | |
| import os | |
| from django.conf import settings | |
| from django.core.exceptions import ImproperlyConfigured | |
| from django.utils.importlib import import_module | |
| DEFAULT_DB_ALIAS = 'default' | |
| # Define some exceptions that mirror the PEP249 interface. | |
| # We will rethrow any backend-specific errors using these | |
| # common wrappers | |
| class DatabaseError(Exception): | |
| pass | |
| class IntegrityError(DatabaseError): | |
| pass | |
| def load_backend(backend_name): | |
| try: | |
| module = import_module('.base', 'django.db.backends.%s' % backend_name) | |
| import warnings | |
| warnings.warn( | |
| "Short names for DATABASE_ENGINE are deprecated; prepend with 'django.db.backends.'", | |
| DeprecationWarning | |
| ) | |
| return module | |
| except ImportError, e: | |
| # Look for a fully qualified database backend name | |
| try: | |
| return import_module('.base', backend_name) | |
| except ImportError, e_user: | |
| # The database backend wasn't found. Display a helpful error message | |
| # listing all possible (built-in) database backends. | |
| backend_dir = os.path.join(os.path.dirname(__file__), 'backends') | |
| try: | |
| available_backends = [f for f in os.listdir(backend_dir) | |
| if os.path.isdir(os.path.join(backend_dir, f)) | |
| and not f.startswith('.')] | |
| except EnvironmentError: | |
| available_backends = [] | |
| if backend_name.startswith('django.db.backends.'): | |
| backend_name = backend_name[19:] # See #15621. | |
| if backend_name not in available_backends: | |
| error_msg = ("%r isn't an available database backend. \n" + | |
| "Try using django.db.backends.XXX, where XXX is one of:\n %s\n" + | |
| "Error was: %s") % \ | |
| (backend_name, ", ".join(map(repr, sorted(available_backends))), e_user) | |
| raise ImproperlyConfigured(error_msg) | |
| else: | |
| raise # If there's some other error, this must be an error in Django itself. | |
| class ConnectionDoesNotExist(Exception): | |
| pass | |
| class ConnectionHandler(object): | |
| def __init__(self, databases): | |
| self.databases = databases | |
| self._connections = {} | |
| def ensure_defaults(self, alias): | |
| """ | |
| Puts the defaults into the settings dictionary for a given connection | |
| where no settings is provided. | |
| """ | |
| try: | |
| conn = self.databases[alias] | |
| except KeyError: | |
| raise ConnectionDoesNotExist("The connection %s doesn't exist" % alias) | |
| conn.setdefault('ENGINE', 'django.db.backends.dummy') | |
| if conn['ENGINE'] == 'django.db.backends.' or not conn['ENGINE']: | |
| conn['ENGINE'] = 'django.db.backends.dummy' | |
| conn.setdefault('OPTIONS', {}) | |
| conn.setdefault('TEST_CHARSET', None) | |
| conn.setdefault('TEST_COLLATION', None) | |
| conn.setdefault('TEST_NAME', None) | |
| conn.setdefault('TEST_MIRROR', None) | |
| conn.setdefault('TIME_ZONE', settings.TIME_ZONE) | |
| for setting in ('NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'): | |
| conn.setdefault(setting, '') | |
| def __getitem__(self, alias): | |
| if alias in self._connections: | |
| return self._connections[alias] | |
| self.ensure_defaults(alias) | |
| db = self.databases[alias] | |
| backend = load_backend(db['ENGINE']) | |
| conn = backend.DatabaseWrapper(db, alias) | |
| self._connections[alias] = conn | |
| return conn | |
| def __iter__(self): | |
| return iter(self.databases) | |
| def all(self): | |
| return [self[alias] for alias in self] | |
| class ConnectionRouter(object): | |
| def __init__(self, routers): | |
| self.routers = [] | |
| for r in routers: | |
| if isinstance(r, basestring): | |
| try: | |
| module_name, klass_name = r.rsplit('.', 1) | |
| module = import_module(module_name) | |
| except ImportError, e: | |
| raise ImproperlyConfigured('Error importing database router %s: "%s"' % (klass_name, e)) | |
| try: | |
| router_class = getattr(module, klass_name) | |
| except AttributeError: | |
| raise ImproperlyConfigured('Module "%s" does not define a database router name "%s"' % (module, klass_name)) | |
| else: | |
| router = router_class() | |
| else: | |
| router = r | |
| self.routers.append(router) | |
| def _router_func(action): | |
| def _route_db(self, model, **hints): | |
| chosen_db = None | |
| for router in self.routers: | |
| try: | |
| method = getattr(router, action) | |
| except AttributeError: | |
| # If the router doesn't have a method, skip to the next one. | |
| pass | |
| else: | |
| chosen_db = method(model, **hints) | |
| if chosen_db: | |
| return chosen_db | |
| try: | |
| return hints['instance']._state.db or DEFAULT_DB_ALIAS | |
| except KeyError: | |
| return DEFAULT_DB_ALIAS | |
| return _route_db | |
| db_for_read = _router_func('db_for_read') | |
| db_for_write = _router_func('db_for_write') | |
| def allow_relation(self, obj1, obj2, **hints): | |
| for router in self.routers: | |
| try: | |
| method = router.allow_relation | |
| except AttributeError: | |
| # If the router doesn't have a method, skip to the next one. | |
| pass | |
| else: | |
| allow = method(obj1, obj2, **hints) | |
| if allow is not None: | |
| return allow | |
| return obj1._state.db == obj2._state.db | |
| def allow_syncdb(self, db, model): | |
| for router in self.routers: | |
| try: | |
| method = router.allow_syncdb | |
| except AttributeError: | |
| # If the router doesn't have a method, skip to the next one. | |
| pass | |
| else: | |
| allow = method(db, model) | |
| if allow is not None: | |
| return allow | |
| return True |