mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Fixed #23879 -- Allowed model migration skip based on feature/vendor
Thanks Carl Meyer for the report and review, and Tim Graham for the review.
This commit is contained in:
		| @@ -268,7 +268,7 @@ class Command(BaseCommand): | |||||||
|                 deferred_sql = [] |                 deferred_sql = [] | ||||||
|                 for app_name, model_list in manifest.items(): |                 for app_name, model_list in manifest.items(): | ||||||
|                     for model in model_list: |                     for model in model_list: | ||||||
|                         if model._meta.proxy or not model._meta.managed: |                         if not model._meta.can_migrate(connection): | ||||||
|                             continue |                             continue | ||||||
|                         if self.verbosity >= 3: |                         if self.verbosity >= 3: | ||||||
|                             self.stdout.write( |                             self.stdout.write( | ||||||
|   | |||||||
| @@ -106,7 +106,7 @@ class BaseDatabaseCreation(object): | |||||||
|         # Make a function to iteratively return every object |         # Make a function to iteratively return every object | ||||||
|         def get_objects(): |         def get_objects(): | ||||||
|             for model in serializers.sort_dependencies(app_list): |             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)): |                         router.allow_migrate_model(self.connection.alias, model)): | ||||||
|                     queryset = model._default_manager.using(self.connection.alias).order_by(model._meta.pk.name) |                     queryset = model._default_manager.using(self.connection.alias).order_by(model._meta.pk.name) | ||||||
|                     for obj in queryset.iterator(): |                     for obj in queryset.iterator(): | ||||||
|   | |||||||
| @@ -106,7 +106,7 @@ class Operation(object): | |||||||
|         This is a thin wrapper around router.allow_migrate_model() that |         This is a thin wrapper around router.allow_migrate_model() that | ||||||
|         preemptively rejects any proxy, swapped out, or unmanaged model. |         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 False | ||||||
|  |  | ||||||
|         return router.allow_migrate_model(connection_alias, model) |         return router.allow_migrate_model(connection_alias, model) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ from itertools import chain | |||||||
| from django.apps import apps | from django.apps import apps | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.core.exceptions import FieldDoesNotExist | from django.core.exceptions import FieldDoesNotExist | ||||||
|  | from django.db import connections | ||||||
| from django.db.models.fields import AutoField | from django.db.models.fields import AutoField | ||||||
| from django.db.models.fields.proxy import OrderWrt | from django.db.models.fields.proxy import OrderWrt | ||||||
| from django.db.models.fields.related import ManyToManyField | 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', |                  'order_with_respect_to', 'app_label', 'db_tablespace', | ||||||
|                  'abstract', 'managed', 'proxy', 'swappable', 'auto_created', |                  'abstract', 'managed', 'proxy', 'swappable', 'auto_created', | ||||||
|                  'index_together', 'apps', 'default_permissions', |                  '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): | class raise_deprecation(object): | ||||||
| @@ -111,6 +113,8 @@ class Options(object): | |||||||
|         self.get_latest_by = None |         self.get_latest_by = None | ||||||
|         self.order_with_respect_to = None |         self.order_with_respect_to = None | ||||||
|         self.db_tablespace = settings.DEFAULT_TABLESPACE |         self.db_tablespace = settings.DEFAULT_TABLESPACE | ||||||
|  |         self.required_db_features = [] | ||||||
|  |         self.required_db_vendor = None | ||||||
|         self.meta = meta |         self.meta = meta | ||||||
|         self.pk = None |         self.pk = None | ||||||
|         self.has_auto_field = False |         self.has_auto_field = False | ||||||
| @@ -337,6 +341,22 @@ class Options(object): | |||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return "%s.%s" % (smart_text(self.app_label), smart_text(self.model_name)) |         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 |     @property | ||||||
|     def verbose_name_raw(self): |     def verbose_name_raw(self): | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -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 |     If ``proxy = True``, a model which subclasses another model will be treated as | ||||||
|     a :ref:`proxy model <proxy-models>`. |     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`` | ``select_on_save`` | ||||||
| ------------------ | ------------------ | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user