1
0
mirror of https://github.com/django/django.git synced 2025-07-05 18:29:11 +00:00

[soc2009/multidb] Implemented a connections object that is responsible for tracking multiple database connections and lazily instantiating them. Also implemneted the DATABASES setting which replaces the various DATABASE_* settings

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@10892 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2009-06-03 01:58:33 +00:00
parent 4cd29f2520
commit bce55b1aa8
15 changed files with 232 additions and 152 deletions

View File

@ -131,6 +131,9 @@ DATABASE_HOST = '' # Set to empty string for localhost. Not used wit
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
DATABASE_OPTIONS = {} # Set to empty dictionary for default.
DATABASES = {
}
# Host for sending e-mail.
EMAIL_HOST = 'localhost'

View File

@ -9,12 +9,17 @@ ADMINS = (
MANAGERS = ADMINS
DATABASE_ENGINE = '' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = '' # Or path to database file if using sqlite3.
DATABASE_USER = '' # Not used with sqlite3.
DATABASE_PASSWORD = '' # Not used with sqlite3.
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
DATABASES = {
'default': {
'DATABASE_ENGINE': '', # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'DATABASE_NAME': '', # Or path to database file if using sqlite3.
'DATABASE_USER': '', # Not used with sqlite3.
'DATABASE_PASSWORD': '', # Not used with sqlite3.
'DATABASE_HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'DATABASE_PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name

View File

@ -1,14 +1,23 @@
from optparse import make_option
from django.core.management.base import LabelCommand
from django.db import connections, transaction, models
class Command(LabelCommand):
help = "Creates the table needed to use the SQL cache backend."
args = "<tablename>"
label = 'tablename'
options_list = LabelCommand.options_list + (
make_option('--database', action='store', dest='database',
default='default', help='Selects what database to install the cache table to.'),
)
requires_model_validation = False
def handle_label(self, tablename, **options):
from django.db import connection, transaction, models
alias = options['alias']
connection = connections[alias]
fields = (
# "key" is a reserved word in MySQL, so use "cache_key" instead.
models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
@ -17,7 +26,7 @@ class Command(LabelCommand):
)
table_output = []
index_output = []
qn = connection.ops.quote_name
qn = connections.ops.quote_name
for f in fields:
field_output = [qn(f.name), f.db_type()]
field_output.append("%sNULL" % (not f.null and "NOT " or ""))

View File

@ -1,44 +1,25 @@
import os
from django.conf import settings
from django.core import signals
from django.core.exceptions import ImproperlyConfigured
from django.db.utils import ConnectionHandler, load_backend
from django.utils.functional import curry
from django.utils.importlib import import_module
__all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError')
if not settings.DATABASE_ENGINE:
settings.DATABASE_ENGINE = 'dummy'
if not settings.DATABASES or 'default' not in settings.DATABASES:
settings.DATABASES['default'] = {
'DATABASE_ENGINE': settings.DATABASE_ENGINE,
'DATABASE_HOST': settings.DATABASE_HOST,
'DATABASE_NAME': settings.DATABASE_NAME,
'DATABASE_OPTIONS': settings.DATABASE_OPTIONS,
'DATABASE_PASSWORD': settings.DATABASE_PASSWORD,
'DATABASE_PORT': settings.DATABASE_PORT,
'DATABASE_USER': settings.DATABASE_USER,
'TIME_ZONE': settings.TIME_ZONE,
}
def load_backend(backend_name):
try:
# Most of the time, the database backend will be one of the official
# backends that ships with Django, so look there first.
return import_module('.base', 'django.db.backends.%s' % backend_name)
except ImportError, e:
# If the import failed, we might be looking for a database backend
# distributed external to Django. So we'll try that next.
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(__path__[0], '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 = []
available_backends.sort()
if backend_name not in available_backends:
error_msg = "%r isn't an available database backend. Available options are: %s\nError was: %s" % \
(backend_name, ", ".join(map(repr, available_backends)), e_user)
raise ImproperlyConfigured(error_msg)
else:
raise # If there's some other error, this must be an error in Django itself.
connections = ConnectionHandler(settings.DATABASES)
backend = load_backend(settings.DATABASE_ENGINE)
# `connection`, `DatabaseError` and `IntegrityError` are convenient aliases
# for backend bits.
@ -47,15 +28,10 @@ backend = load_backend(settings.DATABASE_ENGINE)
# we manually create the dictionary from the settings, passing only the
# settings that the database backends care about. Note that TIME_ZONE is used
# by the PostgreSQL backends.
connection = backend.DatabaseWrapper({
'DATABASE_HOST': settings.DATABASE_HOST,
'DATABASE_NAME': settings.DATABASE_NAME,
'DATABASE_OPTIONS': settings.DATABASE_OPTIONS,
'DATABASE_PASSWORD': settings.DATABASE_PASSWORD,
'DATABASE_PORT': settings.DATABASE_PORT,
'DATABASE_USER': settings.DATABASE_USER,
'TIME_ZONE': settings.TIME_ZONE,
})
# we load all these up for backwards compatibility, you should use
# connections['default'] instead.
connection = connections['default']
backend = load_backend(settings.DATABASE_ENGINE)
DatabaseError = backend.DatabaseError
IntegrityError = backend.IntegrityError

55
django/db/utils.py Normal file
View File

@ -0,0 +1,55 @@
import os
from django.utils.importlib import import_module
def load_backend(backend_name):
try:
# Most of the time, the database backend will be one of the official
# backends that ships with Django, so look there first.
return import_module('.base', 'django.db.backends.%s' % backend_name)
except ImportError, e:
# If the import failed, we might be looking for a database backend
# distributed external to Django. So we'll try that next.
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(__path__[0], '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 = []
available_backends.sort()
if backend_name not in available_backends:
error_msg = "%r isn't an available database backend. Available options are: %s\nError was: %s" % \
(backend_name, ", ".join(map(repr, 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 ConnectionHandler(object):
def __init__(self, databases):
self.databases = databases
self._connections = {}
def check_connection(self, alias):
conn = self.databases[alias]
conn.setdefault('DATABASE_ENGINE', 'dummy')
conn.setdefault('DATABASE_OPTIONS', {})
for setting in ('DATABASE_NAME', 'DATABASE_USER', 'DATABASE_PASSWORD',
'DATABASE_HOST', 'DATABASE_PORT'):
conn.setdefault(setting, '')
def __getitem__(self, alias):
if alias in self._connections:
return self._connections[alias]
self.check_connection(alias)
db = self.databases[alias]
backend = load_backend(db['DATABASE_ENGINE'])
conn = backend.DatabaseWrapper(db)
self._connections[alias] = conn
return conn

View File

@ -130,8 +130,8 @@ There's also a hook for backend-specific SQL data. For example, you can have
separate initial-data files for PostgreSQL and MySQL. For each app, Django
looks for a file called ``<appname>/sql/<modelname>.<backend>.sql``, where
``<appname>`` is your app directory, ``<modelname>`` is the model's name in
lowercase and ``<backend>`` is the value of :setting:`DATABASE_ENGINE` in your
settings file (e.g., ``postgresql``, ``mysql``).
lowercase and ``<backend>`` is the value of ``DATABASE_ENGINE`` for the given
database being set up in your settings file (e.g., ``postgresql``, ``mysql``).
Backend-specific SQL data is executed before non-backend-specific SQL data. For
example, if your app contains the files ``sql/person.sql`` and

View File

@ -18,15 +18,16 @@ Give Django your database parameters
====================================
You'll need to tell Django what your database connection parameters are, and
what the name of the database is. Do that by editing these settings in your
:ref:`settings file <topics-settings>`:
what the name of the database is. Do that by editing the :setting:`DATABASES`
setting and assigning values to the following keys in the ``'default'``
dictionary:
* :setting:`DATABASE_NAME`
* :setting:`DATABASE_ENGINE`
* :setting:`DATABASE_USER`
* :setting:`DATABASE_PASSWORD`
* :setting:`DATABASE_HOST`
* :setting:`DATABASE_PORT`
* ``DATABASE_NAME``
* ``DATABASE_ENGINE``
* ``DATABASE_USER``
* ``DATABASE_PASSWORD``
* ``DATABASE_HOST``
* ``DATABASE_PORT``
Auto-generate the models
========================

View File

@ -752,20 +752,20 @@ To run the tests, ``cd`` to the ``tests/`` directory and type:
./runtests.py --settings=path.to.django.settings
Yes, the unit tests need a settings module, but only for database connection
info, with the ``DATABASE_ENGINE`` setting.
info, with the ``DATABASES`` setting.
If you're using the ``sqlite3`` database backend, no further settings are
needed. A temporary database will be created in memory when running the tests.
If you're using another backend:
* Your :setting:`DATABASE_USER` setting needs to specify an existing user account
for the database engine.
* Your the ``DATABASE_USER`` option for the ``'default'`` datbase setting
needs to specify an existing user account for the database engine.
* The :setting:`DATABASE_NAME` setting must be the name of an existing database to
* The ``DATABASE_NAME`` option must be the name of an existing database to
which the given user has permission to connect. The unit tests will not
touch this database; the test runner creates a new database whose name is
:setting:`DATABASE_NAME` prefixed with ``test_``, and this test database is
``DATABASE_NAME`` prefixed with ``test_``, and this test database is
deleted when the tests are finished. This means your user account needs
permission to execute ``CREATE DATABASE``.

View File

@ -159,13 +159,14 @@ Database setup
--------------
Now, edit :file:`settings.py`. It's a normal Python module with module-level
variables representing Django settings. Change these settings to match your
database's connection parameters:
variables representing Django settings. Change the following keys in the
:setting:`DATABASES` ``'default'`` item to match your databases connection
settings.
* :setting:`DATABASE_ENGINE` -- Either 'postgresql_psycopg2', 'mysql' or
* ``DATABASE_ENGINE`` -- Either 'postgresql_psycopg2', 'mysql' or
'sqlite3'. Other backends are :setting:`also available <DATABASE_ENGINE>`.
* :setting:`DATABASE_NAME` -- The name of your database. If you're using
* ``DATABASE_NAME`` -- The name of your database. If you're using
SQLite, the database will be a file on your computer; in that case,
``DATABASE_NAME`` should be the full absolute path, including filename, of
that file. If the file doesn't exist, it will automatically be created
@ -174,17 +175,17 @@ database's connection parameters:
When specifying the path, always use forward slashes, even on Windows
(e.g. ``C:/homes/user/mysite/sqlite3.db``).
* :setting:`DATABASE_USER` -- Your database username (not used for SQLite).
* ``DATABASE_USER`` -- Your database username (not used for SQLite).
* :setting:`DATABASE_PASSWORD` -- Your database password (not used for
* ``DATABASE_PASSWORD`` -- Your database password (not used for
SQLite).
* :setting:`DATABASE_HOST` -- The host your database is on. Leave this as an
* ``DATABASE_HOST`` -- The host your database is on. Leave this as an
empty string if your database server is on the same physical machine (not
used for SQLite).
If you're new to databases, we recommend simply using SQLite (by setting
:setting:`DATABASE_ENGINE` to ``'sqlite3'``). SQLite is included as part of
``DATABASE_ENGINE`` to ``'sqlite3'``). SQLite is included as part of
Python 2.5 and later, so you won't need to install anything else.
.. note::

View File

@ -53,7 +53,7 @@ transaction, rather than having the transaction extend over multiple
operations. In this case, you can still manually start a transaction if you're
doing something that requires consistency across multiple database operations.
The autocommit behavior is enabled by setting the ``autocommit`` key in the
:setting:`DATABASE_OPTIONS` setting::
``DATABASE_OPTIONS`` part of your database in :setting:`DATABASES`::
DATABASE_OPTIONS = {
"autocommit": True,
@ -69,9 +69,9 @@ objects are changed or none of them are.
This functionality is not the same as the
:ref:`topics-db-transactions-autocommit` decorator. That decorator is a
Django-level implementation that commits automatically after data changing
operations. The feature enabled using the :setting:`DATABASE_OPTIONS`
settings provides autocommit behavior at the database adapter level. It
commits after *every* operation.
operations. The feature enabled using the ``DATABASE_OPTIONS`` option
provides autocommit behavior at the database adapter level. It commits
after *every* operation.
If you are using this feature and performing an operation akin to delete or
updating that requires multiple operations, you are strongly recommended to
@ -234,10 +234,10 @@ Refer to the :ref:`settings documentation <ref-settings>`.
Connection settings are used in this order:
1. :setting:`DATABASE_OPTIONS`.
2. :setting:`DATABASE_NAME`, :setting:`DATABASE_USER`,
:setting:`DATABASE_PASSWORD`, :setting:`DATABASE_HOST`,
:setting:`DATABASE_PORT`
1. ``DATABASE_OPTIONS``.
2. ``DATABASE_NAME``, ``DATABASE_USER``,
``DATABASE_PASSWORD``, ``DATABASE_HOST``,
``DATABASE_PORT``
3. MySQL option files.
In other words, if you set the name of the database in ``DATABASE_OPTIONS``,
@ -247,10 +247,15 @@ anything in a `MySQL option file`_.
Here's a sample configuration which uses a MySQL option file::
# settings.py
DATABASE_ENGINE = "mysql"
DATABASE_OPTIONS = {
DATABASES = {
'default': {
'DATABASE_ENGINE': "mysql",
'DATABASE_OPTIONS': {
'read_default_file': '/path/to/my.cnf',
},
}
}
# my.cnf
[client]
@ -501,25 +506,34 @@ Connecting to the database
Your Django settings.py file should look something like this for Oracle::
DATABASE_ENGINE = 'oracle'
DATABASE_NAME = 'xe'
DATABASE_USER = 'a_user'
DATABASE_PASSWORD = 'a_password'
DATABASE_HOST = ''
DATABASE_PORT = ''
DATABASES = {
'default': {
'DATABASE_ENGINE': 'oracle',
'DATABASE_NAME': 'xe',
'DATABASE_USER': 'a_user',
'DATABASE_PASSWORD': 'a_password',
'DATABASE_HOST': '',
'DATABASE_PORT': '' ,
}
}
If you don't use a ``tnsnames.ora`` file or a similar naming method that
recognizes the SID ("xe" in this example), then fill in both
:setting:`DATABASE_HOST` and :setting:`DATABASE_PORT` like so::
``DATABASE_HOST`` and ``DATABASE_PORT`` like so::
DATABASE_ENGINE = 'oracle'
DATABASE_NAME = 'xe'
DATABASE_USER = 'a_user'
DATABASE_PASSWORD = 'a_password'
DATABASE_HOST = 'dbprod01ned.mycompany.com'
DATABASE_PORT = '1540'
DATABASES = {
'default': {
'DATABASE_ENGINE': 'oracle',
'DATABASE_NAME': 'xe',
'DATABASE_USER': 'a_user',
'DATABASE_PASSWORD': 'a_password',
'DATABASE_HOST': 'dbprod01ned.mycompany.com',
'DATABASE_PORT': '1540',
}
}
You should supply both :setting:`DATABASE_HOST` and :setting:`DATABASE_PORT`, or leave both
You should supply both ``DATABASE_HOST`` and ``DATABASE_PORT``, or leave both
as empty strings.
Tablespace options

View File

@ -144,10 +144,20 @@ Default: ``600``
The default number of seconds to cache a page when the caching middleware or
``cache_page()`` decorator is used.
.. setting:: DATABASE_ENGINE
.. setting:: DATABASES
DATABASES
---------
Default: ``{}`` (Empty dictionary)
This is a dictionary containg the settings for all databases to be used with
Django. It is a nested dictionary who's contents maps aliases to a dictionary
containing the options for an individual database. The following inner options
are used:
DATABASE_ENGINE
---------------
~~~~~~~~~~~~~~~
Default: ``''`` (Empty string)
@ -164,10 +174,8 @@ examples.
.. versionadded:: 1.0
Support for external database backends is new in 1.0.
.. setting:: DATABASE_HOST
DATABASE_HOST
-------------
~~~~~~~~~~~~~
Default: ``''`` (Empty string)
@ -177,7 +185,7 @@ localhost. Not used with SQLite.
If this value starts with a forward slash (``'/'``) and you're using MySQL,
MySQL will connect via a Unix socket to the specified socket. For example::
DATABASE_HOST = '/var/run/mysql'
"DATABASE_HOST": '/var/run/mysql'
If you're using MySQL and this value *doesn't* start with a forward slash, then
this value is assumed to be the host.
@ -187,10 +195,8 @@ for the connection, rather than a network connection to localhost. If you
explicitly need to use a TCP/IP connection on the local machine with
PostgreSQL, specify ``localhost`` here.
.. setting:: DATABASE_NAME
DATABASE_NAME
-------------
~~~~~~~~~~~~~
Default: ``''`` (Empty string)
@ -198,39 +204,31 @@ The name of the database to use. For SQLite, it's the full path to the database
file. When specifying the path, always use forward slashes, even on Windows
(e.g. ``C:/homes/user/mysite/sqlite3.db``).
.. setting:: DATABASE_OPTIONS
DATABASE_OPTIONS
----------------
~~~~~~~~~~~~~~~~
Default: ``{}`` (Empty dictionary)
Extra parameters to use when connecting to the database. Consult backend
module's document for available keywords.
.. setting:: DATABASE_PASSWORD
DATABASE_PASSWORD
-----------------
~~~~~~~~~~~~~~~~~
Default: ``''`` (Empty string)
The password to use when connecting to the database. Not used with SQLite.
.. setting:: DATABASE_PORT
DATABASE_PORT
-------------
~~~~~~~~~~~~~
Default: ``''`` (Empty string)
The port to use when connecting to the database. An empty string means the
default port. Not used with SQLite.
.. setting:: DATABASE_USER
DATABASE_USER
-------------
~~~~~~~~~~~~~
Default: ``''`` (Empty string)

View File

@ -284,7 +284,7 @@ Regardless of whether the tests pass or fail, the test database is destroyed
when all the tests have been executed.
By default this test database gets its name by prepending ``test_`` to the
value of the :setting:`DATABASE_NAME` setting. When using the SQLite database
value of the ``DATABASE_NAME`` setting. When using the SQLite database
engine the tests will by default use an in-memory database (i.e., the database
will be created in memory, bypassing the filesystem entirely!). If you want to
use a different database name, specify the :setting:`TEST_DATABASE_NAME`
@ -292,9 +292,9 @@ setting.
Aside from using a separate database, the test runner will otherwise use all of
the same database settings you have in your settings file:
:setting:`DATABASE_ENGINE`, :setting:`DATABASE_USER`, :setting:`DATABASE_HOST`,
``DATABASE_ENGINE``, ``DATABASE_USER``, ``DATABASE_HOST``,
etc. The test database is created by the user specified by
:setting:`DATABASE_USER`, so you'll need to make sure that the given user
``DATABASE_USER``, so you'll need to make sure that the given user
account has sufficient privileges to create a new database on the system.
.. versionadded:: 1.0

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,17 @@
from django.conf import settings
from django.db import connections
from django.test import TestCase
class DatabaseSettingTestCase(TestCase):
def setUp(self):
settings.DATABASES['__test_db'] = {
'DATABASE_ENGINE': 'sqlite3',
'DATABASE_NAME': ':memory:',
}
def tearDown(self):
del settings.DATABASES['__test_db']
def test_db_connection(self):
connections['default'].cursor()
connections['__test_db'].cursor()