1
0
mirror of https://github.com/django/django.git synced 2025-04-14 20:34:36 +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:
Russell Keith-Magee 2006-02-16 14:07:23 +00:00
parent 0604002ced
commit 6e8203a06c
4 changed files with 81 additions and 18 deletions

View File

@ -524,7 +524,7 @@ def install(app):
# First, try validating the models.
s = StringIO()
num_errors = get_validation_errors(s)
num_errors = get_validation_errors(s, app)
if num_errors:
sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
s.seek(0)
@ -559,7 +559,7 @@ def reset(app):
# First, try validating the models.
s = StringIO()
num_errors = get_validation_errors(s)
num_errors = get_validation_errors(s, app)
if num_errors:
sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
s.seek(0)
@ -819,13 +819,17 @@ class ModelErrorCollection:
self.errors.append((opts, error))
self.outfile.write("%s.%s: %s\n" % (opts.app_label, opts.module_name, error))
def get_validation_errors(outfile):
"Validates all installed models. Writes errors, if any, to outfile. Returns number of errors."
def get_validation_errors(outfile, app=None):
"""
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.models.fields.related import RelatedObject
e = ModelErrorCollection(outfile)
for cls in models.get_models():
for cls in models.get_models(app):
opts = cls._meta
# Do field-specific validation.
@ -864,6 +868,9 @@ def get_validation_errors(outfile):
# existing fields, m2m fields, m2m related objects or related objects
if f.rel:
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()
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))
@ -879,6 +886,9 @@ def get_validation_errors(outfile):
# existing fields, m2m fields, m2m related objects or related objects
if f.rel:
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()
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))

View File

@ -0,0 +1 @@

View 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.
"""

View File

@ -82,6 +82,14 @@ class TestRunner:
# Determine which models we're going to test.
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:
# Only run the specified tests.
bad_models = [m for m in self.which_tests if m not in test_models]
@ -138,21 +146,39 @@ class TestRunner:
except Exception, e:
log_error(model_name, "Error while importing", ''.join(traceback.format_exception(*sys.exc_info())[1:]))
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.
p = doctest.DocTestParser()
test_namespace = dict([(m._meta.object_name, m) \
for m in django.db.models.get_models(mod)])
dtest = p.get_doctest(mod.API_TESTS, test_namespace, model_name, None, None)
# Manually set verbose=False, because "-v" command-line parameter
# has side effects on doctest TestRunner class.
runner = DjangoDoctestRunner(verbosity_level=verbosity_level, verbose=False)
self.output(1, "%s model: Running tests" % model_name)
runner.run(dtest, clear_globs=True, out=sys.stdout.write)
# Run the API tests.
p = doctest.DocTestParser()
test_namespace = dict([(m._meta.object_name, m) \
for m in django.db.models.get_models(mod)])
dtest = p.get_doctest(mod.API_TESTS, test_namespace, model_name, None, None)
# Manually set verbose=False, because "-v" command-line parameter
# has side effects on doctest TestRunner class.
runner = DjangoDoctestRunner(verbosity_level=verbosity_level, verbose=False)
self.output(1, "%s model: Running tests" % model_name)
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
self.output(1, "Running other tests")
other_tests_dir = os.path.join(os.path.dirname(__file__), OTHER_TESTS_DIR)