Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yet another app loading branch #2076

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
99742c0
Moved django.db.models.loading to django.apps.cache.
aaugustin Dec 11, 2013
1535a5d
Removed BaseAppCache.app_store.
aaugustin Dec 11, 2013
6c7c476
Removed ModelDict.
aaugustin Dec 11, 2013
2cecee4
Removed module-level functions for the app cache.
aaugustin Dec 11, 2013
c2c50cc
Stored AppConfig objects instead of models modules in the app cache.
aaugustin Dec 12, 2013
54e57e8
Removed unused attribute app_errors of the app cache.
aaugustin Dec 12, 2013
b14151d
Moved list of models inside AppConfig instances.
aaugustin Dec 12, 2013
321ce9c
Fleshed out AppConfig objects.
aaugustin Dec 12, 2013
8cd57fe
Added get_app_config() to look up app configs by label.
aaugustin Dec 13, 2013
c6ce08a
Added get_app_configs() to iterate on app_config instances.
aaugustin Dec 13, 2013
9c4c2bb
Deprecated get_app_package, get_app_path and get_app_paths.
aaugustin Dec 13, 2013
c82bb4b
Simplified handling of available_apps slightly.
aaugustin Dec 13, 2013
0deee8e
Simplified register_models.
aaugustin Dec 13, 2013
1f1795b
Removed the _-prefix for populate().
aaugustin Dec 14, 2013
849143f
Deprecated get_apps().
aaugustin Dec 14, 2013
8260787
Deprecated get_app().
aaugustin Dec 14, 2013
2eb00ad
Made it possible to create apps without a models module.
aaugustin Dec 14, 2013
581faf7
Refactored old test runner to handle apps without a models module.
aaugustin Dec 14, 2013
20fc3fe
Fixed incorrect decrementation of nesting_level.
aaugustin Dec 15, 2013
9cee160
Inlined trivial method that was used only once.
aaugustin Dec 15, 2013
c561545
Restored deprecated aliases for functions in django.db.models.
aaugustin Dec 16, 2013
0ccc719
Moved the new app cache inside core.
aaugustin Dec 16, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions django/contrib/admin/validation.py
@@ -1,3 +1,4 @@
from django.core.apps import app_cache
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.db.models.fields import FieldDoesNotExist
Expand All @@ -15,9 +16,9 @@

class BaseValidator(object):
def __init__(self):
# Before we can introspect models, they need to be fully loaded so that
# inter-relations are set up correctly. We force that here.
models.get_apps()
# Before we can introspect models, they need the app cache to be fully
# loaded so that inter-relations are set up correctly.
app_cache.populate()

def validate(self, cls, model):
for m in dir(self):
Expand Down
20 changes: 8 additions & 12 deletions django/contrib/admindocs/views.py
Expand Up @@ -7,8 +7,9 @@
from django.conf import settings
from django.contrib import admin
from django.contrib.admin.views.decorators import staff_member_required
from django.core.apps import app_cache
from django.db import models
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
from django.core.exceptions import ViewDoesNotExist
from django.http import Http404
from django.core import urlresolvers
from django.contrib.admindocs import utils
Expand Down Expand Up @@ -182,7 +183,7 @@ class ModelIndexView(BaseAdminDocsView):
template_name = 'admin_doc/model_index.html'

def get_context_data(self, **kwargs):
m_list = [m._meta for m in models.get_models()]
m_list = [m._meta for m in app_cache.get_models()]
kwargs.update({'models': m_list})
return super(ModelIndexView, self).get_context_data(**kwargs)

Expand All @@ -193,17 +194,12 @@ class ModelDetailView(BaseAdminDocsView):
def get_context_data(self, **kwargs):
# Get the model class.
try:
app_mod = models.get_app(self.kwargs['app_label'])
except ImproperlyConfigured:
raise Http404(_("App %r not found") % self.kwargs['app_label'])
model = None
for m in models.get_models(app_mod):
if m._meta.model_name == self.kwargs['model_name']:
model = m
break
app_cache.get_app_config(self.kwargs['app_label'])
except LookupError:
raise Http404(_("App %(app_label)r not found") % self.kwargs)
model = app_cache.get_model(self.kwargs['app_label'], self.kwargs['model_name'])
if model is None:
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % {
'model_name': self.kwargs['model_name'], 'app_label': self.kwargs['app_label']})
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs)

opts = model._meta

Expand Down
4 changes: 2 additions & 2 deletions django/contrib/auth/__init__.py
Expand Up @@ -123,13 +123,13 @@ def get_user_model():
"""
Returns the User model that is active in this project.
"""
from django.db.models import get_model
from django.core.apps import app_cache

