Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

magic-removal: Fixes #1343 -- Modified model validator to only valida…

…te 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
  • Loading branch information...
commit 6e8203a06c6515d681e25db80f48eaa4bb52eb90 1 parent 0604002
Russell Keith-Magee authored February 16, 2006
20  django/core/management.py
@@ -524,7 +524,7 @@ def install(app):
524 524
 
525 525
     # First, try validating the models.
526 526
     s = StringIO()
527  
-    num_errors = get_validation_errors(s)
  527
+    num_errors = get_validation_errors(s, app)
528 528
     if num_errors:
529 529
         sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
530 530
         s.seek(0)
@@ -559,7 +559,7 @@ def reset(app):
559 559
 
560 560
     # First, try validating the models.
561 561
     s = StringIO()
562  
-    num_errors = get_validation_errors(s)
  562
+    num_errors = get_validation_errors(s, app)
563 563
     if num_errors:
564 564
         sys.stderr.write("Error: %s couldn't be installed, because there were errors in your model:\n" % app_name)
565 565
         s.seek(0)
@@ -819,13 +819,17 @@ def add(self, opts, error):
819 819
         self.errors.append((opts, error))
820 820
         self.outfile.write("%s.%s: %s\n" % (opts.app_label, opts.module_name, error))
821 821
 
822  
-def get_validation_errors(outfile):
823  
-    "Validates all installed models. Writes errors, if any, to outfile. Returns number of errors."
  822
+def get_validation_errors(outfile, app=None):
  823
+    """
  824
+    Validates all models that are part of the specified app. If no app name is provided, 
  825
+    validates all models of all installed apps. Writes errors, if any, to outfile. 
  826
+    Returns number of errors.
  827
+    """
824 828
     from django.db import models
825 829
     from django.db.models.fields.related import RelatedObject
826 830
 
827 831
     e = ModelErrorCollection(outfile)
828  
-    for cls in models.get_models():
  832
+    for cls in models.get_models(app):
829 833
         opts = cls._meta
830 834
 
831 835
         # Do field-specific validation.
@@ -864,6 +868,9 @@ def get_validation_errors(outfile):
864 868
             # existing fields, m2m fields, m2m related objects or related objects
865 869
             if f.rel:
866 870
                 rel_opts = f.rel.to._meta
  871
+                if f.rel.to not in models.get_models():
  872
+                     e.add(opts, "'%s' field: relates to uninstalled model %s" % (f.name, rel_opts.object_name))
  873
+                    
867 874
                 rel_name = RelatedObject(f.rel.to, cls, f).OLD_get_accessor_name()
868 875
                 if rel_name in [r.name for r in rel_opts.fields]:
869 876
                     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):
879 886
             # existing fields, m2m fields, m2m related objects or related objects
880 887
             if f.rel:
881 888
                 rel_opts = f.rel.to._meta
  889
+                if f.rel.to not in models.get_models():
  890
+                    e.add(opts, "'%s' field: has m2m relation with uninstalled model %s" % (f.name, rel_opts.object_name))
  891
+
882 892
                 rel_name = RelatedObject(f.rel.to, cls, f).OLD_get_accessor_name()
883 893
                 if rel_name in [r.name for r in rel_opts.fields]:
884 894
                     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
... ...
@@ -0,0 +1 @@
  1
+
26  tests/modeltests/invalid_models/models.py
... ...
@@ -0,0 +1,26 @@
  1
+"""
  2
+26. A test to check that the model validator works can correctly identify errors in a model. 
  3
+"""
  4
+
  5
+from django.db import models
  6
+
  7
+class FieldErrors(models.Model):
  8
+    charfield = models.CharField()
  9
+    floatfield = models.FloatField()
  10
+    filefield = models.FileField()
  11
+    prepopulate = models.CharField(maxlength=10, prepopulate_from='bad')
  12
+    choices = models.CharField(maxlength=10, choices='bad')
  13
+    choices2 = models.CharField(maxlength=10, choices=[(1,2,3),(1,2,3)])
  14
+    index = models.CharField(maxlength=10, db_index='bad')    
  15
+
  16
+
  17
