1
0
mirror of https://github.com/django/django.git synced 2025-10-24 06:06:09 +00:00

Fixed #22583 -- Allowed RunPython and RunSQL to provide hints to the db router.

Thanks Markus Holtermann and Tim Graham for the review.
This commit is contained in:
Loic Bistuer
2015-01-09 00:10:10 +07:00
parent 665e0aa6ec
commit 8f4877c89d
9 changed files with 247 additions and 75 deletions

View File

@@ -206,7 +206,7 @@ Special Operations
RunSQL
------
.. class:: RunSQL(sql, reverse_sql=None, state_operations=None)
.. class:: RunSQL(sql, reverse_sql=None, state_operations=None, hints=None)
Allows running of arbitrary SQL on the database - useful for more advanced
features of database backends that Django doesn't support directly, like
@@ -235,6 +235,11 @@ operation here so that the autodetector still has an up-to-date state of the
model (otherwise, when you next run ``makemigrations``, it won't see any
operation that adds that field and so will try to run it again).
The optional ``hints`` argument will be passed as ``**hints`` to the
:meth:`allow_migrate` method of database routers to assist them in making
routing decisions. See :ref:`topics-db-multi-db-hints` for more details on
database hints.
.. versionchanged:: 1.7.1
If you want to include literal percent signs in a query without parameters
@@ -245,6 +250,8 @@ operation that adds that field and so will try to run it again).
The ability to pass parameters to the ``sql`` and ``reverse_sql`` queries
was added.
The ``hints`` argument was added.
.. attribute:: RunSQL.noop
.. versionadded:: 1.8
@@ -258,7 +265,7 @@ operation that adds that field and so will try to run it again).
RunPython
---------
.. class:: RunPython(code, reverse_code=None, atomic=True)
.. class:: RunPython(code, reverse_code=None, atomic=True, hints=None)
Runs custom Python code in a historical context. ``code`` (and ``reverse_code``
if supplied) should be callable objects that accept two arguments; the first is
@@ -267,6 +274,15 @@ match the operation's place in the project history, and the second is an
instance of :class:`SchemaEditor
<django.db.backends.schema.BaseDatabaseSchemaEditor>`.
The optional ``hints`` argument will be passed as ``**hints`` to the
:meth:`allow_migrate` method of database routers to assist them in making a
routing decision. See :ref:`topics-db-multi-db-hints` for more details on
database hints.
.. versionadded:: 1.8
The ``hints`` argument was added.
You are advised to write the code as a separate function above the ``Migration``
class in the migration file, and just pass it to ``RunPython``. Here's an
example of using ``RunPython`` to create some initial objects on a ``Country``

View File

@@ -462,6 +462,12 @@ Migrations
attribute/method were added to ease in making ``RunPython`` and ``RunSQL``
operations reversible.
* The :class:`~django.db.migrations.operations.RunPython` and
:class:`~django.db.migrations.operations.RunSQL` operations now accept a
``hints`` parameter that will be passed to :meth:`allow_migrate`. To take
advantage of this feature you must ensure that the ``allow_migrate()`` method
of all your routers accept ``**hints``.
Models
^^^^^^
@@ -1029,6 +1035,14 @@ Miscellaneous
* :func:`django.utils.translation.get_language()` now returns ``None`` instead
of :setting:`LANGUAGE_CODE` when translations are temporarily deactivated.
* The migration operations :class:`~django.db.migrations.operations.RunPython`
and :class:`~django.db.migrations.operations.RunSQL` now call the
:meth:`allow_migrate` method of database routers. In these cases the
``model`` argument of ``allow_migrate()`` is set to ``None``, so the router
must properly handle this value. This is most useful when used together with
the newly introduced ``hints`` parameter for these operations, but it can
also be used to disable migrations from running on a particular database.
.. _deprecated-features-1.8:
Features deprecated in 1.8

View File

@@ -150,7 +150,7 @@ A database Router is a class that provides up to four methods:
used by foreign key and many to many operations to determine if a
relation should be allowed between two objects.
.. method:: allow_migrate(db, model)
.. method:: allow_migrate(db, model, **hints)
Determine if the ``model`` should have tables/indexes created in the
database with alias ``db``. Return True if the model should be
@@ -293,7 +293,7 @@ send queries for the ``auth`` app to ``auth_db``::
return True
return None
def allow_migrate(self, db, model):
def allow_migrate(self, db, model, **hints):
"""
Make sure the auth app only appears in the 'auth_db'
database.
@@ -333,7 +333,7 @@ from::
return True
return None
def allow_migrate(self, db, model):
def allow_migrate(self, db, model, **hints):
"""
All non-auth models end up in this pool.
"""

View File

@@ -545,28 +545,26 @@ attribute::
migrations.RunPython(forwards),
]
You can also use your database router's ``allow_migrate()`` method, but keep in
mind that the imported router needs to stay around as long as it is referenced
inside a migration:
.. versionadded:: 1.8
You can also provide hints that will be passed to the :meth:`allow_migrate()`
method of database routers as ``**hints``:
.. snippet::
:filename: myapp/dbrouters.py
class MyRouter(object):
def allow_migrate(self, db, model):
return db == 'default'
def allow_migrate(self, db, model, **hints):
if 'target_db' in hints:
return db == hints['target_db']
return True
Then, to leverage this in your migrations, do the following::
from django.db import migrations
from myappname.dbrouters import MyRouter
def forwards(apps, schema_editor):
MyModel = apps.get_model("myappname", "MyModel")
if not MyRouter().allow_migrate(schema_editor.connection.alias, MyModel):
return
# Your migration code goes here
class Migration(migrations.Migration):
@@ -576,7 +574,7 @@ Then, to leverage this in your migrations, do the following::
]
operations = [
migrations.RunPython(forwards),
migrations.RunPython(forwards, hints={'target_db': 'default'}),
]
More advanced migrations