Fixes #1812 -- Added model validity checks to ensure that models.py exists, and has been successfully imported for all INSTALLED_APPS. Previous behaviour was to silently ignore empty/problem models, which resulted in the display of an admin page that doesn't display a supposedly installed model. Thanks to Ian Holsman for the original report.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3201 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
dc473309ef
commit
23c24fc08b
|
@ -803,9 +803,9 @@ class ModelErrorCollection:
|
||||||
self.errors = []
|
self.errors = []
|
||||||
self.outfile = outfile
|
self.outfile = outfile
|
||||||
|
|
||||||
def add(self, opts, error):
|
def add(self, context, error):
|
||||||
self.errors.append((opts, error))
|
self.errors.append((context, error))
|
||||||
self.outfile.write(style.ERROR("%s.%s: %s\n" % (opts.app_label, opts.module_name, error)))
|
self.outfile.write(style.ERROR("%s: %s\n" % (context, error)))
|
||||||
|
|
||||||
def get_validation_errors(outfile, app=None):
|
def get_validation_errors(outfile, app=None):
|
||||||
"""
|
"""
|
||||||
|
@ -814,9 +814,14 @@ def get_validation_errors(outfile, app=None):
|
||||||
Returns number of errors.
|
Returns number of errors.
|
||||||
"""
|
"""
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models.loading import get_app_errors
|
||||||
from django.db.models.fields.related import RelatedObject
|
from django.db.models.fields.related import RelatedObject
|
||||||
|
|
||||||
e = ModelErrorCollection(outfile)
|
e = ModelErrorCollection(outfile)
|
||||||
|
|
||||||
|
for (app_name, error) in get_app_errors().items():
|
||||||
|
e.add(app_name, error)
|
||||||
|
|
||||||
for cls in models.get_models(app):
|
for cls in models.get_models(app):
|
||||||
opts = cls._meta
|
opts = cls._meta
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@ _app_list = [] # Cache of installed apps.
|
||||||
_app_models = {} # Dictionary of models against app label
|
_app_models = {} # Dictionary of models against app label
|
||||||
# Each value is a dictionary of model name: model class
|
# Each value is a dictionary of model name: model class
|
||||||
# Applabel and Model entry exists in cache when individual model is loaded.
|
# Applabel and Model entry exists in cache when individual model is loaded.
|
||||||
|
_app_errors = {} # Dictionary of errors that were experienced when loading the INSTALLED_APPS
|
||||||
|
# Key is the app_name of the model, value is the exception that was raised
|
||||||
|
# during model loading.
|
||||||
_loaded = False # Has the contents of settings.INSTALLED_APPS been loaded?
|
_loaded = False # Has the contents of settings.INSTALLED_APPS been loaded?
|
||||||
# i.e., has get_apps() been called?
|
# i.e., has get_apps() been called?
|
||||||
|
|
||||||
|
@ -22,11 +25,9 @@ def get_apps():
|
||||||
for app_name in settings.INSTALLED_APPS:
|
for app_name in settings.INSTALLED_APPS:
|
||||||
try:
|
try:
|
||||||
load_app(app_name)
|
load_app(app_name)
|
||||||
except ImportError:
|
except Exception, e:
|
||||||
pass # Assume this app doesn't have a models.py in it.
|
# Problem importing the app
|
||||||
# GOTCHA: It may have a models.py that raises ImportError.
|
_app_errors[app_name] = e
|
||||||
except AttributeError:
|
|
||||||
pass # This app doesn't have a models.py in it.
|
|
||||||
return _app_list
|
return _app_list
|
||||||
|
|
||||||
def get_app(app_label):
|
def get_app(app_label):
|
||||||
|
@ -39,11 +40,18 @@ def get_app(app_label):
|
||||||
|
|
||||||
def load_app(app_name):
|
def load_app(app_name):
|
||||||
"Loads the app with the provided fully qualified name, and returns the model module."
|
"Loads the app with the provided fully qualified name, and returns the model module."
|
||||||
|
global _app_list
|
||||||
mod = __import__(app_name, '', '', ['models'])
|
mod = __import__(app_name, '', '', ['models'])
|
||||||
if mod.models not in _app_list:
|
if mod.models not in _app_list:
|
||||||
_app_list.append(mod.models)
|
_app_list.append(mod.models)
|
||||||
return mod.models
|
return mod.models
|
||||||
|
|
||||||
|
def get_app_errors():
|
||||||
|
"Returns the map of known problems with the INSTALLED_APPS"
|
||||||
|
global _app_errors
|
||||||
|
get_apps() # Run get_apps() to populate the _app_list cache. Slightly hackish.
|
||||||
|
return _app_errors
|
||||||
|
|
||||||
def get_models(app_mod=None):
|
def get_models(app_mod=None):
|
||||||
"""
|
"""
|
||||||
Given a module containing models, returns a list of the models. Otherwise
|
Given a module containing models, returns a list of the models. Otherwise
|
||||||
|
|
|
@ -88,6 +88,9 @@ class Options(object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Options for %s>' % self.object_name
|
return '<Options for %s>' % self.object_name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "%s.%s" % (self.app_label, self.module_name)
|
||||||
|
|
||||||
def get_field(self, name, many_to_many=True):
|
def get_field(self, name, many_to_many=True):
|
||||||
"Returns the requested field by name. Raises FieldDoesNotExist on error."
|
"Returns the requested field by name. Raises FieldDoesNotExist on error."
|
||||||
to_search = many_to_many and (self.fields + self.many_to_many) or self.fields
|
to_search = many_to_many and (self.fields + self.many_to_many) or self.fields
|
||||||
|
|
Loading…
Reference in New Issue