summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2015-04-04 18:07:46 +0200
committerClaude Paroz <claude@2xlibre.net>2015-04-18 15:17:49 +0200
commit8097e5483210fa8c9d6142f1d48e1caed6d7b7bb (patch)
treedd7e2ecb4160e539d8e6c5045312d5ff8a89be9f
parentf043434174db3432eb63c341573c1ea89ef59b91 (diff)
Fixed #23879 -- Allowed model migration skip based on feature/vendor
Thanks Carl Meyer for the report and review, and Tim Graham for the review.
-rw-r--r--django/core/management/commands/migrate.py2
-rw-r--r--django/db/backends/base/creation.py2
-rw-r--r--django/db/migrations/operations/base.py2
-rw-r--r--django/db/models/options.py22
-rw-r--r--docs/ref/models/options.txt25
5 files changed, 49 insertions, 4 deletions
diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py
index 1c836ff4a7..2217d2f277 100644
--- a/django/core/management/commands/migrate.py
+++ b/django/core/management/commands/migrate.py
@@ -268,7 +268,7 @@ class Command(BaseCommand):
deferred_sql = []
for app_name, model_list in manifest.items():
for model in model_list:
- if model._meta.proxy or not model._meta.managed:
+ if not model._meta.can_migrate(connection):
continue
if self.verbosity >= 3:
self.stdout.write(
diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py
index a5e69ceedb..9bb9657b47 100644
--- a/django/db/backends/base/creation.py
+++ b/django/db/backends/base/creation.py
@@ -106,7 +106,7 @@ class BaseDatabaseCreation(object):
# Make a function to iteratively return every object
def get_objects():
for model in serializers.sort_dependencies(app_list):
- if (not model._meta.proxy and model._meta.managed and
+ if (model._meta.can_migrate(self.connection) and
router.allow_migrate_model(self.connection.alias, model)):
queryset = model._default_manager.using(self.connection.alias).order_by(model._meta.pk.name)
for obj in queryset.iterator():
diff --git a/django/db/migrations/operations/base.py b/django/db/migrations/operations/base.py
index b750a1e4be..44a0643fdd 100644
--- a/django/db/migrations/operations/base.py
+++ b/django/db/migrations/operations/base.py
@@ -106,7 +106,7 @@ class Operation(object):
This is a thin wrapper around router.allow_migrate_model() that
preemptively rejects any proxy, swapped out, or unmanaged model.
"""
- if model._meta.proxy or model._meta.swapped or not model._meta.managed:
+ if not model._meta.can_migrate(connection_alias):
return False
return router.allow_migrate_model(connection_alias, model)
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 91b6790b84..a2cdca82ee 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -8,6 +8,7 @@ from itertools import chain
from django.apps import apps
from django.conf import settings
from django.core.exceptions import FieldDoesNotExist
+from django.db import connections
from django.db.models.fields import AutoField
from django.db.models.fields.proxy import OrderWrt
from django.db.models.fields.related import ManyToManyField
@@ -36,7 +37,8 @@ DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
'order_with_respect_to', 'app_label', 'db_tablespace',
'abstract', 'managed', 'proxy', 'swappable', 'auto_created',
'index_together', 'apps', 'default_permissions',
- 'select_on_save', 'default_related_name')
+ 'select_on_save', 'default_related_name',
+ 'required_db_features', 'required_db_vendor')
class raise_deprecation(object):
@@ -111,6 +113,8 @@ class Options(object):
self.get_latest_by = None
self.order_with_respect_to = None
self.db_tablespace = settings.DEFAULT_TABLESPACE
+ self.required_db_features = []
+ self.required_db_vendor = None
self.meta = meta
self.pk = None
self.has_auto_field = False
@@ -337,6 +341,22 @@ class Options(object):
def __str__(self):
return "%s.%s" % (smart_text(self.app_label), smart_text(self.model_name))
+ def can_migrate(self, connection):
+ """
+ Return True if the model can/should be migrated on the `connection`.
+ `connection` can be either a real connection or a connection alias.
+ """
+ if self.proxy or self.swapped or not self.managed:
+ return False
+ if isinstance(connection, six.string_types):
+ connection = connections[connection]
+ if self.required_db_vendor:
+ return self.required_db_vendor == connection.vendor
+ if self.required_db_features:
+ return all(getattr(connection.features, feat, False)
+ for feat in self.required_db_features)
+ return True
+
@property
def verbose_name_raw(self):
"""
diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt
index 2f4f5aff71..f9a17721cf 100644
--- a/docs/ref/models/options.txt
+++ b/docs/ref/models/options.txt
@@ -283,6 +283,31 @@ Django quotes column and table names behind the scenes.
If ``proxy = True``, a model which subclasses another model will be treated as
a :ref:`proxy model <proxy-models>`.
+``required_db_features``
+------------------------
+
+.. attribute:: Options.required_db_features
+
+ .. versionadded:: 1.9
+
+ List of database features that the current connection should have so that
+ the model is considered during the migration phase. For example, if you set
+ this list to ``['gis_enabled']``, the model will only be synchronized on
+ GIS-enabled databases. It's also useful to skip some models when testing
+ with several database backends.
+
+``required_db_vendor``
+----------------------
+
+.. attribute:: Options.required_db_vendor
+
+ .. versionadded:: 1.9
+
+ Name of a supported database vendor that this model is specific to. Current
+ built-in vendor names are: ``sqlite``, ``postgresql``, ``mysql``,
+ ``oracle``. If this attribute is not empty and the current connection vendor
+ doesn't match it, the model will not be synchronized.
+
``select_on_save``
------------------