mirror of
https://github.com/django/django.git
synced 2025-06-05 03:29:12 +00:00
magic-removal: Fixes #1343 -- Modified model validator to only validate the model being installed. Includes unit test framework changes to allow validation tests.
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2310 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
0604002ced
commit
6e8203a06c
@ -524,7 +524,7 @@ def install(app):
|
|||||||
|
|
||||||
# First, try validating the models.
|
# First, try validating the models.
|
||||||
s = StringIO()
|
s = StringIO()
|
||||||
num_errors = get_validation_errors(s)
|
num_errors = get_validation_errors(s, app)
|
||||||
if num_errors:
|
if num_errors:
|
||||||
sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
|
sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
|
||||||
s.seek(0)
|
s.seek(0)
|
||||||
@ -559,7 +559,7 @@ def reset(app):
|
|||||||
|
|
||||||
# First, try validating the models.
|
# First, try validating the models.
|
||||||
s = StringIO()
|
s = StringIO()
|
||||||
num_errors = get_validation_errors(s)
|
num_errors = get_validation_errors(s, app)
|
||||||
if num_errors:
|
if num_errors:
|
||||||
sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
|
sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
|
||||||
s.seek(0)
|
s.seek(0)
|
||||||
@ -819,13 +819,17 @@ class ModelErrorCollection:
|
|||||||
self.errors.append((opts, error))
|
self.errors.append((opts, error))
|
||||||
self.outfile.write("%s.%s: %s\n" % (opts.app_label, opts.module_name, error))
|
self.outfile.write("%s.%s: %s\n" % (opts.app_label, opts.module_name, error))
|
||||||
|
|
||||||
def get_validation_errors(outfile):
|
def get_validation_errors(outfile, app=None):
|
||||||
"Validates all installed models. Writes errors, if any, to outfile. Returns number of errors."
|
"""
|
||||||
|
Validates all models that are part of the specified app. If no app name is provided,
|
||||||
|
validates all models of all installed apps. Writes errors, if any, to outfile.
|
||||||
|
Returns number of errors.
|
||||||
|
"""
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields.related import RelatedObject
|
from django.db.models.fields.related import RelatedObject
|
||||||
|
|
||||||
e = ModelErrorCollection(outfile)
|
e = ModelErrorCollection(outfile)
|
||||||
for cls in models.get_models():
|
for cls in models.get_models(app):
|
||||||
opts = cls._meta
|
opts = cls._meta
|
||||||
|
|
||||||
# Do field-specific validation.
|
# Do field-specific validation.
|
||||||
@ -864,6 +868,9 @@ def get_validation_errors(outfile):
|
|||||||
# existing fields, m2m fields, m2m related objects or related objects
|
# existing fields, m2m fields, m2m related objects or related objects
|
||||||
if f.rel:
|
if f.rel:
|
||||||
rel_opts = f.rel.to._meta
|
rel_opts = f.rel.to._meta
|
||||||
|
if f.rel.to not in models.get_models():
|
||||||
|
e.add(opts, "'%s' field: relates to uninstalled model %s" % (f.name, rel_opts.object_name))
|
||||||
|
|
||||||
rel_name = RelatedObject(f.rel.to, cls, f).OLD_get_accessor_name()
|
rel_name = RelatedObject(f.rel.to, cls, f).OLD_get_accessor_name()
|
||||||
if rel_name in [r.name for r in rel_opts.fields]:
|
if rel_name in [r.name for r in rel_opts.fields]:
|
||||||
e.add(opts, "'%s.%s' related field: Clashes with field on '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name))
|
e.add(opts, "'%s.%s' related field: Clashes with field on '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name))
|
||||||
@ -879,6 +886,9 @@ def get_validation_errors(outfile):
|
|||||||
# existing fields, m2m fields, m2m related objects or related objects
|
# existing fields, m2m fields, m2m related objects or related objects
|
||||||
if f.rel:
|
if f.rel:
|
||||||
rel_opts = f.rel.to._meta
|
rel_opts = f.rel.to._meta
|
||||||
|
if f.rel.to not in models.get_models():
|
||||||
|
e.add(opts, "'%s' field: has m2m relation with uninstalled model %s" % (f.name, rel_opts.object_name))
|
||||||
|
|
||||||
rel_name = RelatedObject(f.rel.to, cls, f).OLD_get_accessor_name()
|
rel_name = RelatedObject(f.rel.to, cls, f).OLD_get_accessor_name()
|
||||||
if rel_name in [r.name for r in rel_opts.fields]:
|
if rel_name in [r.name for r in rel_opts.fields]:
|
||||||
e.add(opts, "'%s.%s' related m2m field: Clashes with field on '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name))
|
e.add(opts, "'%s.%s' related m2m field: Clashes with field on '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name))
|
||||||
|
1
tests/modeltests/invalid_models/__init__.py
Normal file
1
tests/modeltests/invalid_models/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
26
tests/modeltests/invalid_models/models.py
Normal file
26
tests/modeltests/invalid_models/models.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"""
|
||||||
|
26. A test to check that the model validator works can correctly identify errors in a model.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class FieldErrors(models.Model):
|
||||||
|
charfield = models.CharField()
|
||||||
|
floatfield = models.FloatField()
|
||||||
|
filefield = models.FileField()
|
||||||
|
prepopulate = models.CharField(maxlength=10, prepopulate_from='bad')
|
||||||
|
choices = models.CharField(maxlength=10, choices='bad')
|
||||||
|
choices2 = models.CharField(maxlength=10, choices=[(1,2,3),(1,2,3)])
|
||||||
|
index = models.CharField(maxlength=10, db_index='bad')
|
||||||
|
|
||||||
|
|
||||||
|
error_log = """invalid_models.fielderrors: "charfield" field: CharFields require a "maxlength" attribute.
|
||||||
|
invalid_models.fielderrors: "floatfield" field: FloatFields require a "decimal_places" attribute.
|
||||||
|
invalid_models.fielderrors: "floatfield" field: FloatFields require a "max_digits" attribute.
|
||||||
|
invalid_models.fielderrors: "filefield" field: FileFields require an "upload_to" attribute.
|
||||||
|
invalid_models.fielderrors: "prepopulate" field: prepopulate_from should be a list or tuple.
|
||||||
|
invalid_models.fielderrors: "choices" field: "choices" should be either a tuple or list.
|
||||||
|
invalid_models.fielderrors: "choices2" field: "choices" should be a sequence of two-tuples.
|
||||||
|
invalid_models.fielderrors: "choices2" field: "choices" should be a sequence of two-tuples.
|
||||||
|
invalid_models.fielderrors: "index" field: "db_index" should be either None, True or False.
|
||||||
|
"""
|
@ -82,6 +82,14 @@ class TestRunner:
|
|||||||
|
|
||||||
# Determine which models we're going to test.
|
# Determine which models we're going to test.
|
||||||
test_models = get_test_models()
|
test_models = get_test_models()
|
||||||
|
if 'othertests' in self.which_tests:
|
||||||
|
self.which_tests.remove('othertests')
|
||||||
|
run_othertests = True
|
||||||
|
if not self.which_tests:
|
||||||
|
test_models = []
|
||||||
|
else:
|
||||||
|
run_othertests = not self.which_tests
|
||||||
|
|
||||||
if self.which_tests:
|
if self.which_tests:
|
||||||
# Only run the specified tests.
|
# Only run the specified tests.
|
||||||
bad_models = [m for m in self.which_tests if m not in test_models]
|
bad_models = [m for m in self.which_tests if m not in test_models]
|
||||||
@ -138,21 +146,39 @@ class TestRunner:
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
log_error(model_name, "Error while importing", ''.join(traceback.format_exception(*sys.exc_info())[1:]))
|
log_error(model_name, "Error while importing", ''.join(traceback.format_exception(*sys.exc_info())[1:]))
|
||||||
continue
|
continue
|
||||||
self.output(1, "%s model: Installing" % model_name)
|
|
||||||
management.install(mod)
|
if not getattr(mod, 'error_log', None):
|
||||||
|
# Model is not marked as an invalid model
|
||||||
|
self.output(1, "%s model: Installing" % model_name)
|
||||||
|
management.install(mod)
|
||||||
|
|
||||||
# Run the API tests.
|
# Run the API tests.
|
||||||
p = doctest.DocTestParser()
|
p = doctest.DocTestParser()
|
||||||
test_namespace = dict([(m._meta.object_name, m) \
|
test_namespace = dict([(m._meta.object_name, m) \
|
||||||
for m in django.db.models.get_models(mod)])
|
for m in django.db.models.get_models(mod)])
|
||||||
dtest = p.get_doctest(mod.API_TESTS, test_namespace, model_name, None, None)
|
dtest = p.get_doctest(mod.API_TESTS, test_namespace, model_name, None, None)
|
||||||
# Manually set verbose=False, because "-v" command-line parameter
|
# Manually set verbose=False, because "-v" command-line parameter
|
||||||
# has side effects on doctest TestRunner class.
|
# has side effects on doctest TestRunner class.
|
||||||
runner = DjangoDoctestRunner(verbosity_level=verbosity_level, verbose=False)
|
runner = DjangoDoctestRunner(verbosity_level=verbosity_level, verbose=False)
|
||||||
self.output(1, "%s model: Running tests" % model_name)
|
self.output(1, "%s model: Running tests" % model_name)
|
||||||
runner.run(dtest, clear_globs=True, out=sys.stdout.write)
|
runner.run(dtest, clear_globs=True, out=sys.stdout.write)
|
||||||
|
else:
|
||||||
|
# Check that model known to be invalid is invalid for the right reasons.
|
||||||
|
self.output(1, "%s model: Validating" % model_name)
|
||||||
|
|
||||||
|
from cStringIO import StringIO
|
||||||
|
s = StringIO()
|
||||||
|
count = management.get_validation_errors(s, mod)
|
||||||
|
s.seek(0)
|
||||||
|
error_log = s.read()
|
||||||
|
expected = len(mod.error_log.split('\n')) - 1
|
||||||
|
if error_log != mod.error_log:
|
||||||
|
log_error(model_name,
|
||||||
|
"Validator found %d validation errors, %d expected" % (count, expected),
|
||||||
|
"Expected errors:\n%s\n\nActual errors:\n%s" % (mod.error_log, error_log))
|
||||||
|
|
||||||
if not self.which_tests:
|
|
||||||
|
if run_othertests:
|
||||||
# Run the non-model tests in the other tests dir
|
# Run the non-model tests in the other tests dir
|
||||||
self.output(1, "Running other tests")
|
self.output(1, "Running other tests")
|
||||||
other_tests_dir = os.path.join(os.path.dirname(__file__), OTHER_TESTS_DIR)
|
other_tests_dir = os.path.join(os.path.dirname(__file__), OTHER_TESTS_DIR)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user