try:
app_label, model_name = settings.AUTH_USER_MODEL.split('.')
except ValueError:
raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
user_model = get_model(app_label, model_name)
user_model = app_cache.get_model(app_label, model_name)
if user_model is None:
raise ImproperlyConfigured("AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL)
return user_model
Expand Down
9 changes: 5 additions & 4 deletions django/contrib/auth/management/__init__.py
Expand Up @@ -8,10 +8,11 @@

from django.contrib.auth import (models as auth_app, get_permission_codename,
get_user_model)
from django.core.apps import app_cache, UnavailableApp
from django.core import exceptions
from django.core.management.base import CommandError
from django.db import DEFAULT_DB_ALIAS, router
from django.db.models import get_model, get_models, signals, UnavailableApp
from django.db.models import signals
from django.utils.encoding import DEFAULT_LOCALE_ENCODING
from django.utils import six
from django.utils.six.moves import input
Expand Down Expand Up @@ -61,7 +62,7 @@ def _check_permission_clashing(custom, builtin, ctype):

def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs):
try:
get_model('auth', 'Permission')
app_cache.get_model('auth', 'Permission')
except UnavailableApp:
return

Expand All @@ -70,7 +71,7 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw

from django.contrib.contenttypes.models import ContentType

app_models = get_models(app)
app_models = app_cache.get_models(app)

# This will hold the permissions we're looking for as
# (content_type, (codename, name))
Expand Down Expand Up @@ -119,7 +120,7 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw

def create_superuser(app, created_models, verbosity, db, **kwargs):
try:
get_model('auth', 'Permission')
app_cache.get_model('auth', 'Permission')
UserModel = get_user_model()
except UnavailableApp:
return
Expand Down
8 changes: 4 additions & 4 deletions django/contrib/auth/tests/test_management.py
Expand Up @@ -8,11 +8,11 @@
from django.contrib.auth.tests.custom_user import CustomUser
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.contenttypes.models import ContentType
from django.core.apps import app_cache
from django.core import exceptions
from django.core.management import call_command
from django.core.management.base import CommandError
from django.core.management.validation import get_validation_errors
from django.db.models.loading import get_app
from django.test import TestCase
from django.test.utils import override_settings
from django.utils import six
Expand Down Expand Up @@ -184,21 +184,21 @@ class CustomUserModelValidationTestCase(TestCase):
def test_required_fields_is_list(self):
"REQUIRED_FIELDS should be a list."
new_io = StringIO()
get_validation_errors(new_io, get_app('auth'))
get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
self.assertIn("The REQUIRED_FIELDS must be a list or tuple.", new_io.getvalue())

@override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields')
def test_username_not_in_required_fields(self):
"USERNAME_FIELD should not appear in REQUIRED_FIELDS."
new_io = StringIO()
get_validation_errors(new_io, get_app('auth'))
get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
self.assertIn("The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.", new_io.getvalue())

@override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername')
def test_username_non_unique(self):
"A non-unique USERNAME_FIELD should raise a model validation error."
new_io = StringIO()
get_validation_errors(new_io, get_app('auth'))
get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue())


