mirror of
				https://github.com/django/django.git
				synced 2025-10-26 07:06:08 +00:00 
			
		
		
		
	Rest of the _meta.app_cache stuff. Schema tests work now.
This commit is contained in:
		| @@ -2,7 +2,7 @@ from operator import attrgetter | |||||||
|  |  | ||||||
| from django.db import connection, connections, router | from django.db import connection, connections, router | ||||||
| from django.db.backends import util | from django.db.backends import util | ||||||
| from django.db.models import signals, get_model | from django.db.models import signals | ||||||
| from django.db.models.fields import (AutoField, Field, IntegerField, | from django.db.models.fields import (AutoField, Field, IntegerField, | ||||||
|     PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist) |     PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist) | ||||||
| from django.db.models.related import RelatedObject, PathInfo | from django.db.models.related import RelatedObject, PathInfo | ||||||
| @@ -18,8 +18,6 @@ from django import forms | |||||||
|  |  | ||||||
| RECURSIVE_RELATIONSHIP_CONSTANT = 'self' | RECURSIVE_RELATIONSHIP_CONSTANT = 'self' | ||||||
|  |  | ||||||
| pending_lookups = {} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def add_lazy_relation(cls, field, relation, operation): | def add_lazy_relation(cls, field, relation, operation): | ||||||
|     """ |     """ | ||||||
| @@ -70,14 +68,14 @@ def add_lazy_relation(cls, field, relation, operation): | |||||||
|     # string right away. If get_model returns None, it means that the related |     # string right away. If get_model returns None, it means that the related | ||||||
|     # model isn't loaded yet, so we need to pend the relation until the class |     # model isn't loaded yet, so we need to pend the relation until the class | ||||||
|     # is prepared. |     # is prepared. | ||||||
|     model = get_model(app_label, model_name, |     model = cls._meta.app_cache.get_model(app_label, model_name, | ||||||
|                       seed_cache=False, only_installed=False) |                       seed_cache=False, only_installed=False) | ||||||
|     if model: |     if model: | ||||||
|         operation(field, model, cls) |         operation(field, model, cls) | ||||||
|     else: |     else: | ||||||
|         key = (app_label, model_name) |         key = (app_label, model_name) | ||||||
|         value = (cls, field, operation) |         value = (cls, field, operation) | ||||||
|         pending_lookups.setdefault(key, []).append(value) |         cls._meta.app_cache.pending_lookups.setdefault(key, []).append(value) | ||||||
|  |  | ||||||
|  |  | ||||||
| def do_pending_lookups(sender, **kwargs): | def do_pending_lookups(sender, **kwargs): | ||||||
| @@ -85,7 +83,7 @@ def do_pending_lookups(sender, **kwargs): | |||||||
|     Handle any pending relations to the sending model. Sent from class_prepared. |     Handle any pending relations to the sending model. Sent from class_prepared. | ||||||
|     """ |     """ | ||||||
|     key = (sender._meta.app_label, sender.__name__) |     key = (sender._meta.app_label, sender.__name__) | ||||||
|     for cls, field, operation in pending_lookups.pop(key, []): |     for cls, field, operation in sender._meta.app_cache.pending_lookups.pop(key, []): | ||||||
|         operation(field, sender, cls) |         operation(field, sender, cls) | ||||||
|  |  | ||||||
| signals.class_prepared.connect(do_pending_lookups) | signals.class_prepared.connect(do_pending_lookups) | ||||||
| @@ -1330,6 +1328,7 @@ def create_many_to_many_intermediary_model(field, klass): | |||||||
|         'unique_together': (from_, to), |         'unique_together': (from_, to), | ||||||
|         'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to}, |         'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to}, | ||||||
|         'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to}, |         'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to}, | ||||||
|  |         'app_cache': field.model._meta.app_cache, | ||||||
|     }) |     }) | ||||||
|     # Construct and return the new class. |     # Construct and return the new class. | ||||||
|     return type(str(name), (models.Model,), { |     return type(str(name), (models.Model,), { | ||||||
|   | |||||||
| @@ -35,7 +35,11 @@ def _initialize(): | |||||||
|         # Mapping of app_labels to errors raised when trying to import the app. |         # Mapping of app_labels to errors raised when trying to import the app. | ||||||
|         app_errors = {}, |         app_errors = {}, | ||||||
|  |  | ||||||
|  |         # Pending lookups for lazy relations | ||||||
|  |         pending_lookups = {}, | ||||||
|  |  | ||||||
|         # -- Everything below here is only used when populating the cache -- |         # -- Everything below here is only used when populating the cache -- | ||||||
|  |         loads_installed = True, | ||||||
|         loaded = False, |         loaded = False, | ||||||
|         handled = {}, |         handled = {}, | ||||||
|         postponed = [], |         postponed = [], | ||||||
| @@ -56,12 +60,44 @@ class BaseAppCache(object): | |||||||
|  |  | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self.__dict__ = _initialize() |         self.__dict__ = _initialize() | ||||||
|  |         # This stops _populate loading from INSTALLED_APPS and ignores the | ||||||
|  |         # only_installed arguments to get_model[s] | ||||||
|  |         self.loads_installed = False | ||||||
|  |  | ||||||
|     def _populate(self): |     def _populate(self): | ||||||
|         """ |         """ | ||||||
|         Stub method - this base class does no auto-loading. |         Stub method - this base class does no auto-loading. | ||||||
|         """ |         """ | ||||||
|         self.loaded = True |         """ | ||||||
|  |         Fill in all the cache information. This method is threadsafe, in the | ||||||
|  |         sense that every caller will see the same state upon return, and if the | ||||||
|  |         cache is already initialised, it does no work. | ||||||
|  |         """ | ||||||
|  |         if self.loaded: | ||||||
|  |             return | ||||||
|  |         if not self.loads_installed: | ||||||
|  |             self.loaded = True | ||||||
|  |             return | ||||||
|  |         # Note that we want to use the import lock here - the app loading is | ||||||
|  |         # in many cases initiated implicitly by importing, and thus it is | ||||||
|  |         # possible to end up in deadlock when one thread initiates loading | ||||||
|  |         # without holding the importer lock and another thread then tries to | ||||||
|  |         # import something which also launches the app loading. For details of | ||||||
|  |         # this situation see #18251. | ||||||
|  |         imp.acquire_lock() | ||||||
|  |         try: | ||||||
|  |             if self.loaded: | ||||||
|  |                 return | ||||||
|  |             for app_name in settings.INSTALLED_APPS: | ||||||
|  |                 if app_name in self.handled: | ||||||
|  |                     continue | ||||||
|  |                 self.load_app(app_name, True) | ||||||
|  |             if not self.nesting_level: | ||||||
|  |                 for app_name in self.postponed: | ||||||
|  |                     self.load_app(app_name) | ||||||
|  |                 self.loaded = True | ||||||
|  |         finally: | ||||||
|  |             imp.release_lock() | ||||||
|  |  | ||||||
|     def _label_for(self, app_mod): |     def _label_for(self, app_mod): | ||||||
|         """ |         """ | ||||||
| @@ -169,12 +205,15 @@ class BaseAppCache(object): | |||||||
|  |  | ||||||
|         By default, models that aren't part of installed apps will *not* |         By default, models that aren't part of installed apps will *not* | ||||||
|         be included in the list of models. However, if you specify |         be included in the list of models. However, if you specify | ||||||
|         only_installed=False, they will be. |         only_installed=False, they will be. If you're using a non-default | ||||||
|  |         AppCache, this argument does nothing - all models will be included. | ||||||
|  |  | ||||||
|         By default, models that have been swapped out will *not* be |         By default, models that have been swapped out will *not* be | ||||||
|         included in the list of models. However, if you specify |         included in the list of models. However, if you specify | ||||||
|         include_swapped, they will be. |         include_swapped, they will be. | ||||||
|         """ |         """ | ||||||
|  |         if not self.loads_installed: | ||||||
|  |             only_installed = False | ||||||
|         cache_key = (app_mod, include_auto_created, include_deferred, only_installed, include_swapped) |         cache_key = (app_mod, include_auto_created, include_deferred, only_installed, include_swapped) | ||||||
|         try: |         try: | ||||||
|             return self._get_models_cache[cache_key] |             return self._get_models_cache[cache_key] | ||||||
| @@ -212,6 +251,8 @@ class BaseAppCache(object): | |||||||
|  |  | ||||||
|         Returns None if no model is found. |         Returns None if no model is found. | ||||||
|         """ |         """ | ||||||
|  |         if not self.loads_installed: | ||||||
|  |             only_installed = False | ||||||
|         if seed_cache: |         if seed_cache: | ||||||
|             self._populate() |             self._populate() | ||||||
|         if only_installed and app_label not in self.app_labels: |         if only_installed and app_label not in self.app_labels: | ||||||
| @@ -241,12 +282,6 @@ class BaseAppCache(object): | |||||||
|             model_dict[model_name] = model |             model_dict[model_name] = model | ||||||
|         self._get_models_cache.clear() |         self._get_models_cache.clear() | ||||||
|  |  | ||||||
|     def copy_from(self, other): |  | ||||||
|         "Registers all models from the other cache into this one" |  | ||||||
|         cache._populate() |  | ||||||
|         for app_label, models in other.app_models.items(): |  | ||||||
|             self.register_models(app_label, *models.values()) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class AppCache(BaseAppCache): | class AppCache(BaseAppCache): | ||||||
|     """ |     """ | ||||||
| @@ -261,35 +296,6 @@ class AppCache(BaseAppCache): | |||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self.__dict__ = self.__shared_state |         self.__dict__ = self.__shared_state | ||||||
|  |  | ||||||
|     def _populate(self): |  | ||||||
|         """ |  | ||||||
|         Fill in all the cache information. This method is threadsafe, in the |  | ||||||
|         sense that every caller will see the same state upon return, and if the |  | ||||||
|         cache is already initialised, it does no work. |  | ||||||
|         """ |  | ||||||
|         if self.loaded: |  | ||||||
|             return |  | ||||||
|         # Note that we want to use the import lock here - the app loading is |  | ||||||
|         # in many cases initiated implicitly by importing, and thus it is |  | ||||||
|         # possible to end up in deadlock when one thread initiates loading |  | ||||||
|         # without holding the importer lock and another thread then tries to |  | ||||||
|         # import something which also launches the app loading. For details of |  | ||||||
|         # this situation see #18251. |  | ||||||
|         imp.acquire_lock() |  | ||||||
|         try: |  | ||||||
|             if self.loaded: |  | ||||||
|                 return |  | ||||||
|             for app_name in settings.INSTALLED_APPS: |  | ||||||
|                 if app_name in self.handled: |  | ||||||
|                     continue |  | ||||||
|                 self.load_app(app_name, True) |  | ||||||
|             if not self.nesting_level: |  | ||||||
|                 for app_name in self.postponed: |  | ||||||
|                     self.load_app(app_name) |  | ||||||
|                 self.loaded = True |  | ||||||
|         finally: |  | ||||||
|             imp.release_lock() |  | ||||||
|  |  | ||||||
| cache = AppCache() | cache = AppCache() | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								tests/app_cache/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/app_cache/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										17
									
								
								tests/app_cache/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/app_cache/models.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | from django.db import models | ||||||
|  | from django.db.models.loading import BaseAppCache | ||||||
|  |  | ||||||
|  | # We're testing app cache presence on load, so this is handy. | ||||||
|  |  | ||||||
|  | new_app_cache = BaseAppCache() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TotallyNormal(models.Model): | ||||||
|  |     name = models.CharField(max_length=255) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SoAlternative(models.Model): | ||||||
|  |     name = models.CharField(max_length=255) | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |         app_cache = new_app_cache | ||||||
							
								
								
									
										50
									
								
								tests/app_cache/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								tests/app_cache/tests.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | from __future__ import absolute_import | ||||||
|  | import datetime | ||||||
|  | from django.test import TransactionTestCase | ||||||
|  | from django.utils.unittest import skipUnless | ||||||
|  | from django.db import connection, DatabaseError, IntegrityError | ||||||
|  | from django.db.models.fields import IntegerField, TextField, CharField, SlugField | ||||||
|  | from django.db.models.fields.related import ManyToManyField, ForeignKey | ||||||
|  | from django.db.models.loading import cache, BaseAppCache | ||||||
|  | from django.db import models | ||||||
|  | from .models import TotallyNormal, SoAlternative, new_app_cache | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AppCacheTests(TransactionTestCase): | ||||||
|  |     """ | ||||||
|  |     Tests the AppCache borg and non-borg versions | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def test_models_py(self): | ||||||
|  |         """ | ||||||
|  |         Tests that the models in the models.py file were loaded correctly. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         self.assertEqual(cache.get_model("app_cache", "TotallyNormal"), TotallyNormal) | ||||||
|  |         self.assertEqual(cache.get_model("app_cache", "SoAlternative"), None) | ||||||
|  |  | ||||||
|  |         self.assertEqual(new_app_cache.get_model("app_cache", "TotallyNormal"), None) | ||||||
|  |         self.assertEqual(new_app_cache.get_model("app_cache", "SoAlternative"), SoAlternative) | ||||||
|  |  | ||||||
|  |     def test_dynamic_load(self): | ||||||
|  |         """ | ||||||
|  |         Makes a new model at runtime and ensures it goes into the right place. | ||||||
|  |         """ | ||||||
|  |         old_models = cache.get_models(cache.get_app("app_cache")) | ||||||
|  |         # Construct a new model in a new app cache | ||||||
|  |         body = {} | ||||||
|  |         new_app_cache = BaseAppCache() | ||||||
|  |         meta_contents = { | ||||||
|  |             'app_label': "app_cache", | ||||||
|  |             'app_cache': new_app_cache, | ||||||
|  |         } | ||||||
|  |         meta = type("Meta", tuple(), meta_contents) | ||||||
|  |         body['Meta'] = meta | ||||||
|  |         body['__module__'] = TotallyNormal.__module__ | ||||||
|  |         temp_model = type("SouthPonies", (models.Model,), body) | ||||||
|  |         # Make sure it appeared in the right place! | ||||||
|  |         self.assertEqual( | ||||||
|  |             old_models, | ||||||
|  |             cache.get_models(cache.get_app("app_cache")), | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(new_app_cache.get_model("app_cache", "SouthPonies"), temp_model) | ||||||
		Reference in New Issue
	
	Block a user