Fixed #24351, #24346 -- Changed the signature of allow_migrate().

The new signature enables better support for routing RunPython and
RunSQL operations, especially w.r.t. reusable and third-party apps.

This commit also takes advantage of the deprecation cycle for the old
signature to remove the backward incompatibility introduced in #22583;
RunPython and RunSQL won't call allow_migrate() when when the router
has the old signature.

Thanks Aymeric Augustin and Tim Graham for helping shape up the patch.

Refs 22583.
This commit is contained in:
Loic Bistuer 2015-02-19 14:27:58 +07:00
parent dd0b487872
commit bed504d70b
26 changed files with 221 additions and 117 deletions

View file

@ -128,7 +128,7 @@ A database Router is a class that provides up to four methods:
provided in the ``hints`` dictionary. Details on valid hints are
provided :ref:`below <topics-db-multi-db-hints>`.
Returns None if there is no suggestion.
Returns ``None`` if there is no suggestion.
.. method:: db_for_write(model, **hints)
@ -140,32 +140,53 @@ A database Router is a class that provides up to four methods:
provided in the ``hints`` dictionary. Details on valid hints are
provided :ref:`below <topics-db-multi-db-hints>`.
Returns None if there is no suggestion.
Returns ``None`` if there is no suggestion.
.. method:: allow_relation(obj1, obj2, **hints)
Return True if a relation between obj1 and obj2 should be
allowed, False if the relation should be prevented, or None if
Return ``True`` if a relation between ``obj1`` and ``obj2`` should be
allowed, ``False`` if the relation should be prevented, or ``None`` if
the router has no opinion. This is purely a validation operation,
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, **hints)
.. method:: allow_migrate(db, app_label, model_name=None, **hints)
Determine if the ``model`` should have tables/indexes created in the
database with alias ``db``. Return True if the model should be
migrated, False if it should not be migrated, or None if
the router has no opinion. This method can be used to determine
the availability of a model on a given database.
Determine if the migration operation is allowed to run on the database with
alias ``db``. Return ``True`` if the operation should run, ``False`` if it
shouldn't run, or ``None`` if the router has no opinion.
Note that migrations will just silently not perform any operations
on a model for which this returns ``False``. This may result in broken
ForeignKeys, extra tables or missing tables if you change it once you
have applied some migrations.
The ``app_label`` positional argument is the label of the application
being migrated.
The value passed for ``model`` may be a
:ref:`historical model <historical-models>`, and thus not have any
custom attributes, methods or managers. You should only rely on ``_meta``.
``model_name`` is set by most migration operations to the value of
``model._meta.model_name`` (the lowercased version of the model
``__name__``) of the model being migrated. Its value is ``None`` for the
:class:`~django.db.migrations.operations.RunPython` and
:class:`~django.db.migrations.operations.RunSQL` operations unless they
provide it using hints.
``hints`` are used by certain operations to communicate additional
information to the router.
When ``model_name`` is set, ``hints`` normally contains the model class
under the key ``'model'``. Note that it may be a :ref:`historical model
<historical-models>`, and thus not have any custom attributes, methods, or
managers. You should only rely on ``_meta``.
This method can also be used to determine the availability of a model on a
given database.
Note that migrations will just silently not perform any operations on a
model for which this returns ``False``. This may result in broken foreign
keys, extra tables, or missing tables if you change it once you have
applied some migrations.
.. versionchanged:: 1.8
The signature of ``allow_migrate`` has changed significantly from previous
versions. See the :ref:`deprecation notes
<deprecated-signature-of-allow-migrate>` for more details.
A router doesn't have to provide *all* these methods -- it may omit one
or more of them. If one of the methods is omitted, Django will skip
@ -293,15 +314,13 @@ send queries for the ``auth`` app to ``auth_db``::
return True
return None
def allow_migrate(self, db, model, **hints):
def allow_migrate(self, db, app_label, model, **hints):
"""
Make sure the auth app only appears in the 'auth_db'
database.
"""
if db == 'auth_db':
return model._meta.app_label == 'auth'
elif model._meta.app_label == 'auth':
return False
if app_label == 'auth':
return db == 'auth_db'
return None
And we also want a router that sends all other apps to the
@ -333,7 +352,7 @@ from::
return True
return None
def allow_migrate(self, db, model, **hints):
def allow_migrate(self, db, app_label, model, **hints):
"""
All non-auth models end up in this pool.
"""