+error_log = """invalid_models.fielderrors: "charfield" field: CharFields require a "maxlength" attribute.
  18
+invalid_models.fielderrors: "floatfield" field: FloatFields require a "decimal_places" attribute.
  19
+invalid_models.fielderrors: "floatfield" field: FloatFields require a "max_digits" attribute.
  20
+invalid_models.fielderrors: "filefield" field: FileFields require an "upload_to" attribute.
  21
+invalid_models.fielderrors: "prepopulate" field: prepopulate_from should be a list or tuple.
  22
+invalid_models.fielderrors: "choices" field: "choices" should be either a tuple or list.
  23
+invalid_models.fielderrors: "choices2" field: "choices" should be a sequence of two-tuples.
  24
+invalid_models.fielderrors: "choices2" field: "choices" should be a sequence of two-tuples.
  25
+invalid_models.fielderrors: "index" field: "db_index" should be either None, True or False.
  26
+"""
56  tests/runtests.py
@@ -82,6 +82,14 @@ def run_tests(self):
82 82
 
83 83
         # Determine which models we're going to test.
84 84
         test_models = get_test_models()
  85
+        if 'othertests' in self.which_tests:
  86
+            self.which_tests.remove('othertests')
  87
+            run_othertests = True
  88
+            if not self.which_tests:
  89
+                test_models = []
  90
+        else:
  91
+            run_othertests = not self.which_tests
  92
+            
85 93
         if self.which_tests:
86 94
             # Only run the specified tests.
87 95
             bad_models = [m for m in self.which_tests if m not in test_models]
@@ -138,21 +146,39 @@ def run_tests(self):
138 146
             except Exception, e:
139 147
                 log_error(model_name, "Error while importing", ''.join(traceback.format_exception(*sys.exc_info())[1:]))
140 148
                 continue
141  
-            self.output(1, "%s model: Installing" % model_name)
142  
-            management.install(mod)
143  
-
144  
-            # Run the API tests.
145  
-            p = doctest.DocTestParser()
146  
-            test_namespace = dict([(m._meta.object_name, m) \
147  
-                                    for m in django.db.models.get_models(mod)])
148  
-            dtest = p.get_doctest(mod.API_TESTS, test_namespace, model_name, None, None)
149  
-            # Manually set verbose=False, because "-v" command-line parameter
150  
-            # has side effects on doctest TestRunner class.
151  
-            runner = DjangoDoctestRunner(verbosity_level=verbosity_level, verbose=False)
152  
-            self.output(1, "%s model: Running tests" % model_name)
153  
-            runner.run(dtest, clear_globs=True, out=sys.stdout.write)
154  
-
155  
-        if not self.which_tests:
  149
+                
  150
+            if not getattr(mod, 'error_log', None):
  151
+                # Model is not marked as an invalid model
  152
+                self.output(1, "%s model: Installing" % model_name)
  153
+                management.install(mod)
  154
+
  155
+                # Run the API tests.
  156
+                p = doctest.DocTestParser()
  157
+                test_namespace = dict([(m._meta.object_name, m) \
  158
+                                        for m in django.db.models.get_models(mod)])
  159
+                dtest = p.get_doctest(mod.API_TESTS, test_namespace, model_name, None, None)
  160
+                # Manually set verbose=False, because "-v" command-line parameter
  161
+                # has side effects on doctest TestRunner class.
  162
+                runner = DjangoDoctestRunner(verbosity_level=verbosity_level, verbose=False)
  163
+                self.output(1, "%s model: Running tests" % model_name)
  164
+                runner.run(dtest, clear_globs=True, out=sys.stdout.write)
  165
+            else: 
  166
+                # Check that model known to be invalid is invalid for the right reasons.
  167
+                self.output(1, "%s model: Validating" % model_name)
  168
+            
  169
+                from cStringIO import StringIO
  170
+                s = StringIO()
  171
+                count = management.get_validation_errors(s, mod)
  172
+                s.seek(0)
  173
+                error_log = s.read()
  174
+                expected = len(mod.error_log.split('\n')) - 1
  175
+                if error_log != mod.error_log:
  176
+                    log_error(model_name,
  177
+                        "Validator found %d validation errors, %d expected" % (count, expected),
  178
+                        "Expected errors:\n%s\n\nActual errors:\n%s" % (mod.error_log, error_log))
  179
+
  180
+
  181
+        if run_othertests:
156 182
             # Run the non-model tests in the other tests dir
157 183
             self.output(1, "Running other tests")
158 184
             other_tests_dir = os.path.join(os.path.dirname(__file__), OTHER_TESTS_DIR)

0 notes on commit 6e8203a

Please sign in to comment.
Something went wrong with that request. Please try again.