Ticket #16681: django-invalid-model-tests.diff

File django-invalid-model-tests.diff, 56.2 KB (added by anthonyb, 13 years ago)

Patch to fix invalid_models and streamline runtests.py

  • new file tests/modeltests/invalid_models/invalid_models/models.py

    diff -r 9a1f08c480bf tests/modeltests/invalid_models/invalid_models/models.py
    - +  
     1"""
     226. Invalid models
     3
     4This example exists purely to point out errors in models.
     5"""
     6
     7from django.db import models
     8
     9class FieldErrors(models.Model):
     10    charfield = models.CharField()
     11    charfield2 = models.CharField(max_length=-1)
     12    charfield3 = models.CharField(max_length="bad")
     13    decimalfield = models.DecimalField()
     14    decimalfield2 = models.DecimalField(max_digits=-1, decimal_places=-1)
     15    decimalfield3 = models.DecimalField(max_digits="bad", decimal_places="bad")
     16    decimalfield4 = models.DecimalField(max_digits=9, decimal_places=10)
     17    decimalfield5 = models.DecimalField(max_digits=10, decimal_places=10)
     18    filefield = models.FileField()
     19    choices = models.CharField(max_length=10, choices='bad')
     20    choices2 = models.CharField(max_length=10, choices=[(1,2,3),(1,2,3)])
     21    index = models.CharField(max_length=10, db_index='bad')
     22    field_ = models.CharField(max_length=10)
     23    nullbool = models.BooleanField(null=True)
     24
     25class Target(models.Model):
     26    tgt_safe = models.CharField(max_length=10)
     27    clash1 = models.CharField(max_length=10)
     28    clash2 = models.CharField(max_length=10)
     29
     30    clash1_set = models.CharField(max_length=10)
     31
     32class Clash1(models.Model):
     33    src_safe = models.CharField(max_length=10)
     34
     35    foreign = models.ForeignKey(Target)
     36    m2m = models.ManyToManyField(Target)
     37
     38class Clash2(models.Model):
     39    src_safe = models.CharField(max_length=10)
     40
     41    foreign_1 = models.ForeignKey(Target, related_name='id')
     42    foreign_2 = models.ForeignKey(Target, related_name='src_safe')
     43
     44    m2m_1 = models.ManyToManyField(Target, related_name='id')
     45    m2m_2 = models.ManyToManyField(Target, related_name='src_safe')
     46
     47class Target2(models.Model):
     48    clash3 = models.CharField(max_length=10)
     49    foreign_tgt = models.ForeignKey(Target)
     50    clashforeign_set = models.ForeignKey(Target)
     51
     52    m2m_tgt = models.ManyToManyField(Target)
     53    clashm2m_set = models.ManyToManyField(Target)
     54
     55class Clash3(models.Model):
     56    src_safe = models.CharField(max_length=10)
     57
     58    foreign_1 = models.ForeignKey(Target2, related_name='foreign_tgt')
     59    foreign_2 = models.ForeignKey(Target2, related_name='m2m_tgt')
     60
     61    m2m_1 = models.ManyToManyField(Target2, related_name='foreign_tgt')
     62    m2m_2 = models.ManyToManyField(Target2, related_name='m2m_tgt')
     63
     64class ClashForeign(models.Model):
     65    foreign = models.ForeignKey(Target2)
     66
     67class ClashM2M(models.Model):
     68    m2m = models.ManyToManyField(Target2)
     69
     70class SelfClashForeign(models.Model):
     71    src_safe = models.CharField(max_length=10)
     72    selfclashforeign = models.CharField(max_length=10)
     73
     74    selfclashforeign_set = models.ForeignKey("SelfClashForeign")
     75    foreign_1 = models.ForeignKey("SelfClashForeign", related_name='id')
     76    foreign_2 = models.ForeignKey("SelfClashForeign", related_name='src_safe')
     77
     78class ValidM2M(models.Model):
     79    src_safe = models.CharField(max_length=10)
     80    validm2m = models.CharField(max_length=10)
     81
     82    # M2M fields are symmetrical by default. Symmetrical M2M fields
     83    # on self don't require a related accessor, so many potential
     84    # clashes are avoided.
     85    validm2m_set = models.ManyToManyField("self")
     86
     87    m2m_1 = models.ManyToManyField("self", related_name='id')
     88    m2m_2 = models.ManyToManyField("self", related_name='src_safe')
     89
     90    m2m_3 = models.ManyToManyField('self')
     91    m2m_4 = models.ManyToManyField('self')
     92
     93class SelfClashM2M(models.Model):
     94    src_safe = models.CharField(max_length=10)
     95    selfclashm2m = models.CharField(max_length=10)
     96
     97    # Non-symmetrical M2M fields _do_ have related accessors, so
     98    # there is potential for clashes.
     99    selfclashm2m_set = models.ManyToManyField("self", symmetrical=False)
     100
     101    m2m_1 = models.ManyToManyField("self", related_name='id', symmetrical=False)
     102    m2m_2 = models.ManyToManyField("self", related_name='src_safe', symmetrical=False)
     103
     104    m2m_3 = models.ManyToManyField('self', symmetrical=False)
     105    m2m_4 = models.ManyToManyField('self', symmetrical=False)
     106
     107class Model(models.Model):
     108    "But it's valid to call a model Model."
     109    year = models.PositiveIntegerField() #1960
     110    make = models.CharField(max_length=10) #Aston Martin
     111    name = models.CharField(max_length=10) #DB 4 GT
     112
     113class Car(models.Model):
     114    colour = models.CharField(max_length=5)
     115    model = models.ForeignKey(Model)
     116
     117class MissingRelations(models.Model):
     118    rel1 = models.ForeignKey("Rel1")
     119    rel2 = models.ManyToManyField("Rel2")
     120
     121class MissingManualM2MModel(models.Model):
     122    name = models.CharField(max_length=5)
     123    missing_m2m = models.ManyToManyField(Model, through="MissingM2MModel")
     124
     125class Person(models.Model):
     126    name = models.CharField(max_length=5)
     127
     128class Group(models.Model):
     129    name = models.CharField(max_length=5)
     130    primary = models.ManyToManyField(Person, through="Membership", related_name="primary")
     131    secondary = models.ManyToManyField(Person, through="Membership", related_name="secondary")
     132    tertiary = models.ManyToManyField(Person, through="RelationshipDoubleFK", related_name="tertiary")
     133
     134class GroupTwo(models.Model):
     135    name = models.CharField(max_length=5)
     136    primary = models.ManyToManyField(Person, through="Membership")
     137    secondary = models.ManyToManyField(Group, through="MembershipMissingFK")
     138
     139class Membership(models.Model):
     140    person = models.ForeignKey(Person)
     141    group = models.ForeignKey(Group)
     142    not_default_or_null = models.CharField(max_length=5)
     143
     144class MembershipMissingFK(models.Model):
     145    person = models.ForeignKey(Person)
     146
     147class PersonSelfRefM2M(models.Model):
     148    name = models.CharField(max_length=5)
     149    friends = models.ManyToManyField('self', through="Relationship")
     150    too_many_friends = models.ManyToManyField('self', through="RelationshipTripleFK")
     151
     152class PersonSelfRefM2MExplicit(models.Model):
     153    name = models.CharField(max_length=5)
     154    friends = models.ManyToManyField('self', through="ExplicitRelationship", symmetrical=True)
     155
     156class Relationship(models.Model):
     157    first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set")
     158    second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set")
     159    date_added = models.DateTimeField()
     160
     161class ExplicitRelationship(models.Model):
     162    first = models.ForeignKey(PersonSelfRefM2MExplicit, related_name="rel_from_set")
     163    second = models.ForeignKey(PersonSelfRefM2MExplicit, related_name="rel_to_set")
     164    date_added = models.DateTimeField()
     165
     166class RelationshipTripleFK(models.Model):
     167    first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set_2")
     168    second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set_2")
     169    third = models.ForeignKey(PersonSelfRefM2M, related_name="too_many_by_far")
     170    date_added = models.DateTimeField()
     171
     172class RelationshipDoubleFK(models.Model):
     173    first = models.ForeignKey(Person, related_name="first_related_name")
     174    second = models.ForeignKey(Person, related_name="second_related_name")
     175    third = models.ForeignKey(Group, related_name="rel_to_set")
     176    date_added = models.DateTimeField()
     177
     178class AbstractModel(models.Model):
     179    name = models.CharField(max_length=10)
     180    class Meta:
     181        abstract = True
     182
     183class AbstractRelationModel(models.Model):
     184    fk1 = models.ForeignKey('AbstractModel')
     185    fk2 = models.ManyToManyField('AbstractModel')
     186
     187class UniqueM2M(models.Model):
     188    """ Model to test for unique ManyToManyFields, which are invalid. """
     189    unique_people = models.ManyToManyField(Person, unique=True)
     190
     191class NonUniqueFKTarget1(models.Model):
     192    """ Model to test for non-unique FK target in yet-to-be-defined model: expect an error """
     193    tgt = models.ForeignKey('FKTarget', to_field='bad')
     194
     195class UniqueFKTarget1(models.Model):
     196    """ Model to test for unique FK target in yet-to-be-defined model: expect no error """
     197    tgt = models.ForeignKey('FKTarget', to_field='good')
     198
     199class FKTarget(models.Model):
     200    bad = models.IntegerField()
     201    good = models.IntegerField(unique=True)
     202
     203class NonUniqueFKTarget2(models.Model):
     204    """ Model to test for non-unique FK target in previously seen model: expect an error """
     205    tgt = models.ForeignKey(FKTarget, to_field='bad')
     206
     207class UniqueFKTarget2(models.Model):
     208    """ Model to test for unique FK target in previously seen model: expect no error """
     209    tgt = models.ForeignKey(FKTarget, to_field='good')
     210
     211class NonExistingOrderingWithSingleUnderscore(models.Model):
     212    class Meta:
     213        ordering = ("does_not_exist",)
     214
     215class InvalidSetNull(models.Model):
     216    fk = models.ForeignKey('self', on_delete=models.SET_NULL)
     217
     218class InvalidSetDefault(models.Model):
     219    fk = models.ForeignKey('self', on_delete=models.SET_DEFAULT)
     220
     221model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute that is a positive integer.
     222invalid_models.fielderrors: "charfield2": CharFields require a "max_length" attribute that is a positive integer.
     223invalid_models.fielderrors: "charfield3": CharFields require a "max_length" attribute that is a positive integer.
     224invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute that is a non-negative integer.
     225invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute that is a positive integer.
     226invalid_models.fielderrors: "decimalfield2": DecimalFields require a "decimal_places" attribute that is a non-negative integer.
     227invalid_models.fielderrors: "decimalfield2": DecimalFields require a "max_digits" attribute that is a positive integer.
     228invalid_models.fielderrors: "decimalfield3": DecimalFields require a "decimal_places" attribute that is a non-negative integer.
     229invalid_models.fielderrors: "decimalfield3": DecimalFields require a "max_digits" attribute that is a positive integer.
     230invalid_models.fielderrors: "decimalfield4": DecimalFields require a "max_digits" attribute value that is greater than the value of the "decimal_places" attribute.
     231invalid_models.fielderrors: "decimalfield5": DecimalFields require a "max_digits" attribute value that is greater than the value of the "decimal_places" attribute.
     232invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
     233invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list).
     234invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
     235invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
     236invalid_models.fielderrors: "index": "db_index" should be either None, True or False.
     237invalid_models.fielderrors: "field_": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.
     238invalid_models.fielderrors: "nullbool": BooleanFields do not accept null values. Use a NullBooleanField instead.
     239invalid_models.clash1: Accessor for field 'foreign' clashes with field 'Target.clash1_set'. Add a related_name argument to the definition for 'foreign'.
     240invalid_models.clash1: Accessor for field 'foreign' clashes with related m2m field 'Target.clash1_set'. Add a related_name argument to the definition for 'foreign'.
     241invalid_models.clash1: Reverse query name for field 'foreign' clashes with field 'Target.clash1'. Add a related_name argument to the definition for 'foreign'.
     242invalid_models.clash1: Accessor for m2m field 'm2m' clashes with field 'Target.clash1_set'. Add a related_name argument to the definition for 'm2m'.
     243invalid_models.clash1: Accessor for m2m field 'm2m' clashes with related field 'Target.clash1_set'. Add a related_name argument to the definition for 'm2m'.
     244invalid_models.clash1: Reverse query name for m2m field 'm2m' clashes with field 'Target.clash1'. Add a related_name argument to the definition for 'm2m'.
     245invalid_models.clash2: Accessor for field 'foreign_1' clashes with field 'Target.id'. Add a related_name argument to the definition for 'foreign_1'.
     246invalid_models.clash2: Accessor for field 'foreign_1' clashes with related m2m field 'Target.id'. Add a related_name argument to the definition for 'foreign_1'.
     247invalid_models.clash2: Reverse query name for field 'foreign_1' clashes with field 'Target.id'. Add a related_name argument to the definition for 'foreign_1'.
     248invalid_models.clash2: Reverse query name for field 'foreign_1' clashes with related m2m field 'Target.id'. Add a related_name argument to the definition for 'foreign_1'.
     249invalid_models.clash2: Accessor for field 'foreign_2' clashes with related m2m field 'Target.src_safe'. Add a related_name argument to the definition for 'foreign_2'.
     250invalid_models.clash2: Reverse query name for field 'foreign_2' clashes with related m2m field 'Target.src_safe'. Add a related_name argument to the definition for 'foreign_2'.
     251invalid_models.clash2: Accessor for m2m field 'm2m_1' clashes with field 'Target.id'. Add a related_name argument to the definition for 'm2m_1'.
     252invalid_models.clash2: Accessor for m2m field 'm2m_1' clashes with related field 'Target.id'. Add a related_name argument to the definition for 'm2m_1'.
     253invalid_models.clash2: Reverse query name for m2m field 'm2m_1' clashes with field 'Target.id'. Add a related_name argument to the definition for 'm2m_1'.
     254invalid_models.clash2: Reverse query name for m2m field 'm2m_1' clashes with related field 'Target.id'. Add a related_name argument to the definition for 'm2m_1'.
     255invalid_models.clash2: Accessor for m2m field 'm2m_2' clashes with related field 'Target.src_safe'. Add a related_name argument to the definition for 'm2m_2'.
     256invalid_models.clash2: Reverse query name for m2m field 'm2m_2' clashes with related field 'Target.src_safe'. Add a related_name argument to the definition for 'm2m_2'.
     257invalid_models.clash3: Accessor for field 'foreign_1' clashes with field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'foreign_1'.
     258invalid_models.clash3: Accessor for field 'foreign_1' clashes with related m2m field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'foreign_1'.
     259invalid_models.clash3: Reverse query name for field 'foreign_1' clashes with field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'foreign_1'.
     260invalid_models.clash3: Reverse query name for field 'foreign_1' clashes with related m2m field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'foreign_1'.
     261invalid_models.clash3: Accessor for field 'foreign_2' clashes with m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'foreign_2'.
     262invalid_models.clash3: Accessor for field 'foreign_2' clashes with related m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'foreign_2'.
     263invalid_models.clash3: Reverse query name for field 'foreign_2' clashes with m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'foreign_2'.
     264invalid_models.clash3: Reverse query name for field 'foreign_2' clashes with related m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'foreign_2'.
     265invalid_models.clash3: Accessor for m2m field 'm2m_1' clashes with field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'm2m_1'.
     266invalid_models.clash3: Accessor for m2m field 'm2m_1' clashes with related field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'm2m_1'.
     267invalid_models.clash3: Reverse query name for m2m field 'm2m_1' clashes with field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'm2m_1'.
     268invalid_models.clash3: Reverse query name for m2m field 'm2m_1' clashes with related field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'm2m_1'.
     269invalid_models.clash3: Accessor for m2m field 'm2m_2' clashes with m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'm2m_2'.
     270invalid_models.clash3: Accessor for m2m field 'm2m_2' clashes with related field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'm2m_2'.
     271invalid_models.clash3: Reverse query name for m2m field 'm2m_2' clashes with m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'm2m_2'.
     272invalid_models.clash3: Reverse query name for m2m field 'm2m_2' clashes with related field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'm2m_2'.
     273invalid_models.clashforeign: Accessor for field 'foreign' clashes with field 'Target2.clashforeign_set'. Add a related_name argument to the definition for 'foreign'.
     274invalid_models.clashm2m: Accessor for m2m field 'm2m' clashes with m2m field 'Target2.clashm2m_set'. Add a related_name argument to the definition for 'm2m'.
     275invalid_models.target2: Accessor for field 'foreign_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'foreign_tgt'.
     276invalid_models.target2: Accessor for field 'foreign_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'foreign_tgt'.
     277invalid_models.target2: Accessor for field 'foreign_tgt' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'foreign_tgt'.
     278invalid_models.target2: Accessor for field 'clashforeign_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashforeign_set'.
     279invalid_models.target2: Accessor for field 'clashforeign_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashforeign_set'.
     280invalid_models.target2: Accessor for field 'clashforeign_set' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'clashforeign_set'.
     281invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
     282invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
     283invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
     284invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
     285invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
     286invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
     287invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
     288invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
     289invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
     290invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
     291invalid_models.selfclashforeign: Accessor for field 'selfclashforeign_set' clashes with field 'SelfClashForeign.selfclashforeign_set'. Add a related_name argument to the definition for 'selfclashforeign_set'.
     292invalid_models.selfclashforeign: Reverse query name for field 'selfclashforeign_set' clashes with field 'SelfClashForeign.selfclashforeign'. Add a related_name argument to the definition for 'selfclashforeign_set'.
     293invalid_models.selfclashforeign: Accessor for field 'foreign_1' clashes with field 'SelfClashForeign.id'. Add a related_name argument to the definition for 'foreign_1'.
     294invalid_models.selfclashforeign: Reverse query name for field 'foreign_1' clashes with field 'SelfClashForeign.id'. Add a related_name argument to the definition for 'foreign_1'.
     295invalid_models.selfclashforeign: Accessor for field 'foreign_2' clashes with field 'SelfClashForeign.src_safe'. Add a related_name argument to the definition for 'foreign_2'.
     296invalid_models.selfclashforeign: Reverse query name for field 'foreign_2' clashes with field 'SelfClashForeign.src_safe'. Add a related_name argument to the definition for 'foreign_2'.
     297invalid_models.selfclashm2m: Accessor for m2m field 'selfclashm2m_set' clashes with m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'selfclashm2m_set'.
     298invalid_models.selfclashm2m: Reverse query name for m2m field 'selfclashm2m_set' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'selfclashm2m_set'.
     299invalid_models.selfclashm2m: Accessor for m2m field 'selfclashm2m_set' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'selfclashm2m_set'.
     300invalid_models.selfclashm2m: Accessor for m2m field 'm2m_1' clashes with field 'SelfClashM2M.id'. Add a related_name argument to the definition for 'm2m_1'.
     301invalid_models.selfclashm2m: Accessor for m2m field 'm2m_2' clashes with field 'SelfClashM2M.src_safe'. Add a related_name argument to the definition for 'm2m_2'.
     302invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_1' clashes with field 'SelfClashM2M.id'. Add a related_name argument to the definition for 'm2m_1'.
     303invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_2' clashes with field 'SelfClashM2M.src_safe'. Add a related_name argument to the definition for 'm2m_2'.
     304invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_3'.
     305invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_3'.
     306invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_3'.
     307invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'.
     308invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'.
     309invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'.
     310invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_3' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_3'.
     311invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_4' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_4'.
     312invalid_models.missingrelations: 'rel1' has a relation with model Rel1, which has either not been installed or is abstract.
     313invalid_models.missingrelations: 'rel2' has an m2m relation with model Rel2, which has either not been installed or is abstract.
     314invalid_models.grouptwo: 'primary' is a manually-defined m2m relation through model Membership, which does not have foreign keys to Person and GroupTwo
     315invalid_models.grouptwo: 'secondary' is a manually-defined m2m relation through model MembershipMissingFK, which does not have foreign keys to Group and GroupTwo
     316invalid_models.missingmanualm2mmodel: 'missing_m2m' specifies an m2m relation through model MissingM2MModel, which has not been installed
     317invalid_models.group: The model Group has two manually-defined m2m relations through the model Membership, which is not permitted. Please consider using an extra field on your intermediary model instead.
     318invalid_models.group: Intermediary model RelationshipDoubleFK has more than one foreign key to Person, which is ambiguous and is not permitted.
     319invalid_models.personselfrefm2m: Many-to-many fields with intermediate tables cannot be symmetrical.
     320invalid_models.personselfrefm2m: Intermediary model RelationshipTripleFK has more than two foreign keys to PersonSelfRefM2M, which is ambiguous and is not permitted.
     321invalid_models.personselfrefm2mexplicit: Many-to-many fields with intermediate tables cannot be symmetrical.
     322invalid_models.abstractrelationmodel: 'fk1' has a relation with model AbstractModel, which has either not been installed or is abstract.
     323invalid_models.abstractrelationmodel: 'fk2' has an m2m relation with model AbstractModel, which has either not been installed or is abstract.
     324invalid_models.uniquem2m: ManyToManyFields cannot be unique.  Remove the unique argument on 'unique_people'.
     325invalid_models.nonuniquefktarget1: Field 'bad' under model 'FKTarget' must have a unique=True constraint.
     326invalid_models.nonuniquefktarget2: Field 'bad' under model 'FKTarget' must have a unique=True constraint.
     327invalid_models.nonexistingorderingwithsingleunderscore: "ordering" refers to "does_not_exist", a field that doesn't exist.
     328invalid_models.invalidsetnull: 'fk' specifies on_delete=SET_NULL, but cannot be null.
     329invalid_models.invalidsetdefault: 'fk' specifies on_delete=SET_DEFAULT, but has no default value.
     330"""
  • tests/modeltests/invalid_models/models.py

    diff -r 9a1f08c480bf tests/modeltests/invalid_models/models.py
    a b  
    1 """
    2 26. Invalid models
    3 
    4 This example exists purely to point out errors in models.
    5 """
    6 
    7 from django.db import models
    8 
    9 class FieldErrors(models.Model):
    10     charfield = models.CharField()
    11     charfield2 = models.CharField(max_length=-1)
    12     charfield3 = models.CharField(max_length="bad")
    13     decimalfield = models.DecimalField()
    14     decimalfield2 = models.DecimalField(max_digits=-1, decimal_places=-1)
    15     decimalfield3 = models.DecimalField(max_digits="bad", decimal_places="bad")
    16     decimalfield4 = models.DecimalField(max_digits=9, decimal_places=10)
    17     decimalfield5 = models.DecimalField(max_digits=10, decimal_places=10)
    18     filefield = models.FileField()
    19     choices = models.CharField(max_length=10, choices='bad')
    20     choices2 = models.CharField(max_length=10, choices=[(1,2,3),(1,2,3)])
    21     index = models.CharField(max_length=10, db_index='bad')
    22     field_ = models.CharField(max_length=10)
    23     nullbool = models.BooleanField(null=True)
    24 
    25 class Target(models.Model):
    26     tgt_safe = models.CharField(max_length=10)
    27     clash1 = models.CharField(max_length=10)
    28     clash2 = models.CharField(max_length=10)
    29 
    30     clash1_set = models.CharField(max_length=10)
    31 
    32 class Clash1(models.Model):
    33     src_safe = models.CharField(max_length=10)
    34 
    35     foreign = models.ForeignKey(Target)
    36     m2m = models.ManyToManyField(Target)
    37 
    38 class Clash2(models.Model):
    39     src_safe = models.CharField(max_length=10)
    40 
    41     foreign_1 = models.ForeignKey(Target, related_name='id')
    42     foreign_2 = models.ForeignKey(Target, related_name='src_safe')
    43 
    44     m2m_1 = models.ManyToManyField(Target, related_name='id')
    45     m2m_2 = models.ManyToManyField(Target, related_name='src_safe')
    46 
    47 class Target2(models.Model):
    48     clash3 = models.CharField(max_length=10)
    49     foreign_tgt = models.ForeignKey(Target)
    50     clashforeign_set = models.ForeignKey(Target)
    51 
    52     m2m_tgt = models.ManyToManyField(Target)
    53     clashm2m_set = models.ManyToManyField(Target)
    54 
    55 class Clash3(models.Model):
    56     src_safe = models.CharField(max_length=10)
    57 
    58     foreign_1 = models.ForeignKey(Target2, related_name='foreign_tgt')
    59     foreign_2 = models.ForeignKey(Target2, related_name='m2m_tgt')
    60 
    61     m2m_1 = models.ManyToManyField(Target2, related_name='foreign_tgt')
    62     m2m_2 = models.ManyToManyField(Target2, related_name='m2m_tgt')
    63 
    64 class ClashForeign(models.Model):
    65     foreign = models.ForeignKey(Target2)
    66 
    67 class ClashM2M(models.Model):
    68     m2m = models.ManyToManyField(Target2)
    69 
    70 class SelfClashForeign(models.Model):
    71     src_safe = models.CharField(max_length=10)
    72     selfclashforeign = models.CharField(max_length=10)
    73 
    74     selfclashforeign_set = models.ForeignKey("SelfClashForeign")
    75     foreign_1 = models.ForeignKey("SelfClashForeign", related_name='id')
    76     foreign_2 = models.ForeignKey("SelfClashForeign", related_name='src_safe')
    77 
    78 class ValidM2M(models.Model):
    79     src_safe = models.CharField(max_length=10)
    80     validm2m = models.CharField(max_length=10)
    81 
    82     # M2M fields are symmetrical by default. Symmetrical M2M fields
    83     # on self don't require a related accessor, so many potential
    84     # clashes are avoided.
    85     validm2m_set = models.ManyToManyField("self")
    86 
    87     m2m_1 = models.ManyToManyField("self", related_name='id')
    88     m2m_2 = models.ManyToManyField("self", related_name='src_safe')
    89 
    90     m2m_3 = models.ManyToManyField('self')
    91     m2m_4 = models.ManyToManyField('self')
    92 
    93 class SelfClashM2M(models.Model):
    94     src_safe = models.CharField(max_length=10)
    95     selfclashm2m = models.CharField(max_length=10)
    96 
    97     # Non-symmetrical M2M fields _do_ have related accessors, so
    98     # there is potential for clashes.
    99     selfclashm2m_set = models.ManyToManyField("self", symmetrical=False)
    100 
    101     m2m_1 = models.ManyToManyField("self", related_name='id', symmetrical=False)
    102     m2m_2 = models.ManyToManyField("self", related_name='src_safe', symmetrical=False)
    103 
    104     m2m_3 = models.ManyToManyField('self', symmetrical=False)
    105     m2m_4 = models.ManyToManyField('self', symmetrical=False)
    106 
    107 class Model(models.Model):
    108     "But it's valid to call a model Model."
    109     year = models.PositiveIntegerField() #1960
    110     make = models.CharField(max_length=10) #Aston Martin
    111     name = models.CharField(max_length=10) #DB 4 GT
    112 
    113 class Car(models.Model):
    114     colour = models.CharField(max_length=5)
    115     model = models.ForeignKey(Model)
    116 
    117 class MissingRelations(models.Model):
    118     rel1 = models.ForeignKey("Rel1")
    119     rel2 = models.ManyToManyField("Rel2")
    120 
    121 class MissingManualM2MModel(models.Model):
    122     name = models.CharField(max_length=5)
    123     missing_m2m = models.ManyToManyField(Model, through="MissingM2MModel")
    124 
    125 class Person(models.Model):
    126     name = models.CharField(max_length=5)
    127 
    128 class Group(models.Model):
    129     name = models.CharField(max_length=5)
    130     primary = models.ManyToManyField(Person, through="Membership", related_name="primary")
    131     secondary = models.ManyToManyField(Person, through="Membership", related_name="secondary")
    132     tertiary = models.ManyToManyField(Person, through="RelationshipDoubleFK", related_name="tertiary")
    133 
    134 class GroupTwo(models.Model):
    135     name = models.CharField(max_length=5)
    136     primary = models.ManyToManyField(Person, through="Membership")
    137     secondary = models.ManyToManyField(Group, through="MembershipMissingFK")
    138 
    139 class Membership(models.Model):
    140     person = models.ForeignKey(Person)
    141     group = models.ForeignKey(Group)
    142     not_default_or_null = models.CharField(max_length=5)
    143 
    144 class MembershipMissingFK(models.Model):
    145     person = models.ForeignKey(Person)
    146 
    147 class PersonSelfRefM2M(models.Model):
    148     name = models.CharField(max_length=5)
    149     friends = models.ManyToManyField('self', through="Relationship")
    150     too_many_friends = models.ManyToManyField('self', through="RelationshipTripleFK")
    151 
    152 class PersonSelfRefM2MExplicit(models.Model):
    153     name = models.CharField(max_length=5)
    154     friends = models.ManyToManyField('self', through="ExplicitRelationship", symmetrical=True)
    155 
    156 class Relationship(models.Model):
    157     first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set")
    158     second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set")
    159     date_added = models.DateTimeField()
    160 
    161 class ExplicitRelationship(models.Model):
    162     first = models.ForeignKey(PersonSelfRefM2MExplicit, related_name="rel_from_set")
    163     second = models.ForeignKey(PersonSelfRefM2MExplicit, related_name="rel_to_set")
    164     date_added = models.DateTimeField()
    165 
    166 class RelationshipTripleFK(models.Model):
    167     first = models.ForeignKey(PersonSelfRefM2M, related_name="rel_from_set_2")
    168     second = models.ForeignKey(PersonSelfRefM2M, related_name="rel_to_set_2")
    169     third = models.ForeignKey(PersonSelfRefM2M, related_name="too_many_by_far")
    170     date_added = models.DateTimeField()
    171 
    172 class RelationshipDoubleFK(models.Model):
    173     first = models.ForeignKey(Person, related_name="first_related_name")
    174     second = models.ForeignKey(Person, related_name="second_related_name")
    175     third = models.ForeignKey(Group, related_name="rel_to_set")
    176     date_added = models.DateTimeField()
    177 
    178 class AbstractModel(models.Model):
    179     name = models.CharField(max_length=10)
    180     class Meta:
    181         abstract = True
    182 
    183 class AbstractRelationModel(models.Model):
    184     fk1 = models.ForeignKey('AbstractModel')
    185     fk2 = models.ManyToManyField('AbstractModel')
    186 
    187 class UniqueM2M(models.Model):
    188     """ Model to test for unique ManyToManyFields, which are invalid. """
    189     unique_people = models.ManyToManyField(Person, unique=True)
    190 
    191 class NonUniqueFKTarget1(models.Model):
    192     """ Model to test for non-unique FK target in yet-to-be-defined model: expect an error """
    193     tgt = models.ForeignKey('FKTarget', to_field='bad')
    194 
    195 class UniqueFKTarget1(models.Model):
    196     """ Model to test for unique FK target in yet-to-be-defined model: expect no error """
    197     tgt = models.ForeignKey('FKTarget', to_field='good')
    198 
    199 class FKTarget(models.Model):
    200     bad = models.IntegerField()
    201     good = models.IntegerField(unique=True)
    202 
    203 class NonUniqueFKTarget2(models.Model):
    204     """ Model to test for non-unique FK target in previously seen model: expect an error """
    205     tgt = models.ForeignKey(FKTarget, to_field='bad')
    206 
    207 class UniqueFKTarget2(models.Model):
    208     """ Model to test for unique FK target in previously seen model: expect no error """
    209     tgt = models.ForeignKey(FKTarget, to_field='good')
    210 
    211 class NonExistingOrderingWithSingleUnderscore(models.Model):
    212     class Meta:
    213         ordering = ("does_not_exist",)
    214 
    215 class InvalidSetNull(models.Model):
    216     fk = models.ForeignKey('self', on_delete=models.SET_NULL)
    217 
    218 class InvalidSetDefault(models.Model):
    219     fk = models.ForeignKey('self', on_delete=models.SET_DEFAULT)
    220 
    221 model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute that is a positive integer.
    222 invalid_models.fielderrors: "charfield2": CharFields require a "max_length" attribute that is a positive integer.
    223 invalid_models.fielderrors: "charfield3": CharFields require a "max_length" attribute that is a positive integer.
    224 invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute that is a non-negative integer.
    225 invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute that is a positive integer.
    226 invalid_models.fielderrors: "decimalfield2": DecimalFields require a "decimal_places" attribute that is a non-negative integer.
    227 invalid_models.fielderrors: "decimalfield2": DecimalFields require a "max_digits" attribute that is a positive integer.
    228 invalid_models.fielderrors: "decimalfield3": DecimalFields require a "decimal_places" attribute that is a non-negative integer.
    229 invalid_models.fielderrors: "decimalfield3": DecimalFields require a "max_digits" attribute that is a positive integer.
    230 invalid_models.fielderrors: "decimalfield4": DecimalFields require a "max_digits" attribute value that is greater than the value of the "decimal_places" attribute.
    231 invalid_models.fielderrors: "decimalfield5": DecimalFields require a "max_digits" attribute value that is greater than the value of the "decimal_places" attribute.
    232 invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
    233 invalid_models.fielderrors: "choices": "choices" should be iterable (e.g., a tuple or list).
    234 invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
    235 invalid_models.fielderrors: "choices2": "choices" should be a sequence of two-tuples.
    236 invalid_models.fielderrors: "index": "db_index" should be either None, True or False.
    237 invalid_models.fielderrors: "field_": Field names cannot end with underscores, because this would lead to ambiguous queryset filters.
    238 invalid_models.fielderrors: "nullbool": BooleanFields do not accept null values. Use a NullBooleanField instead.
    239 invalid_models.clash1: Accessor for field 'foreign' clashes with field 'Target.clash1_set'. Add a related_name argument to the definition for 'foreign'.
    240 invalid_models.clash1: Accessor for field 'foreign' clashes with related m2m field 'Target.clash1_set'. Add a related_name argument to the definition for 'foreign'.
    241 invalid_models.clash1: Reverse query name for field 'foreign' clashes with field 'Target.clash1'. Add a related_name argument to the definition for 'foreign'.
    242 invalid_models.clash1: Accessor for m2m field 'm2m' clashes with field 'Target.clash1_set'. Add a related_name argument to the definition for 'm2m'.
    243 invalid_models.clash1: Accessor for m2m field 'm2m' clashes with related field 'Target.clash1_set'. Add a related_name argument to the definition for 'm2m'.
    244 invalid_models.clash1: Reverse query name for m2m field 'm2m' clashes with field 'Target.clash1'. Add a related_name argument to the definition for 'm2m'.
    245 invalid_models.clash2: Accessor for field 'foreign_1' clashes with field 'Target.id'. Add a related_name argument to the definition for 'foreign_1'.
    246 invalid_models.clash2: Accessor for field 'foreign_1' clashes with related m2m field 'Target.id'. Add a related_name argument to the definition for 'foreign_1'.
    247 invalid_models.clash2: Reverse query name for field 'foreign_1' clashes with field 'Target.id'. Add a related_name argument to the definition for 'foreign_1'.
    248 invalid_models.clash2: Reverse query name for field 'foreign_1' clashes with related m2m field 'Target.id'. Add a related_name argument to the definition for 'foreign_1'.
    249 invalid_models.clash2: Accessor for field 'foreign_2' clashes with related m2m field 'Target.src_safe'. Add a related_name argument to the definition for 'foreign_2'.
    250 invalid_models.clash2: Reverse query name for field 'foreign_2' clashes with related m2m field 'Target.src_safe'. Add a related_name argument to the definition for 'foreign_2'.
    251 invalid_models.clash2: Accessor for m2m field 'm2m_1' clashes with field 'Target.id'. Add a related_name argument to the definition for 'm2m_1'.
    252 invalid_models.clash2: Accessor for m2m field 'm2m_1' clashes with related field 'Target.id'. Add a related_name argument to the definition for 'm2m_1'.
    253 invalid_models.clash2: Reverse query name for m2m field 'm2m_1' clashes with field 'Target.id'. Add a related_name argument to the definition for 'm2m_1'.
    254 invalid_models.clash2: Reverse query name for m2m field 'm2m_1' clashes with related field 'Target.id'. Add a related_name argument to the definition for 'm2m_1'.
    255 invalid_models.clash2: Accessor for m2m field 'm2m_2' clashes with related field 'Target.src_safe'. Add a related_name argument to the definition for 'm2m_2'.
    256 invalid_models.clash2: Reverse query name for m2m field 'm2m_2' clashes with related field 'Target.src_safe'. Add a related_name argument to the definition for 'm2m_2'.
    257 invalid_models.clash3: Accessor for field 'foreign_1' clashes with field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'foreign_1'.
    258 invalid_models.clash3: Accessor for field 'foreign_1' clashes with related m2m field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'foreign_1'.
    259 invalid_models.clash3: Reverse query name for field 'foreign_1' clashes with field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'foreign_1'.
    260 invalid_models.clash3: Reverse query name for field 'foreign_1' clashes with related m2m field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'foreign_1'.
    261 invalid_models.clash3: Accessor for field 'foreign_2' clashes with m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'foreign_2'.
    262 invalid_models.clash3: Accessor for field 'foreign_2' clashes with related m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'foreign_2'.
    263 invalid_models.clash3: Reverse query name for field 'foreign_2' clashes with m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'foreign_2'.
    264 invalid_models.clash3: Reverse query name for field 'foreign_2' clashes with related m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'foreign_2'.
    265 invalid_models.clash3: Accessor for m2m field 'm2m_1' clashes with field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'm2m_1'.
    266 invalid_models.clash3: Accessor for m2m field 'm2m_1' clashes with related field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'm2m_1'.
    267 invalid_models.clash3: Reverse query name for m2m field 'm2m_1' clashes with field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'm2m_1'.
    268 invalid_models.clash3: Reverse query name for m2m field 'm2m_1' clashes with related field 'Target2.foreign_tgt'. Add a related_name argument to the definition for 'm2m_1'.
    269 invalid_models.clash3: Accessor for m2m field 'm2m_2' clashes with m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'm2m_2'.
    270 invalid_models.clash3: Accessor for m2m field 'm2m_2' clashes with related field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'm2m_2'.
    271 invalid_models.clash3: Reverse query name for m2m field 'm2m_2' clashes with m2m field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'm2m_2'.
    272 invalid_models.clash3: Reverse query name for m2m field 'm2m_2' clashes with related field 'Target2.m2m_tgt'. Add a related_name argument to the definition for 'm2m_2'.
    273 invalid_models.clashforeign: Accessor for field 'foreign' clashes with field 'Target2.clashforeign_set'. Add a related_name argument to the definition for 'foreign'.
    274 invalid_models.clashm2m: Accessor for m2m field 'm2m' clashes with m2m field 'Target2.clashm2m_set'. Add a related_name argument to the definition for 'm2m'.
    275 invalid_models.target2: Accessor for field 'foreign_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'foreign_tgt'.
    276 invalid_models.target2: Accessor for field 'foreign_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'foreign_tgt'.
    277 invalid_models.target2: Accessor for field 'foreign_tgt' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'foreign_tgt'.
    278 invalid_models.target2: Accessor for field 'clashforeign_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashforeign_set'.
    279 invalid_models.target2: Accessor for field 'clashforeign_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashforeign_set'.
    280 invalid_models.target2: Accessor for field 'clashforeign_set' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'clashforeign_set'.
    281 invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
    282 invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
    283 invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
    284 invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
    285 invalid_models.target2: Accessor for m2m field 'm2m_tgt' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'm2m_tgt'.
    286 invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
    287 invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
    288 invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
    289 invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
    290 invalid_models.target2: Accessor for m2m field 'clashm2m_set' clashes with related m2m field 'Target.target2_set'. Add a related_name argument to the definition for 'clashm2m_set'.
    291 invalid_models.selfclashforeign: Accessor for field 'selfclashforeign_set' clashes with field 'SelfClashForeign.selfclashforeign_set'. Add a related_name argument to the definition for 'selfclashforeign_set'.
    292 invalid_models.selfclashforeign: Reverse query name for field 'selfclashforeign_set' clashes with field 'SelfClashForeign.selfclashforeign'. Add a related_name argument to the definition for 'selfclashforeign_set'.
    293 invalid_models.selfclashforeign: Accessor for field 'foreign_1' clashes with field 'SelfClashForeign.id'. Add a related_name argument to the definition for 'foreign_1'.
    294 invalid_models.selfclashforeign: Reverse query name for field 'foreign_1' clashes with field 'SelfClashForeign.id'. Add a related_name argument to the definition for 'foreign_1'.
    295 invalid_models.selfclashforeign: Accessor for field 'foreign_2' clashes with field 'SelfClashForeign.src_safe'. Add a related_name argument to the definition for 'foreign_2'.
    296 invalid_models.selfclashforeign: Reverse query name for field 'foreign_2' clashes with field 'SelfClashForeign.src_safe'. Add a related_name argument to the definition for 'foreign_2'.
    297 invalid_models.selfclashm2m: Accessor for m2m field 'selfclashm2m_set' clashes with m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'selfclashm2m_set'.
    298 invalid_models.selfclashm2m: Reverse query name for m2m field 'selfclashm2m_set' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'selfclashm2m_set'.
    299 invalid_models.selfclashm2m: Accessor for m2m field 'selfclashm2m_set' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'selfclashm2m_set'.
    300 invalid_models.selfclashm2m: Accessor for m2m field 'm2m_1' clashes with field 'SelfClashM2M.id'. Add a related_name argument to the definition for 'm2m_1'.
    301 invalid_models.selfclashm2m: Accessor for m2m field 'm2m_2' clashes with field 'SelfClashM2M.src_safe'. Add a related_name argument to the definition for 'm2m_2'.
    302 invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_1' clashes with field 'SelfClashM2M.id'. Add a related_name argument to the definition for 'm2m_1'.
    303 invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_2' clashes with field 'SelfClashM2M.src_safe'. Add a related_name argument to the definition for 'm2m_2'.
    304 invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_3'.
    305 invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_3'.
    306 invalid_models.selfclashm2m: Accessor for m2m field 'm2m_3' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_3'.
    307 invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'.
    308 invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'.
    309 invalid_models.selfclashm2m: Accessor for m2m field 'm2m_4' clashes with related m2m field 'SelfClashM2M.selfclashm2m_set'. Add a related_name argument to the definition for 'm2m_4'.
    310 invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_3' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_3'.
    311 invalid_models.selfclashm2m: Reverse query name for m2m field 'm2m_4' clashes with field 'SelfClashM2M.selfclashm2m'. Add a related_name argument to the definition for 'm2m_4'.
    312 invalid_models.missingrelations: 'rel1' has a relation with model Rel1, which has either not been installed or is abstract.
    313 invalid_models.missingrelations: 'rel2' has an m2m relation with model Rel2, which has either not been installed or is abstract.
    314 invalid_models.grouptwo: 'primary' is a manually-defined m2m relation through model Membership, which does not have foreign keys to Person and GroupTwo
    315 invalid_models.grouptwo: 'secondary' is a manually-defined m2m relation through model MembershipMissingFK, which does not have foreign keys to Group and GroupTwo
    316 invalid_models.missingmanualm2mmodel: 'missing_m2m' specifies an m2m relation through model MissingM2MModel, which has not been installed
    317 invalid_models.group: The model Group has two manually-defined m2m relations through the model Membership, which is not permitted. Please consider using an extra field on your intermediary model instead.
    318 invalid_models.group: Intermediary model RelationshipDoubleFK has more than one foreign key to Person, which is ambiguous and is not permitted.
    319 invalid_models.personselfrefm2m: Many-to-many fields with intermediate tables cannot be symmetrical.
    320 invalid_models.personselfrefm2m: Intermediary model RelationshipTripleFK has more than two foreign keys to PersonSelfRefM2M, which is ambiguous and is not permitted.
    321 invalid_models.personselfrefm2mexplicit: Many-to-many fields with intermediate tables cannot be symmetrical.
    322 invalid_models.abstractrelationmodel: 'fk1' has a relation with model AbstractModel, which has either not been installed or is abstract.
    323 invalid_models.abstractrelationmodel: 'fk2' has an m2m relation with model AbstractModel, which has either not been installed or is abstract.
    324 invalid_models.uniquem2m: ManyToManyFields cannot be unique.  Remove the unique argument on 'unique_people'.
    325 invalid_models.nonuniquefktarget1: Field 'bad' under model 'FKTarget' must have a unique=True constraint.
    326 invalid_models.nonuniquefktarget2: Field 'bad' under model 'FKTarget' must have a unique=True constraint.
    327 invalid_models.nonexistingorderingwithsingleunderscore: "ordering" refers to "does_not_exist", a field that doesn't exist.
    328 invalid_models.invalidsetnull: 'fk' specifies on_delete=SET_NULL, but cannot be null.
    329 invalid_models.invalidsetdefault: 'fk' specifies on_delete=SET_DEFAULT, but has no default value.
    330 """
  • new file tests/modeltests/invalid_models/tests.py

    diff -r 9a1f08c480bf tests/modeltests/invalid_models/tests.py
    - +  
     1import sys
     2
     3from django.utils import unittest
     4
     5
     6class InvalidModelTestCase(unittest.TestCase):
     7    """Import an appliation with invalid models and test the exceptions."""
     8
     9    def test_invalid_models(self):
     10        from django.core.management.validation import get_validation_errors
     11        from django.db.models.loading import load_app
     12        from cStringIO import StringIO
     13       
     14        try:
     15            module = load_app("modeltests.invalid_models.invalid_models")
     16        except Exception, e:
     17            self.fail('Unable to load invalid model module')
     18       
     19        # Make sure sys.stdout is not a tty so that we get errors without
     20        # coloring attached (makes matching the results easier). We restore
     21        # sys.stderr afterwards.
     22        orig_stdout = sys.stdout
     23        s = StringIO()
     24        sys.stdout = s
     25        count = get_validation_errors(s, module)
     26        sys.stdout = orig_stdout
     27        s.seek(0)
     28        error_log = s.read()
     29        actual = error_log.split('\n')
     30        expected = module.model_errors.split('\n')
     31
     32        unexpected = [err for err in actual if err not in expected]
     33        missing = [err for err in expected if err not in actual]
     34        self.assertFalse(unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
     35        self.assertFalse(missing, "Missing Errors: " + '\n'.join(missing))
     36
     37
  • tests/runtests.py

    diff -r 9a1f08c480bf tests/runtests.py
    a b  
    66import tempfile
    77
    88import django.contrib as contrib
    9 from django.utils import unittest
    109
    1110CONTRIB_DIR_NAME = 'django.contrib'
    1211MODEL_TESTS_DIR_NAME = 'modeltests'
     
    5049    modules = []
    5150    for loc, dirpath in (MODEL_TESTS_DIR_NAME, MODEL_TEST_DIR), (REGRESSION_TESTS_DIR_NAME, REGRESSION_TEST_DIR), (CONTRIB_DIR_NAME, CONTRIB_DIR):
    5251        for f in os.listdir(dirpath):
    53             if f.startswith('__init__') or f.startswith('.') or \
    54                f.startswith('sql') or f.startswith('invalid') or \
    55                os.path.basename(f) in REGRESSION_SUBDIRS_TO_SKIP:
     52            if (f.startswith('__init__') or
     53                f.startswith('.') or
     54                f.startswith('sql') or
     55                os.path.basename(f) in REGRESSION_SUBDIRS_TO_SKIP):
    5656                continue
    5757            modules.append((loc, f))
    5858    return modules
    5959
    60 def get_invalid_modules():
    61     modules = []
    62     for loc, dirpath in (MODEL_TESTS_DIR_NAME, MODEL_TEST_DIR), (REGRESSION_TESTS_DIR_NAME, REGRESSION_TEST_DIR), (CONTRIB_DIR_NAME, CONTRIB_DIR):
    63         for f in os.listdir(dirpath):
    64             if f.startswith('__init__') or f.startswith('.') or f.startswith('sql'):
    65                 continue
    66             if f.startswith('invalid'):
    67                 modules.append((loc, f))
    68     return modules
    69 
    70 class InvalidModelTestCase(unittest.TestCase):
    71     def __init__(self, module_label):
    72         unittest.TestCase.__init__(self)
    73         self.module_label = module_label
    74 
    75     def runTest(self):
    76         from django.core.management.validation import get_validation_errors
    77         from django.db.models.loading import load_app
    78         from cStringIO import StringIO
    79 
    80         try:
    81             module = load_app(self.module_label)
    82         except Exception, e:
    83             self.fail('Unable to load invalid model module')
    84 
    85         # Make sure sys.stdout is not a tty so that we get errors without
    86         # coloring attached (makes matching the results easier). We restore
    87         # sys.stderr afterwards.
    88         orig_stdout = sys.stdout
    89         s = StringIO()
    90         sys.stdout = s
    91         count = get_validation_errors(s, module)
    92         sys.stdout = orig_stdout
    93         s.seek(0)
    94         error_log = s.read()
    95         actual = error_log.split('\n')
    96         expected = module.model_errors.split('\n')
    97 
    98         unexpected = [err for err in actual if err not in expected]
    99         missing = [err for err in expected if err not in actual]
    100 
    101         self.assertTrue(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
    102         self.assertTrue(not missing, "Missing Errors: " + '\n'.join(missing))
    103 
    10460def setup(verbosity, test_labels):
    10561    from django.conf import settings
    10662    state = {
     
    178134def django_tests(verbosity, interactive, failfast, test_labels):
    179135    from django.conf import settings
    180136    state = setup(verbosity, test_labels)
    181 
    182     # Add tests for invalid models apps.
    183137    extra_tests = []
    184     for module_dir, module_name in get_invalid_modules():
    185         module_label = '.'.join([module_dir, module_name])
    186         if not test_labels or module_name in test_labels:
    187             extra_tests.append(InvalidModelTestCase(module_label))
    188             try:
    189                 # Invalid models are not working apps, so we cannot pass them into
    190                 # the test runner with the other test_labels
    191                 test_labels.remove(module_name)
    192             except ValueError:
    193                 pass
    194138
    195139    # If GeoDjango is used, add it's tests that aren't a part of
    196140    # an application (e.g., GEOS, GDAL, Distance objects).
Back to Top