Ticket #17673: 17673.diff

File 17673.diff, 8.2 KB (added by Anssi Kääriäinen, 12 years ago)
  • django/core/management/validation.py

    diff --git a/django/core/management/validation.py b/django/core/management/validation.py
    index f0b5b1c..a91ef81 100644
    a b def get_validation_errors(outfile, app=None):  
    3232
    3333    for cls in models.get_models(app):
    3434        opts = cls._meta
     35        # Keep record of used field names. Shadowing field names in inheritance
     36        # cases is not allowed as this will lead to buggy behavior. See ticket
     37        # #17673 for details.
     38        # It is also possible to define clashing names inside a single model:
     39        #   f = models.ForeignKey()
     40        #   f_id = models.IntegerField
     41        # => f.attname clashes with f_id.name.
     42        used_fields = {} # name or attname -> field
     43        for parent in opts.parents:
     44            for f in parent._meta.local_fields:
     45                # Check that multi-inheritance doesn't cause field name
     46                # shadowing.
     47                clash = used_fields.get(f.name) or used_fields.get(f.attname) or None
     48                if clash:
     49                    e.add(opts, 'The field "%s" from parent model "%s" clashes with the field '
     50                                '"%s" from another parent model "%s"' % (
     51                          f.name, f.model._meta, clash.name, clash.model._meta
     52                    ))
     53                used_fields[f.name] = f
     54                used_fields[f.attname] = f
    3555
    3656        # Do field-specific validation.
    3757        for f in opts.local_fields:
     58            clash = used_fields.get(f.name) or used_fields.get(f.attname) or None
     59            if clash:
     60                e.add(opts, '"%s": This field clashes with field "%s" from "%s"' %
     61                            (f.name, clash.name, clash.model._meta))
     62            used_fields[f.name] = f
     63            used_fields[f.attname] = f
     64            if f.name == 'pk':
     65                e.add(opts, '"%s": You can\'t use "pk" as a field name. It is a reserved name.' %
     66                                   f.name)
     67            if '__' in f.name:
     68                e.add(opts, '"%s": Field\'s name must not contain "__".' % f.name)
    3869            if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
    3970                e.add(opts, '"%s": You can\'t use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.' % f.name)
    4071            if f.name.endswith('_'):
  • tests/modeltests/invalid_models/invalid_models/models.py

    diff --git a/tests/modeltests/invalid_models/invalid_models/models.py b/tests/modeltests/invalid_models/invalid_models/models.py
    index ed69fb6..a870417 100644
    a b class OrderByPKModel(models.Model):  
    243243    class Meta:
    244244        ordering = ('pk',)
    245245
     246class InvalidFieldNames(models.Model):
     247    pk = models.IntegerField()
     248    some__field = models.IntegerField()
     249
     250class Parent1(models.Model):
     251    somef_id = models.IntegerField()
     252    someotherf = models.IntegerField()
     253
     254class Parent2(models.Model):
     255    somef_id = models.IntegerField()
     256
     257class Child1(Parent1):
     258    somef = models.ForeignKey(Parent2)
     259
     260class MultiInheritanceClash(Parent1, Parent2):
     261    # Here we have two clashed: id (automatic field) and somef, because
     262    # both parents define these fields.
     263    pass
     264
     265class InternalClashingNames(models.Model):
     266    # fk.attname must not clash with fk_id.name
     267    fk = models.ForeignKey(Parent1)
     268    fk_id = models.IntegerField()
     269
    246270model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute that is a positive integer.
    247271invalid_models.fielderrors: "charfield2": CharFields require a "max_length" attribute that is a positive integer.
    248272invalid_models.fielderrors: "charfield3": CharFields require a "max_length" attribute that is a positive integer.
    invalid_models.nonuniquefktarget2: Field 'bad' under model 'FKTarget' must have  
    351375invalid_models.nonexistingorderingwithsingleunderscore: "ordering" refers to "does_not_exist", a field that doesn't exist.
    352376invalid_models.invalidsetnull: 'fk' specifies on_delete=SET_NULL, but cannot be null.
    353377invalid_models.invalidsetdefault: 'fk' specifies on_delete=SET_DEFAULT, but has no default value.
     378invalid_models.invalidfieldnames: "pk": You can't use "pk" as a field name. It is a reserved name.
     379invalid_models.invalidfieldnames: "some__field": Field's name must not contain "__".
     380invalid_models.child1: "somef": This field clashes with field "somef_id" from "invalid_models.parent1"
     381invalid_models.multiinheritanceclash: The field "id" from parent model "invalid_models.parent2" clashes with the field "id" from another parent model "invalid_models.parent1"
     382invalid_models.multiinheritanceclash: The field "somef_id" from parent model "invalid_models.parent2" clashes with the field "somef_id" from another parent model "invalid_models.parent1"
     383invalid_models.internalclashingnames: "fk_id": This field clashes with field "fk" from "invalid_models.internalclashingnames"
    354384"""
    355385
    356386if not connection.features.interprets_empty_strings_as_nulls:
  • tests/modeltests/model_inheritance/models.py

    diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py
    index a0fed8a..d2afce7 100644
    a b class Student(CommonInfo):  
    3838    class Meta:
    3939        pass
    4040
    41 class StudentWorker(Student, Worker):
    42     pass
     41# This is invalid. The parent student and worker can have different names,
     42# ages and most of all different id. However, the child model can have just
     43# single name, age and id. So, what you get in Python doesn't match what you
     44# have in the DB.
     45#class StudentWorker(Student, Worker):
     46#    pass
    4347
    4448#
    4549# Abstract base classes with related models
  • tests/modeltests/model_inheritance/tests.py

    diff --git a/tests/modeltests/model_inheritance/tests.py b/tests/modeltests/model_inheritance/tests.py
    index 2e1a7a5..19cda4c 100644
    a b from django.core.exceptions import FieldError  
    66from django.test import TestCase
    77
    88from .models import (Chef, CommonInfo, ItalianRestaurant, ParkingLot, Place,
    9     Post, Restaurant, Student, StudentWorker, Supplier, Worker, MixinModel)
     9    Post, Restaurant, Student, Supplier, Worker, MixinModel)
    1010
    1111
    1212class ModelInheritanceTests(TestCase):
    class ModelInheritanceTests(TestCase):  
    4545
    4646        # A StudentWorker which does not exist is both a Student and Worker
    4747        # which does not exist.
    48         self.assertRaises(Student.DoesNotExist,
    49             StudentWorker.objects.get, pk=12321321
    50         )
    51         self.assertRaises(Worker.DoesNotExist,
    52             StudentWorker.objects.get, pk=12321321
    53         )
     48        #self.assertRaises(Student.DoesNotExist,
     49        #    StudentWorker.objects.get, pk=12321321
     50        #)
     51        #self.assertRaises(Worker.DoesNotExist,
     52        #    StudentWorker.objects.get, pk=12321321
     53        #)
    5454
    5555        # MultipleObjectsReturned is also inherited.
    5656        # This is written out "long form", rather than using __init__/create()
    5757        # because of a bug with diamond inheritance (#10808)
    58         sw1 = StudentWorker()
    59         sw1.name = "Wilma"
    60         sw1.age = 35
    61         sw1.save()
    62         sw2 = StudentWorker()
    63         sw2.name = "Betty"
    64         sw2.age = 24
    65         sw2.save()
    66 
    67         self.assertRaises(Student.MultipleObjectsReturned,
    68             StudentWorker.objects.get, pk__lt=sw2.pk + 100
    69         )
    70         self.assertRaises(Worker.MultipleObjectsReturned,
    71             StudentWorker.objects.get, pk__lt=sw2.pk + 100
    72         )
     58        #sw1 = StudentWorker()
     59        #sw1.name = "Wilma"
     60        #sw1.age = 35
     61        #sw1.save()
     62        #sw2 = StudentWorker()
     63        #sw2.name = "Betty"
     64        #sw2.age = 24
     65        #sw2.save()
     66
     67        #self.assertRaises(Student.MultipleObjectsReturned,
     68        #    StudentWorker.objects.get, pk__lt=sw2.pk + 100
     69        #)
     70        #self.assertRaises(Worker.MultipleObjectsReturned,
     71        #    StudentWorker.objects.get, pk__lt=sw2.pk + 100
     72        #)
    7373
    7474    def test_multiple_table(self):
    7575        post = Post.objects.create(title="Lorem Ipsum")
Back to Top