Expand Down
3 changes: 2 additions & 1 deletion django/contrib/comments/views/comments.py
Expand Up @@ -3,6 +3,7 @@
from django.contrib import comments
from django.contrib.comments import signals
from django.contrib.comments.views.utils import next_redirect, confirmation_view
from django.core.apps import app_cache
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models
from django.shortcuts import render_to_response
Expand Down Expand Up @@ -48,7 +49,7 @@ def post_comment(request, next=None, using=None):
if ctype is None or object_pk is None:
return CommentPostBadRequest("Missing content_type or object_pk field.")
try:
model = models.get_model(*ctype.split(".", 1))
model = app_cache.get_model(*ctype.split(".", 1))
target = model._default_manager.using(using).get(pk=object_pk)
except TypeError:
return CommentPostBadRequest(
Expand Down
11 changes: 6 additions & 5 deletions django/contrib/contenttypes/management.py
@@ -1,6 +1,7 @@
from django.contrib.contenttypes.models import ContentType
from django.core.apps import app_cache, UnavailableApp
from django.db import DEFAULT_DB_ALIAS, router
from django.db.models import get_apps, get_model, get_models, signals, UnavailableApp
from django.db.models import signals
from django.utils.encoding import smart_text
from django.utils import six
from django.utils.six.moves import input
Expand All @@ -12,15 +13,15 @@ def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, *
entries that no longer have a matching model class.
"""
try:
get_model('contenttypes', 'ContentType')
app_cache.get_model('contenttypes', 'ContentType')
except UnavailableApp:
return

if not router.allow_migrate(db, ContentType):
return

ContentType.objects.clear_cache()
app_models = get_models(app)
app_models = app_cache.get_models(app)
if not app_models:
return
# They all have the same app_label, get the first one.
Expand Down Expand Up @@ -85,8 +86,8 @@ def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, *


def update_all_contenttypes(verbosity=2, **kwargs):
for app in get_apps():
update_contenttypes(app, None, verbosity, **kwargs)
for app_config in app_cache.get_app_configs(only_with_models_module=True):
update_contenttypes(app_config.models_module, None, verbosity, **kwargs)

signals.post_migrate.connect(update_contenttypes)

Expand Down
3 changes: 2 additions & 1 deletion django/contrib/contenttypes/models.py
@@ -1,3 +1,4 @@
from django.core.apps import app_cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_text, force_text
Expand Down Expand Up @@ -156,7 +157,7 @@ def __str__(self):

def model_class(self):
"Returns the Python model class for this type of content."
return models.get_model(self.app_label, self.model,
return app_cache.get_model(self.app_label, self.model,
only_installed=False)

def get_object_for_this_type(self, **kwargs):
Expand Down
3 changes: 2 additions & 1 deletion django/contrib/gis/sitemaps/kml.py
@@ -1,3 +1,4 @@
from django.core.apps import app_cache
from django.core import urlresolvers
from django.contrib.sitemaps import Sitemap
from django.contrib.gis.db.models.fields import GeometryField
Expand Down Expand Up @@ -25,7 +26,7 @@ def _build_kml_sources(self, sources):
"""
kml_sources = []
if sources is None:
sources = models.get_models()
sources = app_cache.get_models()
for source in sources:
if isinstance(source, models.base.ModelBase):
for field in source._meta.fields:
Expand Down
4 changes: 2 additions & 2 deletions django/contrib/gis/sitemaps/views.py
Expand Up @@ -2,14 +2,14 @@

import warnings

from django.core.apps import app_cache
from django.http import HttpResponse, Http404
from django.template import loader
from django.contrib.sites.models import get_current_site
from django.core import urlresolvers
from django.core.paginator import EmptyPage, PageNotAnInteger
from django.contrib.gis.db.models.fields import GeometryField
from django.db import connections, DEFAULT_DB_ALIAS
from django.db.models import get_model
from django.db.models.fields import FieldDoesNotExist
from django.utils import six
from django.utils.translation import ugettext as _
Expand Down Expand Up @@ -81,7 +81,7 @@ def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB
must be that of a geographic field.
"""
placemarks = []
klass = get_model(label, model)
klass = app_cache.get_model(label, model)
if not klass:
raise Http404('You must supply a valid app label and module name. Got "%s.%s"' % (label, model))

Expand Down
1 change: 1 addition & 0 deletions django/core/apps/__init__.py
@@ -0,0 +1 @@
from .cache import app_cache, UnavailableApp # NOQA
47 changes: 47 additions & 0 deletions django/core/apps/base.py
@@ -0,0 +1,47 @@
from collections import OrderedDict

from django.utils._os import upath


class AppConfig(object):
"""
Class representing a Django application and its configuration.
"""

def __init__(self, name, app_module, models_module):
# Full Python path to the application eg. 'django.contrib.admin'.
# This is the value that appears in INSTALLED_APPS.
self.name = name

# Last component of the Python path to the application eg. 'admin'.
# This value must be unique across a Django project.
self.label = name.rpartition(".")[2]

# Root module eg. <module 'django.contrib.admin' from
# 'django/contrib/admin/__init__.pyc'>.
self.app_module = app_module

# Module containing models eg. <module 'django.contrib.admin.models'
# from 'django/contrib/admin/models.pyc'>. None if the application
# doesn't have a models module.
self.models_module = models_module

# Mapping of lower case model names to model classes.
# Populated by calls to AppCache.register_model().
self.models = OrderedDict()

# Whether the app is in INSTALLED_APPS or was automatically created
# when one of its models was imported.
self.installed = app_module is not None

# Filesystem path to the application directory eg.
# u'/usr/lib/python2.7/dist-packages/django/contrib/admin'.
# This is a unicode object on Python 2 and a str on Python 3.
self.path = upath(app_module.__path__[0]) if app_module is not None else None

@classmethod
def _stub(cls, label):
return cls(label, None, None)

def __repr__(self):
return '<AppConfig: %s>' % self.label