Edited docs/topics/db/multi-db.txt
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12122 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
4a6c746977
commit
bf2283d47d
|
@ -1,7 +1,7 @@
|
||||||
.. _topics-db-multi-db:
|
.. _topics-db-multi-db:
|
||||||
|
|
||||||
==================
|
==================
|
||||||
Multiple Databases
|
Multiple databases
|
||||||
==================
|
==================
|
||||||
|
|
||||||
.. versionadded:: 1.2
|
.. versionadded:: 1.2
|
||||||
|
@ -9,7 +9,7 @@ Multiple Databases
|
||||||
This topic guide describes Django's support for interacting with multiple
|
This topic guide describes Django's support for interacting with multiple
|
||||||
databases. Most of the rest of Django's documentation assumes you are
|
databases. Most of the rest of Django's documentation assumes you are
|
||||||
interacting with a single database. If you want to interact with multiple
|
interacting with a single database. If you want to interact with multiple
|
||||||
databases, some additional steps must be taken.
|
databases, you'll need to take some additional steps.
|
||||||
|
|
||||||
Defining your databases
|
Defining your databases
|
||||||
=======================
|
=======================
|
||||||
|
@ -23,11 +23,11 @@ the inner dictionaries are described fully in the :setting:`DATABASES`
|
||||||
documentation.
|
documentation.
|
||||||
|
|
||||||
Regardless of how many databases you have, you *must* have a database
|
Regardless of how many databases you have, you *must* have a database
|
||||||
named ``'default'``. Any additional databases you have can have
|
named ``'default'``. Any additional databases can have whatever alias
|
||||||
whatever alias you choose.
|
you choose.
|
||||||
|
|
||||||
The following is an example ``settings.py`` snippet defining two
|
The following is an example ``settings.py`` snippet defining two
|
||||||
databases - a default Postgres database, and a MySQL database called
|
databases -- a default PostgreSQL database and a MySQL database called
|
||||||
``users``:
|
``users``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -48,7 +48,7 @@ databases - a default Postgres database, and a MySQL database called
|
||||||
}
|
}
|
||||||
|
|
||||||
If you attempt to access a database that you haven't defined in your
|
If you attempt to access a database that you haven't defined in your
|
||||||
:setting:`DATABASES` setting then Django will raise a
|
:setting:`DATABASES` setting, Django will raise a
|
||||||
``django.db.utils.ConnectionDoesNotExist`` exception.
|
``django.db.utils.ConnectionDoesNotExist`` exception.
|
||||||
|
|
||||||
Synchronizing your databases
|
Synchronizing your databases
|
||||||
|
@ -57,7 +57,7 @@ Synchronizing your databases
|
||||||
The :djadmin:`syncdb` management command operates on one database at a
|
The :djadmin:`syncdb` management command operates on one database at a
|
||||||
time. By default, it operates on the ``default`` database, but by
|
time. By default, it operates on the ``default`` database, but by
|
||||||
providing a :djadminopt:`--database` argument, you can tell syncdb to
|
providing a :djadminopt:`--database` argument, you can tell syncdb to
|
||||||
synchronize a different database. So - to synchronize all models onto
|
synchronize a different database. So, to synchronize all models onto
|
||||||
all databases in our example, you would need to call::
|
all databases in our example, you would need to call::
|
||||||
|
|
||||||
$ ./manage.py syncdb
|
$ ./manage.py syncdb
|
||||||
|
@ -66,67 +66,74 @@ all databases in our example, you would need to call::
|
||||||
If you don't want every application to be synchronized onto a
|
If you don't want every application to be synchronized onto a
|
||||||
particular database. you can specify the :djadminopt:`--exclude`
|
particular database. you can specify the :djadminopt:`--exclude`
|
||||||
argument to :djadmin:`syncdb`. The :djadminopt:`--exclude` option
|
argument to :djadmin:`syncdb`. The :djadminopt:`--exclude` option
|
||||||
allows you to prevent a specific application or applications from
|
lets you prevent a specific application or applications from
|
||||||
being synchronized. For example, if you don't want the ``sales``
|
being synchronized. For example, if you don't want the ``sales``
|
||||||
application to be on the ``users`` database, you could run::
|
application to be in the ``users`` database, you could run::
|
||||||
|
|
||||||
$ ./manage.py syncdb --database=users --exclude=sales
|
$ ./manage.py syncdb --database=users --exclude=sales
|
||||||
|
|
||||||
Alternatively, if you want fine grained control of synchronization,
|
Alternatively, if you want fine-grained control of synchronization,
|
||||||
you can pipe all or part of the output of :djadmin:`sqlall` for a
|
you can pipe all or part of the output of :djadmin:`sqlall` for a
|
||||||
particular application directly into your database prompt.
|
particular application directly into your database prompt, like this::
|
||||||
|
|
||||||
|
$ ./manage.py sqlall sales | ./manage.py dbshell
|
||||||
|
|
||||||
Using other management commands
|
Using other management commands
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
The other ``django-admin.py`` commands that interact with the database
|
The other ``django-admin.py`` commands that interact with the database
|
||||||
operate in the same way as :djadmin:`syncdb` -- they only ever operate
|
operate in the same way as :djadmin:`syncdb` -- they only ever operate
|
||||||
on one database at a time, using the :djadminopt:`--database` to control
|
on one database at a time, using :djadminopt:`--database` to control
|
||||||
the database that is used.
|
the database used.
|
||||||
|
|
||||||
Selecting a database for a ``QuerySet``
|
Selecting a database for a ``QuerySet``
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
It is possible to select the database for a ``QuerySet`` at any point
|
You can select the database for a ``QuerySet`` at any point in the ``QuerySet``
|
||||||
during it's construction. To choose the database that a query will be
|
"chain." Just call ``using()`` on the ``QuerySet`` to get another ``QuerySet``
|
||||||
preformed against simply call the ``using()`` method on the
|
that uses the specified database.
|
||||||
``QuerySet``. ``using()`` takes a single argument: the alias of the
|
|
||||||
database on which you want to run the query. For example:
|
``using()`` takes a single argument: the alias of the database on which you
|
||||||
|
want to run the query. For example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
# This will run on the 'default' database...
|
# This will run on the 'default' database.
|
||||||
>>> Author.objects.all()
|
>>> Author.objects.all()
|
||||||
# So will this...
|
|
||||||
|
# So will this.
|
||||||
>>> Author.objects.using('default').all()
|
>>> Author.objects.using('default').all()
|
||||||
# This will run on the 'other' database
|
|
||||||
|
# This will run on the 'other' database.
|
||||||
>>> Author.objects.using('other').all()
|
>>> Author.objects.using('other').all()
|
||||||
|
|
||||||
Select a database to save to
|
Selecting a database for ``save()``
|
||||||
============================
|
===================================
|
||||||
|
|
||||||
To choose what database to save a model to, provide a ``using`` keyword
|
Use the ``using`` keyword to ``Model.save()`` to specify to which database the
|
||||||
argument to ``Model.save()``. For example if you had a user model that you
|
data should be saved.
|
||||||
wanted to save to the ``'legacy_users'`` database you would save the user
|
|
||||||
by calling::
|
|
||||||
|
|
||||||
>>> user_obj.save(using='legacy_users')
|
For example, to save an object to the ``legacy_users`` database, you'd use this::
|
||||||
|
|
||||||
|
>>> my_object.save(using='legacy_users')
|
||||||
|
|
||||||
|
If you don't specify ``using``, the ``save()`` method will always save into the
|
||||||
|
default database.
|
||||||
|
|
||||||
Moving an object from one database to another
|
Moving an object from one database to another
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
If you have saved an instance to one database, it might be tempting to use
|
If you've saved an instance to one database, it might be tempting to use
|
||||||
``save(using=...)`` as a way to migrate the instance to a new database. However,
|
``save(using=...)`` as a way to migrate the instance to a new database. However,
|
||||||
if you don't take appropriate steps, this could have some unexpected consequences.
|
if you don't take appropriate steps, this could have some unexpected consequences.
|
||||||
|
|
||||||
Consider the following example::
|
Consider the following example::
|
||||||
|
|
||||||
>>> p = Person(name='Fred')
|
>>> p = Person(name='Fred')
|
||||||
>>> p.save(using='first') # (1)
|
>>> p.save(using='first') # (statement 1)
|
||||||
# some other processing ...
|
>>> p.save(using='second') # (statement 2)
|
||||||
>>> p.save(using='second') # (2)
|
|
||||||
|
|
||||||
In statement 1, a new Person object is saved to the ``first``
|
In statement 1, a new ``Person`` object is saved to the ``first``
|
||||||
database. At this time, ``p`` doesn't have a primary key, so Django
|
database. At this time, ``p`` doesn't have a primary key, so Django
|
||||||
issues a SQL ``INSERT`` statement. This creates a primary key, and
|
issues a SQL ``INSERT`` statement. This creates a primary key, and
|
||||||
Django assigns that primary key to ``p``.
|
Django assigns that primary key to ``p``.
|
||||||
|
@ -135,29 +142,26 @@ When the save occurs in statement 2, ``p`` already has a primary key
|
||||||
value, and Django will attempt to use that primary key on the new
|
value, and Django will attempt to use that primary key on the new
|
||||||
database. If the primary key value isn't in use in the ``second``
|
database. If the primary key value isn't in use in the ``second``
|
||||||
database, then you won't have any problems -- the object will be
|
database, then you won't have any problems -- the object will be
|
||||||
copied to the new databse.
|
copied to the new database.
|
||||||
|
|
||||||
However, if the primary key of ``p`` is already in use on the
|
However, if the primary key of ``p`` is already in use on the
|
||||||
``second`` database, the existing object on the ``second`` database
|
``second`` database, the existing object in the ``second`` database
|
||||||
will be lost when ``p`` is saved.
|
will be overridden when ``p`` is saved.
|
||||||
|
|
||||||
There are two ways to avoid this outcome. Firstly, you can clear the
|
You can avoid this in two ways. First, you can clear the primary key
|
||||||
primary key of the instance. If an object has no primary key, Django
|
of the instance. If an object has no primary key, Django will treat it as
|
||||||
will treat it as a new object, avoiding any loss of data on the
|
a new object, avoiding any loss of data on the ``second`` database::
|
||||||
``second`` database::
|
|
||||||
|
|
||||||
>>> p = Person(name='Fred')
|
>>> p = Person(name='Fred')
|
||||||
>>> p.save(using='first')
|
>>> p.save(using='first')
|
||||||
# some other processing ...
|
>>> p.pk = None # Clear the primary key.
|
||||||
>>> p.pk = None # Clear the PK
|
>>> p.save(using='second') # Write a completely new object.
|
||||||
>>> p.save(using='second') # Write a completely new object
|
|
||||||
|
|
||||||
Secondly, you can use the ``force_insert`` option to ``save()`` to ensure that
|
The second option is to use the ``force_insert`` option to ``save()`` to ensure
|
||||||
Django does a SQL ``INSERT``::
|
that Django does a SQL ``INSERT``::
|
||||||
|
|
||||||
>>> p = Person(name='Fred')
|
>>> p = Person(name='Fred')
|
||||||
>>> p.save(using='first')
|
>>> p.save(using='first')
|
||||||
# some other processing ...
|
|
||||||
>>> p.save(using='second', force_insert=True)
|
>>> p.save(using='second', force_insert=True)
|
||||||
|
|
||||||
This will ensure that the person named ``Fred`` will have the same
|
This will ensure that the person named ``Fred`` will have the same
|
||||||
|
@ -165,50 +169,54 @@ primary key on both databases. If that primary key is already in use
|
||||||
when you try to save onto the ``second`` database, an error will be
|
when you try to save onto the ``second`` database, an error will be
|
||||||
raised.
|
raised.
|
||||||
|
|
||||||
Select a database to delete from
|
Selecting a database to delete from
|
||||||
================================
|
===================================
|
||||||
|
|
||||||
By default, a call to delete an existing object will be executed on the
|
By default, a call to delete an existing object will be executed on the
|
||||||
same database that was used to retrieve the object in the first place::
|
same database that was used to retrieve the object in the first place::
|
||||||
|
|
||||||
>>> user_obj = User.objects.using('legacy_users').get(username='fred')
|
>>> u = User.objects.using('legacy_users').get(username='fred')
|
||||||
>>> user_obj.delete() # will delete from the `legacy_users` database
|
>>> u.delete() # will delete from the `legacy_users` database
|
||||||
|
|
||||||
If you want to specify the database from which a model will be
|
To specify the database from which a model will be deleted, pass a
|
||||||
deleted, you can use a ``using`` keyword argument to the
|
``using`` keyword argument to the ``Model.delete()`` method. This argument
|
||||||
``Model.delete()`` method. This argument is analogous to the ``using``
|
works just like the ``using`` keyword argument to ``save()``.
|
||||||
keyword argument to ``save()``. For example if you were migrating a
|
|
||||||
user from the ``'legacy_users'`` database to the ``'new_users'``
|
For example, if you're migrating a user from the ``legacy_users`` database
|
||||||
database you might use the commands::
|
to the ``new_users`` database, you might use these commands::
|
||||||
|
|
||||||
>>> user_obj.save(using='new_users')
|
>>> user_obj.save(using='new_users')
|
||||||
>>> user_obj.delete(using='legacy_users')
|
>>> user_obj.delete(using='legacy_users')
|
||||||
|
|
||||||
Using ``Managers`` with multiple databases
|
Using managers with multiple databases
|
||||||
==========================================
|
======================================
|
||||||
|
|
||||||
When you call ``using()`` Django returns a ``QuerySet`` that will be
|
Use the ``db_manager()`` method on managers to give managers access to a
|
||||||
evaluated against that database. However, sometimes you want to direct
|
non-default database.
|
||||||
a manager to use a specific database chain ``using()``. If you call
|
|
||||||
``using()``, you won't have access to any of the methods on the
|
|
||||||
manager.
|
|
||||||
|
|
||||||
To overcome this limitation, managers provide a ``db_manager()``
|
For example, say you have a custom manager method that touches the database --
|
||||||
method. This method returns a copy of the *manager* bound to that
|
``User.objects.create_user()``. Because ``create_user()`` is a
|
||||||
specific database. So, if you want to load an object using it's
|
manager method, not a ``QuerySet`` method, you can't do
|
||||||
natural key (using the ``get_by_natural_key()`` method on the manager,
|
``User.objects.using('new_users').create_user()``. (The ``create_user()`` method
|
||||||
you can call::
|
is only available on ``User.objects``, the manager, not on ``QuerySet`` objects
|
||||||
|
derived from the manager.) The solution is to use ``db_manager()``, like this::
|
||||||
|
|
||||||
>>> Book.objects.db_manager("other").get_by_natural_key(...)
|
User.objects.db_manager('new_users').create_user(...)
|
||||||
|
|
||||||
If you are overriding ``get_query_set()`` on your manager you must be sure to
|
``db_manager()`` returns a copy of the manager bound to the database you specify.
|
||||||
either, call the method on the parent (using ``super()``), or do the
|
|
||||||
appropriate handling of the ``_db`` attribute on the manager. For example if
|
Using ``get_query_set()`` with multiple databases
|
||||||
you wanted to return a custom ``QuerySet`` class from the ``get_query_set``
|
-------------------------------------------------
|
||||||
method you could do this::
|
|
||||||
|
If you're overriding ``get_query_set()`` on your manager, be sure to either
|
||||||
|
call the method on the parent (using ``super()``) or do the appropriate
|
||||||
|
handling of the ``_db`` attribute on the manager (a string containing the name
|
||||||
|
of the database to use).
|
||||||
|
|
||||||
|
For example, if you want to return a custom ``QuerySet`` class from the
|
||||||
|
``get_query_set`` method, you could do this::
|
||||||
|
|
||||||
class MyManager(models.Manager):
|
class MyManager(models.Manager):
|
||||||
...
|
|
||||||
def get_query_set(self):
|
def get_query_set(self):
|
||||||
qs = CustomQuerySet(self.model)
|
qs = CustomQuerySet(self.model)
|
||||||
if self._db is not None:
|
if self._db is not None:
|
||||||
|
@ -220,67 +228,66 @@ Exposing multiple databases in Django's admin interface
|
||||||
|
|
||||||
Django's admin doesn't have any explicit support for multiple
|
Django's admin doesn't have any explicit support for multiple
|
||||||
databases. If you want to provide an admin interface for a model on a
|
databases. If you want to provide an admin interface for a model on a
|
||||||
database other than ``default``, you need to write custom
|
database other than ``default``, you'll need to write custom
|
||||||
:class:`~django.contrib.admin.ModelAdmin` classes that will direct the
|
:class:`~django.contrib.admin.ModelAdmin` classes that will direct the
|
||||||
admin to use a specific database for content.
|
admin to use a specific database for content.
|
||||||
|
|
||||||
There are four methods that require customization on a ModelAdmin
|
``ModelAdmin`` objects have four methods that require customization for
|
||||||
object::
|
multiple-database support::
|
||||||
|
|
||||||
class MultiDBModelAdmin(admin.ModelAdmin):
|
class MultiDBModelAdmin(admin.ModelAdmin):
|
||||||
# A handy constant for the name of the alternate database
|
# A handy constant for the name of the alternate database.
|
||||||
using = 'other'
|
using = 'other'
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
# Tell Django to save objects to the 'other' database
|
# Tell Django to save objects to the 'other' database.
|
||||||
obj.save(using=self.using)
|
obj.save(using=self.using)
|
||||||
|
|
||||||
def queryset(self, request):
|
def queryset(self, request):
|
||||||
# Tell Django to look for objects on the 'other' database
|
# Tell Django to look for objects on the 'other' database.
|
||||||
return super(MultiDBModelAdmin, self).queryset(request).using(self.using)
|
return super(MultiDBModelAdmin, self).queryset(request).using(self.using)
|
||||||
|
|
||||||
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
|
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
|
||||||
# Tell Django to populate ForeignKey widgets using a query
|
# Tell Django to populate ForeignKey widgets using a query
|
||||||
# on the 'other' database
|
# on the 'other' database.
|
||||||
return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)
|
return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)
|
||||||
|
|
||||||
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
|
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
|
||||||
# Tell Django to populate ManyToMany widgets using a query
|
# Tell Django to populate ManyToMany widgets using a query
|
||||||
# on the 'other' database
|
# on the 'other' database.
|
||||||
return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)
|
return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)
|
||||||
|
|
||||||
The implementation provided here implements a multi-db strategy where
|
The implementation provided here implements a multi-database strategy where
|
||||||
all objects of a given type are stored on a specific database (e.g.,
|
all objects of a given type are stored on a specific database (e.g.,
|
||||||
all ``User`` objects are on the ``other`` database). If your usage of
|
all ``User`` objects are in the ``other`` database). If your usage of
|
||||||
multi-db is more complex, your ModelAdmin will need to reflect that
|
multiple databases is more complex, your ``ModelAdmin`` will need to reflect
|
||||||
strategy.
|
that strategy.
|
||||||
|
|
||||||
Inlines can be handled in a similar fashion -- they require just three
|
Inlines can be handled in a similar fashion. They require three customized methods::
|
||||||
customized methods::
|
|
||||||
|
|
||||||
class MultiDBTabularInline(admin.TabularInline):
|
class MultiDBTabularInline(admin.TabularInline):
|
||||||
using = 'other'
|
using = 'other'
|
||||||
|
|
||||||
def queryset(self, request):
|
def queryset(self, request):
|
||||||
# Tell Django to look for inline objects on the 'other' database
|
# Tell Django to look for inline objects on the 'other' database.
|
||||||
return super(MultiDBTabularInline, self).queryset(request).using(self.using)
|
return super(MultiDBTabularInline, self).queryset(request).using(self.using)
|
||||||
|
|
||||||
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
|
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
|
||||||
# Tell Django to populate ForeignKey widgets using a query
|
# Tell Django to populate ForeignKey widgets using a query
|
||||||
# on the 'other' database
|
# on the 'other' database.
|
||||||
return super(MultiDBTabularInline, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)
|
return super(MultiDBTabularInline, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)
|
||||||
|
|
||||||
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
|
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
|
||||||
# Tell Django to populate ManyToMany widgets using a query
|
# Tell Django to populate ManyToMany widgets using a query
|
||||||
# on the 'other' database
|
# on the 'other' database.
|
||||||
return super(MultiDBTabularInline, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)
|
return super(MultiDBTabularInline, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)
|
||||||
|
|
||||||
Once you have written your model admin definitions, they can be
|
Once you've written your model admin definitions, they can be registered with
|
||||||
registered with any Admin instance::
|
any ``Admin`` instance::
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
# Specialize the multi-db admin objects for use with specific models
|
# Specialize the multi-db admin objects for use with specific models.
|
||||||
class BookInline(MultiDBTabularInline):
|
class BookInline(MultiDBTabularInline):
|
||||||
model = Book
|
model = Book
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue