~ubuntu-branches/debian/sid/python-django/sid

« back to all changes in this revision

Viewing changes to tests/invalid_models_tests/test_models.py

  • Committer: Package Import Robot
  • Author(s): Raphaël Hertzog
  • Date: 2014-09-17 14:15:11 UTC
  • mfrom: (1.3.17) (6.2.18 experimental)
  • Revision ID: package-import@ubuntu.com-20140917141511-icneokthe9ww5sk4
Tags: 1.7-2
* Release to unstable.
* Add a migrate-south sample script to help users apply their South
  migrations. Thanks to Brian May.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- encoding: utf-8 -*-
 
2
from __future__ import unicode_literals
 
3
 
 
4
from django.core.checks import Error
 
5
from django.db import models
 
6
from django.test.utils import override_settings
 
7
 
 
8
from .base import IsolatedModelsTestCase
 
9
 
 
10
 
 
11
class IndexTogetherTests(IsolatedModelsTestCase):
 
12
 
 
13
    def test_non_iterable(self):
 
14
        class Model(models.Model):
 
15
            class Meta:
 
16
                index_together = 42
 
17
 
 
18
        errors = Model.check()
 
19
        expected = [
 
20
            Error(
 
21
                "'index_together' must be a list or tuple.",
 
22
                hint=None,
 
23
                obj=Model,
 
24
                id='models.E008',
 
25
            ),
 
26
        ]
 
27
        self.assertEqual(errors, expected)
 
28
 
 
29
    def test_non_list(self):
 
30
        class Model(models.Model):
 
31
            class Meta:
 
32
                index_together = 'not-a-list'
 
33
 
 
34
        errors = Model.check()
 
35
        expected = [
 
36
            Error(
 
37
                "'index_together' must be a list or tuple.",
 
38
                hint=None,
 
39
                obj=Model,
 
40
                id='models.E008',
 
41
            ),
 
42
        ]
 
43
        self.assertEqual(errors, expected)
 
44
 
 
45
    def test_list_containing_non_iterable(self):
 
46
        class Model(models.Model):
 
47
            class Meta:
 
48
                index_together = [('a', 'b'), 42]
 
49
 
 
50
        errors = Model.check()
 
51
        expected = [
 
52
            Error(
 
53
                "All 'index_together' elements must be lists or tuples.",
 
54
                hint=None,
 
55
                obj=Model,
 
56
                id='models.E009',
 
57
            ),
 
58
        ]
 
59
        self.assertEqual(errors, expected)
 
60
 
 
61
    def test_pointing_to_missing_field(self):
 
62
        class Model(models.Model):
 
63
            class Meta:
 
64
                index_together = [
 
65
                    ["missing_field"],
 
66
                ]
 
67
 
 
68
        errors = Model.check()
 
69
        expected = [
 
70
            Error(
 
71
                "'index_together' refers to the non-existent field 'missing_field'.",
 
72
                hint=None,
 
73
                obj=Model,
 
74
                id='models.E012',
 
75
            ),
 
76
        ]
 
77
        self.assertEqual(errors, expected)
 
78
 
 
79
    def test_pointing_to_m2m_field(self):
 
80
        class Model(models.Model):
 
81
            m2m = models.ManyToManyField('self')
 
82
 
 
83
            class Meta:
 
84
                index_together = [
 
85
                    ["m2m"],
 
86
                ]
 
87
 
 
88
        errors = Model.check()
 
89
        expected = [
 
90
            Error(
 
91
                ("'index_together' refers to a ManyToManyField 'm2m', but "
 
92
                 "ManyToManyFields are not permitted in 'index_together'."),
 
93
                hint=None,
 
94
                obj=Model,
 
95
                id='models.E013',
 
96
            ),
 
97
        ]
 
98
        self.assertEqual(errors, expected)
 
99
 
 
100
 
 
101
# unique_together tests are very similar to index_together tests.
 
102
class UniqueTogetherTests(IsolatedModelsTestCase):
 
103
 
 
104
    def test_non_iterable(self):
 
105
        class Model(models.Model):
 
106
            class Meta:
 
107
                unique_together = 42
 
108
 
 
109
        errors = Model.check()
 
110
        expected = [
 
111
            Error(
 
112
                "'unique_together' must be a list or tuple.",
 
113
                hint=None,
 
114
                obj=Model,
 
115
                id='models.E010',
 
116
            ),
 
117
        ]
 
118
        self.assertEqual(errors, expected)
 
119
 
 
120
    def test_list_containing_non_iterable(self):
 
121
        class Model(models.Model):
 
122
            one = models.IntegerField()
 
123
            two = models.IntegerField()
 
124
 
 
125
            class Meta:
 
126
                unique_together = [('a', 'b'), 42]
 
127
 
 
128
        errors = Model.check()
 
129
        expected = [
 
130
            Error(
 
131
                "All 'unique_together' elements must be lists or tuples.",
 
132
                hint=None,
 
133
                obj=Model,
 
134
                id='models.E011',
 
135
            ),
 
136
        ]
 
137
        self.assertEqual(errors, expected)
 
138
 
 
139
    def test_non_list(self):
 
140
        class Model(models.Model):
 
141
            class Meta:
 
142
                unique_together = 'not-a-list'
 
143
 
 
144
        errors = Model.check()
 
145
        expected = [
 
146
            Error(
 
147
                "'unique_together' must be a list or tuple.",
 
148
                hint=None,
 
149
                obj=Model,
 
150
                id='models.E010',
 
151
            ),
 
152
        ]
 
153
        self.assertEqual(errors, expected)
 
154
 
 
155
    def test_valid_model(self):
 
156
        class Model(models.Model):
 
157
            one = models.IntegerField()
 
158
            two = models.IntegerField()
 
159
 
 
160
            class Meta:
 
161
                # unique_together can be a simple tuple
 
162
                unique_together = ('one', 'two')
 
163
 
 
164
        errors = Model.check()
 
165
        self.assertEqual(errors, [])
 
166
 
 
167
    def test_pointing_to_missing_field(self):
 
168
        class Model(models.Model):
 
169
            class Meta:
 
170
                unique_together = [
 
171
                    ["missing_field"],
 
172
                ]
 
173
 
 
174
        errors = Model.check()
 
175
        expected = [
 
176
            Error(
 
177
                "'unique_together' refers to the non-existent field 'missing_field'.",
 
178
                hint=None,
 
179
                obj=Model,
 
180
                id='models.E012',
 
181
            ),
 
182
        ]
 
183
        self.assertEqual(errors, expected)
 
184
 
 
185
    def test_pointing_to_m2m(self):
 
186
        class Model(models.Model):
 
187
            m2m = models.ManyToManyField('self')
 
188
 
 
189
            class Meta:
 
190
                unique_together = [
 
191
                    ["m2m"],
 
192
                ]
 
193
 
 
194
        errors = Model.check()
 
195
        expected = [
 
196
            Error(
 
197
                ("'unique_together' refers to a ManyToManyField 'm2m', but "
 
198
                 "ManyToManyFields are not permitted in 'unique_together'."),
 
199
                hint=None,
 
200
                obj=Model,
 
201
                id='models.E013',
 
202
            ),
 
203
        ]
 
204
        self.assertEqual(errors, expected)
 
205
 
 
206
 
 
207
class FieldNamesTests(IsolatedModelsTestCase):
 
208
 
 
209
    def test_ending_with_underscore(self):
 
210
        class Model(models.Model):
 
211
            field_ = models.CharField(max_length=10)
 
212
            m2m_ = models.ManyToManyField('self')
 
213
 
 
214
        errors = Model.check()
 
215
        expected = [
 
216
            Error(
 
217
                'Field names must not end with an underscore.',
 
218
                hint=None,
 
219
                obj=Model._meta.get_field('field_'),
 
220
                id='fields.E001',
 
221
            ),
 
222
            Error(
 
223
                'Field names must not end with an underscore.',
 
224
                hint=None,
 
225
                obj=Model._meta.get_field('m2m_'),
 
226
                id='fields.E001',
 
227
            ),
 
228
        ]
 
229
        self.assertEqual(errors, expected)
 
230
 
 
231
    def test_including_separator(self):
 
232
        class Model(models.Model):
 
233
            some__field = models.IntegerField()
 
234
 
 
235
        errors = Model.check()
 
236
        expected = [
 
237
            Error(
 
238
                'Field names must not contain "__".',
 
239
                hint=None,
 
240
                obj=Model._meta.get_field('some__field'),
 
241
                id='fields.E002',
 
242
            )
 
243
        ]
 
244
        self.assertEqual(errors, expected)
 
245
 
 
246
    def test_pk(self):
 
247
        class Model(models.Model):
 
248
            pk = models.IntegerField()
 
249
 
 
250
        errors = Model.check()
 
251
        expected = [
 
252
            Error(
 
253
                "'pk' is a reserved word that cannot be used as a field name.",
 
254
                hint=None,
 
255
                obj=Model._meta.get_field('pk'),
 
256
                id='fields.E003',
 
257
            )
 
258
        ]
 
259
        self.assertEqual(errors, expected)
 
260
 
 
261
 
 
262
class ShadowingFieldsTests(IsolatedModelsTestCase):
 
263
 
 
264
    def test_multiinheritance_clash(self):
 
265
        class Mother(models.Model):
 
266
            clash = models.IntegerField()
 
267
 
 
268
        class Father(models.Model):
 
269
            clash = models.IntegerField()
 
270
 
 
271
        class Child(Mother, Father):
 
272
            # Here we have two clashed: id (automatic field) and clash, because
 
273
            # both parents define these fields.
 
274
            pass
 
275
 
 
276
        errors = Child.check()
 
277
        expected = [
 
278
            Error(
 
279
                ("The field 'id' from parent model "
 
280
                 "'invalid_models_tests.mother' clashes with the field 'id' "
 
281
                 "from parent model 'invalid_models_tests.father'."),
 
282
                hint=None,
 
283
                obj=Child,
 
284
                id='models.E005',
 
285
            ),
 
286
            Error(
 
287
                ("The field 'clash' from parent model "
 
288
                 "'invalid_models_tests.mother' clashes with the field 'clash' "
 
289
                 "from parent model 'invalid_models_tests.father'."),
 
290
                hint=None,
 
291
                obj=Child,
 
292
                id='models.E005',
 
293
            )
 
294
        ]
 
295
        self.assertEqual(errors, expected)
 
296
 
 
297
    def test_inheritance_clash(self):
 
298
        class Parent(models.Model):
 
299
            f_id = models.IntegerField()
 
300
 
 
301
        class Target(models.Model):
 
302
            # This field doesn't result in a clash.
 
303
            f_id = models.IntegerField()
 
304
 
 
305
        class Child(Parent):
 
306
            # This field clashes with parent "f_id" field.
 
307
            f = models.ForeignKey(Target)
 
308
 
 
309
        errors = Child.check()
 
310
        expected = [
 
311
            Error(
 
312
                ("The field 'f' clashes with the field 'f_id' "
 
313
                 "from model 'invalid_models_tests.parent'."),
 
314
                hint=None,
 
315
                obj=Child._meta.get_field('f'),
 
316
                id='models.E006',
 
317
            )
 
318
        ]
 
319
        self.assertEqual(errors, expected)
 
320
 
 
321
    def test_id_clash(self):
 
322
        class Target(models.Model):
 
323
            pass
 
324
 
 
325
        class Model(models.Model):
 
326
            fk = models.ForeignKey(Target)
 
327
            fk_id = models.IntegerField()
 
328
 
 
329
        errors = Model.check()
 
330
        expected = [
 
331
            Error(
 
332
                ("The field 'fk_id' clashes with the field 'fk' from model "
 
333
                 "'invalid_models_tests.model'."),
 
334
                hint=None,
 
335
                obj=Model._meta.get_field('fk_id'),
 
336
                id='models.E006',
 
337
            )
 
338
        ]
 
339
        self.assertEqual(errors, expected)
 
340
 
 
341
 
 
342
class OtherModelTests(IsolatedModelsTestCase):
 
343
 
 
344
    def test_unique_primary_key(self):
 
345
        invalid_id = models.IntegerField(primary_key=False)
 
346
 
 
347
        class Model(models.Model):
 
348
            id = invalid_id
 
349
 
 
350
        errors = Model.check()
 
351
        expected = [
 
352
            Error(
 
353
                "'id' can only be used as a field name if the field also sets 'primary_key=True'.",
 
354
                hint=None,
 
355
                obj=Model,
 
356
                id='models.E004',
 
357
            ),
 
358
        ]
 
359
        self.assertEqual(errors, expected)
 
360
 
 
361
    def test_ordering_non_iterable(self):
 
362
        class Model(models.Model):
 
363
            class Meta:
 
364
                ordering = "missing_field"
 
365
 
 
366
        errors = Model.check()
 
367
        expected = [
 
368
            Error(
 
369
                ("'ordering' must be a tuple or list "
 
370
                 "(even if you want to order by only one field)."),
 
371
                hint=None,
 
372
                obj=Model,
 
373
                id='models.E014',
 
374
            ),
 
375
        ]
 
376
        self.assertEqual(errors, expected)
 
377
 
 
378
    def test_non_valid(self):
 
379
        class RelationModel(models.Model):
 
380
            pass
 
381
 
 
382
        class Model(models.Model):
 
383
            relation = models.ManyToManyField(RelationModel)
 
384
 
 
385
            class Meta:
 
386
                ordering = ['relation']
 
387
 
 
388
        errors = Model.check()
 
389
        expected = [
 
390
            Error(
 
391
                "'ordering' refers to the non-existent field 'relation'.",
 
392
                hint=None,
 
393
                obj=Model,
 
394
                id='models.E015',
 
395
            ),
 
396
        ]
 
397
        self.assertEqual(errors, expected)
 
398
 
 
399
    def test_ordering_pointing_to_missing_field(self):
 
400
        class Model(models.Model):
 
401
            class Meta:
 
402
                ordering = ("missing_field",)
 
403
 
 
404
        errors = Model.check()
 
405
        expected = [
 
406
            Error(
 
407
                "'ordering' refers to the non-existent field 'missing_field'.",
 
408
                hint=None,
 
409
                obj=Model,
 
410
                id='models.E015',
 
411
            )
 
412
        ]
 
413
        self.assertEqual(errors, expected)
 
414
 
 
415
    def test_ordering_pointing_to_missing_foreignkey_field(self):
 
416
        # refs #22711
 
417
 
 
418
        class Model(models.Model):
 
419
            missing_fk_field = models.IntegerField()
 
420
 
 
421
            class Meta:
 
422
                ordering = ("missing_fk_field_id",)
 
423
 
 
424
        errors = Model.check()
 
425
        expected = [
 
426
            Error(
 
427
                "'ordering' refers to the non-existent field 'missing_fk_field_id'.",
 
428
                hint=None,
 
429
                obj=Model,
 
430
                id='models.E015',
 
431
            )
 
432
        ]
 
433
        self.assertEqual(errors, expected)
 
434
 
 
435
    def test_ordering_pointing_to_existing_foreignkey_field(self):
 
436
        # refs #22711
 
437
 
 
438
        class Parent(models.Model):
 
439
            pass
 
440
 
 
441
        class Child(models.Model):
 
442
            parent = models.ForeignKey(Parent)
 
443
 
 
444
            class Meta:
 
445
                ordering = ("parent_id",)
 
446
 
 
447
        self.assertFalse(Child.check())
 
448
 
 
449
    @override_settings(TEST_SWAPPED_MODEL_BAD_VALUE='not-a-model')
 
450
    def test_swappable_missing_app_name(self):
 
451
        class Model(models.Model):
 
452
            class Meta:
 
453
                swappable = 'TEST_SWAPPED_MODEL_BAD_VALUE'
 
454
 
 
455
        errors = Model.check()
 
456
        expected = [
 
457
            Error(
 
458
                "'TEST_SWAPPED_MODEL_BAD_VALUE' is not of the form 'app_label.app_name'.",
 
459
                hint=None,
 
460
                obj=None,
 
461
                id='models.E001',
 
462
            ),
 
463
        ]
 
464
        self.assertEqual(errors, expected)
 
465
 
 
466
    @override_settings(TEST_SWAPPED_MODEL_BAD_MODEL='not_an_app.Target')
 
467
    def test_swappable_missing_app(self):
 
468
        class Model(models.Model):
 
469
            class Meta:
 
470
                swappable = 'TEST_SWAPPED_MODEL_BAD_MODEL'
 
471
 
 
472
        errors = Model.check()
 
473
        expected = [
 
474
            Error(
 
475
                ("'TEST_SWAPPED_MODEL_BAD_MODEL' references 'not_an_app.Target', "
 
476
                 'which has not been installed, or is abstract.'),
 
477
                hint=None,
 
478
                obj=None,
 
479
                id='models.E002',
 
480
            ),
 
481
        ]
 
482
        self.assertEqual(errors, expected)
 
483
 
 
484
    def test_two_m2m_through_same_relationship(self):
 
485
        class Person(models.Model):
 
486
            pass
 
487
 
 
488
        class Group(models.Model):
 
489
            primary = models.ManyToManyField(Person,
 
490
                through="Membership", related_name="primary")
 
491
            secondary = models.ManyToManyField(Person, through="Membership",
 
492
                related_name="secondary")
 
493
 
 
494
        class Membership(models.Model):
 
495
            person = models.ForeignKey(Person)
 
496
            group = models.ForeignKey(Group)
 
497
 
 
498
        errors = Group.check()
 
499
        expected = [
 
500
            Error(
 
501
                ("The model has two many-to-many relations through "
 
502
                 "the intermediate model 'invalid_models_tests.Membership'."),
 
503
                hint=None,
 
504
                obj=Group,
 
505
                id='models.E003',
 
506
            )
 
507
        ]
 
508
        self.assertEqual(errors, expected)