diff --git a/django/core/management.py b/django/core/management.py index a30d08abdd..d20dddd034 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -1,7 +1,7 @@ # Django management-related functions, including "CREATE TABLE" generation and # development-server initialization. -import django +import django from django.core.exceptions import ImproperlyConfigured import os, re, sys, textwrap from optparse import OptionParser @@ -766,6 +766,8 @@ class ModelErrorCollection: def get_validation_errors(outfile): "Validates all installed models. 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(): opts = cls._meta @@ -802,6 +804,20 @@ def get_validation_errors(outfile): if f.db_index not in (None, True, False): e.add(opts, '"%s" field: "db_index" should be either None, True or False.' % f.name) + # Check to see if the related field will clash with any + # existing fields, m2m fields, m2m related objects or related objects + if f.rel: + rel_opts = f.rel.to._meta + 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)) + elif rel_name in [r.name for r in rel_opts.many_to_many]: + e.add(opts, "'%s.%s' related field: Clashes with m2m field on '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name)) + elif rel_name in [r.OLD_get_accessor_name() for r in rel_opts.get_all_related_many_to_many_objects()]: + e.add(opts, "'%s.%s' related field: Clashes with related m2m field '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name)) + elif rel_name in [r.OLD_get_accessor_name() for r in rel_opts.get_all_related_objects() if r.field is not f]: + e.add(opts, "'%s.%s' related field: Clashes with related field on '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name)) + # Check for multiple ManyToManyFields to the same object, and # verify "singular" is set in that case. for i, f in enumerate(opts.many_to_many): @@ -809,6 +825,20 @@ def get_validation_errors(outfile): if f.rel.to._meta == previous_f.rel.to._meta and f.rel.singular == previous_f.rel.singular: e.add(opts, 'The "%s" field requires a "singular" parameter, because the %s model has more than one ManyToManyField to the same model (%s).' % (f.name, opts.object_name, previous_f.rel.to._meta.object_name)) + # Check to see if the related m2m field will clash with any + # existing fields, m2m fields, m2m related objects or related objects + if f.rel: + rel_opts = f.rel.to._meta + 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)) + elif rel_name in [r.name for r in rel_opts.many_to_many]: + e.add(opts, "'%s.%s' related m2m field: Clashes with m2m field on '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name)) + elif rel_name in [r.OLD_get_accessor_name() for r in rel_opts.get_all_related_many_to_many_objects() if r.field is not f]: + e.add(opts, "'%s.%s' related m2m field: Clashes with related m2m field '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name)) + elif rel_name in [r.OLD_get_accessor_name() for r in rel_opts.get_all_related_objects()]: + e.add(opts, "'%s.%s' related m2m field: Clashes with related field on '%s.%s'" % (opts.object_name, f.name, rel_opts.object_name, rel_name)) + # Check admin attribute. if opts.admin is not None: if not isinstance(opts.admin, models.AdminOptions): diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 19279d45e7..b0883f1e73 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -27,8 +27,6 @@ get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '') class FieldDoesNotExist(Exception): pass -class FieldCollision(Exception): - pass def manipulator_validator_unique(f, opts, self, field_data, all_data): "Validates that the value is unique for this field." diff --git a/django/db/models/options.py b/django/db/models/options.py index 652d9446bb..026104aa0e 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -1,6 +1,6 @@ from django.db.models.related import RelatedObject from django.db.models.fields.related import ManyToMany -from django.db.models.fields import AutoField, FieldDoesNotExist, FieldCollision +from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.loading import get_models from django.db.models.query import orderlist2sql from bisect import bisect @@ -123,23 +123,7 @@ class Options: for klass in get_models(): for f in klass._meta.fields: if f.rel and self == f.rel.to._meta: - rel_obj = RelatedObject(f.rel.to, klass, f) - # Check to see if the new related field will clash with any - # existing fields, m2m fields, m2m related objects or related objects - if rel_obj.OLD_get_accessor_name() in [r.name for r in f.rel.to._meta.fields]: - raise FieldCollision, 'Related field ' + klass._meta.object_name + \ - '.' + f.name + ' clashes with existing field on ' + self.object_name - elif rel_obj.OLD_get_accessor_name() in [r.name for r in f.rel.to._meta.many_to_many]: - raise FieldCollision, 'Related field ' + klass._meta.object_name + \ - '.' + f.name + ' clashes with existing m2m field on ' + self.object_name - elif rel_obj.OLD_get_accessor_name() in [r.name for r in f.rel.to._meta.get_all_related_many_to_many_objects()]: - raise FieldCollision, 'Related field ' + klass._meta.object_name + \ - '.' + f.name + ' clashes with existing related m2m field on ' + self.object_name - elif rel_obj.OLD_get_accessor_name() in [r.OLD_get_accessor_name() for r in rel_objs]: - raise FieldCollision, 'Related field ' + klass._meta.object_name + \ - '.' + f.name + ' clashes with existing related field on ' + self.object_name - else: - rel_objs.append(rel_obj) + rel_objs.append(RelatedObject(f.rel.to, klass, f)) self._all_related_objects = rel_objs return rel_objs @@ -173,23 +157,7 @@ class Options: for klass in get_models(): for f in klass._meta.many_to_many: if f.rel and self == f.rel.to._meta: - rel_obj = RelatedObject(f.rel.to, klass, f) - # Check to see if the new related field will clash with any - # existing fields, m2m fields, m2m related objects or related objects - if rel_obj.OLD_get_accessor_name() in [r.name for r in f.rel.to._meta.fields]: - raise FieldCollision, 'Related field ' + klass._meta.object_name + \ - '.' + f.name + ' clashes with existing field on ' + self.object_name - elif rel_obj.OLD_get_accessor_name() in [r.name for r in f.rel.to._meta.many_to_many]: - raise FieldCollision, 'Related field ' + klass._meta.object_name + \ - '.' + f.name + ' clashes with existing m2m field on ' + self.object_name - elif rel_obj.OLD_get_accessor_name() in [r.OLD_get_accessor_name() for r in f.rel.to._meta.get_all_related_objects()]: - raise FieldCollision, 'Related field ' + klass._meta.object_name + \ - '.' + f.name + ' clashes with existing related field on ' + self.object_name - elif rel_obj.OLD_get_accessor_name() in [r.name for r in rel_objs]: - raise FieldCollision, 'Related field ' + klass._meta.object_name + \ - '.' + f.name + ' clashes with existing related m2m field on ' + self.object_name - else: - rel_objs.append(rel_obj) + rel_objs.append(RelatedObject(f.rel.to, klass, f)) self._all_related_many_to_many_objects = rel_objs return rel_objs