49 if isinstance(f, models.ImageField): |
49 if isinstance(f, models.ImageField): |
50 try: |
50 try: |
51 from PIL import Image |
51 from PIL import Image |
52 except ImportError: |
52 except ImportError: |
53 e.add(opts, '"%s": To use ImageFields, you need to install the Python Imaging Library. Get it at http://www.pythonware.com/products/pil/ .' % f.name) |
53 e.add(opts, '"%s": To use ImageFields, you need to install the Python Imaging Library. Get it at http://www.pythonware.com/products/pil/ .' % f.name) |
54 if f.prepopulate_from is not None and type(f.prepopulate_from) not in (list, tuple): |
|
55 e.add(opts, '"%s": prepopulate_from should be a list or tuple.' % f.name) |
|
56 if f.choices: |
54 if f.choices: |
57 if isinstance(f.choices, basestring) or not is_iterable(f.choices): |
55 if isinstance(f.choices, basestring) or not is_iterable(f.choices): |
58 e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name) |
56 e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name) |
59 else: |
57 else: |
60 for c in f.choices: |
58 for c in f.choices: |
61 if not type(c) in (tuple, list) or len(c) != 2: |
59 if not type(c) in (tuple, list) or len(c) != 2: |
62 e.add(opts, '"%s": "choices" should be a sequence of two-tuples.' % f.name) |
60 e.add(opts, '"%s": "choices" should be a sequence of two-tuples.' % f.name) |
63 if f.db_index not in (None, True, False): |
61 if f.db_index not in (None, True, False): |
64 e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name) |
62 e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name) |
65 |
63 |
66 # Check that max_length <= 255 if using older MySQL versions. |
64 # Perform any backend-specific field validation. |
67 if settings.DATABASE_ENGINE == 'mysql': |
65 connection.validation.validate_field(e, opts, f) |
68 db_version = connection.get_server_version() |
|
69 if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.max_length > 255: |
|
70 e.add(opts, '"%s": %s cannot have a "max_length" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s).' % (f.name, f.__class__.__name__, '.'.join([str(n) for n in db_version[:3]]))) |
|
71 |
66 |
72 # Check to see if the related field will clash with any existing |
67 # Check to see if the related field will clash with any existing |
73 # fields, m2m fields, m2m related objects or related objects |
68 # fields, m2m fields, m2m related objects or related objects |
74 if f.rel: |
69 if f.rel: |
75 if f.rel.to not in models.get_models(): |
70 if f.rel.to not in models.get_models(): |
76 e.add(opts, "'%s' has relation with model %s, which has not been installed" % (f.name, f.rel.to)) |
71 e.add(opts, "'%s' has a relation with model %s, which has either not been installed or is abstract." % (f.name, f.rel.to)) |
77 # it is a string and we could not find the model it refers to |
72 # it is a string and we could not find the model it refers to |
78 # so skip the next section |
73 # so skip the next section |
79 if isinstance(f.rel.to, (str, unicode)): |
74 if isinstance(f.rel.to, (str, unicode)): |
80 continue |
75 continue |
81 |
76 |
102 if r.get_accessor_name() == rel_name: |
97 if r.get_accessor_name() == rel_name: |
103 e.add(opts, "Accessor for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) |
98 e.add(opts, "Accessor for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) |
104 if r.get_accessor_name() == rel_query_name: |
99 if r.get_accessor_name() == rel_query_name: |
105 e.add(opts, "Reverse query name for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) |
100 e.add(opts, "Reverse query name for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) |
106 |
101 |
|
102 seen_intermediary_signatures = [] |
107 for i, f in enumerate(opts.local_many_to_many): |
103 for i, f in enumerate(opts.local_many_to_many): |
108 # Check to see if the related m2m field will clash with any |
104 # Check to see if the related m2m field will clash with any |
109 # existing fields, m2m fields, m2m related objects or related |
105 # existing fields, m2m fields, m2m related objects or related |
110 # objects |
106 # objects |
111 if f.rel.to not in models.get_models(): |
107 if f.rel.to not in models.get_models(): |
112 e.add(opts, "'%s' has m2m relation with model %s, which has not been installed" % (f.name, f.rel.to)) |
108 e.add(opts, "'%s' has an m2m relation with model %s, which has either not been installed or is abstract." % (f.name, f.rel.to)) |
113 # it is a string and we could not find the model it refers to |
109 # it is a string and we could not find the model it refers to |
114 # so skip the next section |
110 # so skip the next section |
115 if isinstance(f.rel.to, (str, unicode)): |
111 if isinstance(f.rel.to, (str, unicode)): |
116 continue |
112 continue |
|
113 |
|
114 # Check that the field is not set to unique. ManyToManyFields do not support unique. |
|
115 if f.unique: |
|
116 e.add(opts, "ManyToManyFields cannot be unique. Remove the unique argument on '%s'." % f.name) |
|
117 |
|
118 if getattr(f.rel, 'through', None) is not None: |
|
119 if hasattr(f.rel, 'through_model'): |
|
120 from_model, to_model = cls, f.rel.to |
|
121 if from_model == to_model and f.rel.symmetrical: |
|
122 e.add(opts, "Many-to-many fields with intermediate tables cannot be symmetrical.") |
|
123 seen_from, seen_to, seen_self = False, False, 0 |
|
124 for inter_field in f.rel.through_model._meta.fields: |
|
125 rel_to = getattr(inter_field.rel, 'to', None) |
|
126 if from_model == to_model: # relation to self |
|
127 if rel_to == from_model: |
|
128 seen_self += 1 |
|
129 if seen_self > 2: |
|
130 e.add(opts, "Intermediary model %s has more than two foreign keys to %s, which is ambiguous and is not permitted." % (f.rel.through_model._meta.object_name, from_model._meta.object_name)) |
|
131 else: |
|
132 if rel_to == from_model: |
|
133 if seen_from: |
|
134 e.add(opts, "Intermediary model %s has more than one foreign key to %s, which is ambiguous and is not permitted." % (f.rel.through_model._meta.object_name, from_model._meta.object_name)) |
|
135 else: |
|
136 seen_from = True |
|
137 elif rel_to == to_model: |
|
138 if seen_to: |
|
139 e.add(opts, "Intermediary model %s has more than one foreign key to %s, which is ambiguous and is not permitted." % (f.rel.through_model._meta.object_name, rel_to._meta.object_name)) |
|
140 else: |
|
141 seen_to = True |
|
142 if f.rel.through_model not in models.get_models(): |
|
143 e.add(opts, "'%s' specifies an m2m relation through model %s, which has not been installed." % (f.name, f.rel.through)) |
|
144 signature = (f.rel.to, cls, f.rel.through_model) |
|
145 if signature in seen_intermediary_signatures: |
|
146 e.add(opts, "The model %s has two manually-defined m2m relations through the model %s, which is not permitted. Please consider using an extra field on your intermediary model instead." % (cls._meta.object_name, f.rel.through_model._meta.object_name)) |
|
147 else: |
|
148 seen_intermediary_signatures.append(signature) |
|
149 seen_related_fk, seen_this_fk = False, False |
|
150 for field in f.rel.through_model._meta.fields: |
|
151 if field.rel: |
|
152 if not seen_related_fk and field.rel.to == f.rel.to: |
|
153 seen_related_fk = True |
|
154 elif field.rel.to == cls: |
|
155 seen_this_fk = True |
|
156 if not seen_related_fk or not seen_this_fk: |
|
157 e.add(opts, "'%s' has a manually-defined m2m relation through model %s, which does not have foreign keys to %s and %s" % (f.name, f.rel.through, f.rel.to._meta.object_name, cls._meta.object_name)) |
|
158 else: |
|
159 e.add(opts, "'%s' specifies an m2m relation through model %s, which has not been installed" % (f.name, f.rel.through)) |
117 |
160 |
118 rel_opts = f.rel.to._meta |
161 rel_opts = f.rel.to._meta |
119 rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name() |
162 rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name() |
120 rel_query_name = f.related_query_name() |
163 rel_query_name = f.related_query_name() |
121 # If rel_name is none, there is no reverse accessor (this only |
164 # If rel_name is none, there is no reverse accessor (this only |
143 if r.get_accessor_name() == rel_name: |
186 if r.get_accessor_name() == rel_name: |
144 e.add(opts, "Accessor for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) |
187 e.add(opts, "Accessor for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) |
145 if r.get_accessor_name() == rel_query_name: |
188 if r.get_accessor_name() == rel_query_name: |
146 e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) |
189 e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name)) |
147 |
190 |
148 # Check admin attribute. |
|
149 if opts.admin is not None: |
|
150 if not isinstance(opts.admin, models.AdminOptions): |
|
151 e.add(opts, '"admin" attribute, if given, must be set to a models.AdminOptions() instance.') |
|
152 else: |
|
153 # list_display |
|
154 if not isinstance(opts.admin.list_display, (list, tuple)): |
|
155 e.add(opts, '"admin.list_display", if given, must be set to a list or tuple.') |
|
156 else: |
|
157 for fn in opts.admin.list_display: |
|
158 try: |
|
159 f = opts.get_field(fn) |
|
160 except models.FieldDoesNotExist: |
|
161 if not hasattr(cls, fn): |
|
162 e.add(opts, '"admin.list_display" refers to %r, which isn\'t an attribute, method or property.' % fn) |
|
163 else: |
|
164 if isinstance(f, models.ManyToManyField): |
|
165 e.add(opts, '"admin.list_display" doesn\'t support ManyToManyFields (%r).' % fn) |
|
166 # list_display_links |
|
167 if opts.admin.list_display_links and not opts.admin.list_display: |
|
168 e.add(opts, '"admin.list_display" must be defined for "admin.list_display_links" to be used.') |
|
169 if not isinstance(opts.admin.list_display_links, (list, tuple)): |
|
170 e.add(opts, '"admin.list_display_links", if given, must be set to a list or tuple.') |
|
171 else: |
|
172 for fn in opts.admin.list_display_links: |
|
173 try: |
|
174 f = opts.get_field(fn) |
|
175 except models.FieldDoesNotExist: |
|
176 if not hasattr(cls, fn): |
|
177 e.add(opts, '"admin.list_display_links" refers to %r, which isn\'t an attribute, method or property.' % fn) |
|
178 if fn not in opts.admin.list_display: |
|
179 e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn) |
|
180 # list_filter |
|
181 if not isinstance(opts.admin.list_filter, (list, tuple)): |
|
182 e.add(opts, '"admin.list_filter", if given, must be set to a list or tuple.') |
|
183 else: |
|
184 for fn in opts.admin.list_filter: |
|
185 try: |
|
186 f = opts.get_field(fn) |
|
187 except models.FieldDoesNotExist: |
|
188 e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn) |
|
189 # date_hierarchy |
|
190 if opts.admin.date_hierarchy: |
|
191 try: |
|
192 f = opts.get_field(opts.admin.date_hierarchy) |
|
193 except models.FieldDoesNotExist: |
|
194 e.add(opts, '"admin.date_hierarchy" refers to %r, which isn\'t a field.' % opts.admin.date_hierarchy) |
|
195 |
|
196 # Check ordering attribute. |
191 # Check ordering attribute. |
197 if opts.ordering: |
192 if opts.ordering: |
198 for field_name in opts.ordering: |
193 for field_name in opts.ordering: |
199 if field_name == '?': continue |
194 if field_name == '?': continue |
200 if field_name.startswith('-'): |
195 if field_name.startswith('-'): |
207 continue |
202 continue |
208 try: |
203 try: |
209 opts.get_field(field_name, many_to_many=False) |
204 opts.get_field(field_name, many_to_many=False) |
210 except models.FieldDoesNotExist: |
205 except models.FieldDoesNotExist: |
211 e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name) |
206 e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name) |
212 |
|
213 # Check core=True, if needed. |
|
214 for related in opts.get_followed_related_objects(): |
|
215 if not related.edit_inline: |
|
216 continue |
|
217 try: |
|
218 for f in related.opts.fields: |
|
219 if f.core: |
|
220 raise StopIteration |
|
221 e.add(related.opts, "At least one field in %s should have core=True, because it's being edited inline by %s.%s." % (related.opts.object_name, opts.module_name, opts.object_name)) |
|
222 except StopIteration: |
|
223 pass |
|
224 |
207 |
225 # Check unique_together. |
208 # Check unique_together. |
226 for ut in opts.unique_together: |
209 for ut in opts.unique_together: |
227 for field_name in ut: |
210 for field_name in ut: |
228 try: |
211 try: |