Commit 948d209a authored by Simon Charette's avatar Simon Charette
Browse files

Fixed #21217 -- Avoid connecting `(pre|post)_init` signals to abstract senders.

parent dc3d2ac9
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@ from django.db.models.fields.related import ForeignObject, ForeignObjectRel
from django.db.models.related import PathInfo
from django.db.models.sql.where import Constraint
from django.forms import ModelForm, ALL_FIELDS
from django.forms.models import (BaseModelFormSet, modelformset_factory, save_instance,
from django.forms.models import (BaseModelFormSet, modelformset_factory,
    modelform_defines_fields)
from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
from django.contrib.contenttypes.models import ContentType
@@ -46,10 +46,10 @@ class GenericForeignKey(six.with_metaclass(RenameGenericForeignKeyMethods)):
        self.cache_attr = "_%s_cache" % name
        cls._meta.add_virtual_field(self)

        # For some reason I don't totally understand, using weakrefs here doesn't work.
        signals.pre_init.connect(self.instance_pre_init, sender=cls, weak=False)
        # Only run pre-initialization field assignment on non-abstract models
        if not cls._meta.abstract:
            signals.pre_init.connect(self.instance_pre_init, sender=cls)

        # Connect myself as the descriptor for this field
        setattr(cls, name, self)

    def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
+3 −1
Original line number Diff line number Diff line
@@ -358,6 +358,8 @@ class ImageField(FileField):
        # Attach update_dimension_fields so that dimension fields declared
        # after their corresponding image field don't stay cleared by
        # Model.__init__, see bug #11196.
        # Only run post-initialization dimension update on non-abstract models
        if not cls._meta.abstract:
            signals.post_init.connect(self.update_dimension_fields, sender=cls)

    def update_dimension_fields(self, instance, force=False, *args, **kwargs):
+23 −7
Original line number Diff line number Diff line
@@ -32,28 +32,35 @@ class TaggedItem(models.Model):
    def __str__(self):
        return self.tag


class ValuableTaggedItem(TaggedItem):
    value = models.PositiveIntegerField()

@python_2_unicode_compatible
class Comparison(models.Model):
    """
    A model that tests having multiple GenericForeignKeys
    """

class AbstractComparison(models.Model):
    comparative = models.CharField(max_length=50)

    content_type1 = models.ForeignKey(ContentType, related_name="comparative1_set")
    object_id1 = models.PositiveIntegerField()

    first_obj = generic.GenericForeignKey(ct_field="content_type1", fk_field="object_id1")


@python_2_unicode_compatible
class Comparison(AbstractComparison):
    """
    A model that tests having multiple GenericForeignKeys. One is defined
    through an inherited abstract model and one defined directly on this class.
    """
    content_type2 = models.ForeignKey(ContentType, related_name="comparative2_set")
    object_id2 = models.PositiveIntegerField()

    first_obj = generic.GenericForeignKey(ct_field="content_type1", fk_field="object_id1")
    other_obj = generic.GenericForeignKey(ct_field="content_type2", fk_field="object_id2")

    def __str__(self):
        return "%s is %s than %s" % (self.first_obj, self.comparative, self.other_obj)


@python_2_unicode_compatible
class Animal(models.Model):
    common_name = models.CharField(max_length=150)
@@ -67,6 +74,7 @@ class Animal(models.Model):
    def __str__(self):
        return self.common_name


@python_2_unicode_compatible
class Vegetable(models.Model):
    name = models.CharField(max_length=150)
@@ -77,6 +85,7 @@ class Vegetable(models.Model):
    def __str__(self):
        return self.name


@python_2_unicode_compatible
class Mineral(models.Model):
    name = models.CharField(max_length=150)
@@ -87,18 +96,22 @@ class Mineral(models.Model):
    def __str__(self):
        return self.name


class GeckoManager(models.Manager):
    def get_queryset(self):
        return super(GeckoManager, self).get_queryset().filter(has_tail=True)


class Gecko(models.Model):
    has_tail = models.BooleanField(default=False)
    objects = GeckoManager()


# To test fix for #11263
class Rock(Mineral):
    tags = generic.GenericRelation(TaggedItem)


class ManualPK(models.Model):
    id = models.IntegerField(primary_key=True)
    tags = generic.GenericRelation(TaggedItem)
@@ -110,14 +123,17 @@ class ForProxyModelModel(models.Model):
    obj = generic.GenericForeignKey(for_concrete_model=False)
    title = models.CharField(max_length=255, null=True)


class ForConcreteModelModel(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    obj = generic.GenericForeignKey()


class ConcreteRelatedModel(models.Model):
    bases = generic.GenericRelation(ForProxyModelModel, for_concrete_model=False)


class ProxyRelatedModel(ConcreteRelatedModel):
    class Meta:
        proxy = True
+38 −11
Original line number Diff line number Diff line
@@ -18,13 +18,16 @@ class Foo(models.Model):
    a = models.CharField(max_length=10)
    d = models.DecimalField(max_digits=5, decimal_places=3)


def get_foo():
    return Foo.objects.get(id=1)


class Bar(models.Model):
    b = models.CharField(max_length=10)
    a = models.ForeignKey(Foo, default=get_foo)


class Whiz(models.Model):
    CHOICES = (
        ('Group 1', (
@@ -41,35 +44,44 @@ class Whiz(models.Model):
    )
    c = models.IntegerField(choices=CHOICES, null=True)


class BigD(models.Model):
    d = models.DecimalField(max_digits=38, decimal_places=30)


class BigS(models.Model):
    s = models.SlugField(max_length=255)


class BigInt(models.Model):
    value = models.BigIntegerField()
    null_value = models.BigIntegerField(null=True, blank=True)


class Post(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()


class NullBooleanModel(models.Model):
    nbfield = models.NullBooleanField()


class BooleanModel(models.Model):
    bfield = models.BooleanField(default=None)
    string = models.CharField(max_length=10, default='abc')


class FksToBooleans(models.Model):
    """Model wih FKs to models with {Null,}BooleanField's, #15040"""
    bf = models.ForeignKey(BooleanModel)
    nbf = models.ForeignKey(NullBooleanModel)


class RenamedField(models.Model):
    modelname = models.IntegerField(name="fieldname", choices=((1, 'One'),))


class VerboseNameField(models.Model):
    id = models.AutoField("verbose pk", primary_key=True)
    field1 = models.BigIntegerField("verbose field1")
@@ -99,11 +111,13 @@ class VerboseNameField(models.Model):
    field21 = models.TimeField("verbose field21")
    field22 = models.URLField("verbose field22")


# This model isn't used in any test, just here to ensure it validates successfully.
# See ticket #16570.
class DecimalLessThanOne(models.Model):
    d = models.DecimalField(max_digits=3, decimal_places=3)


class DataModel(models.Model):
    short_data = models.BinaryField(max_length=10, default=b'\x08')
    data = models.BinaryField()
@@ -111,6 +125,7 @@ class DataModel(models.Model):
###############################################################################
# FileField


class Document(models.Model):
    myfile = models.FileField(upload_to='unused')

@@ -127,6 +142,7 @@ if Image:
        def __init__(self, *args, **kwargs):
            self.was_opened = False
            super(TestImageFieldFile, self).__init__(*args, **kwargs)

        def open(self):
            self.was_opened = True
            super(TestImageFieldFile, self).open()
@@ -146,15 +162,26 @@ if Image:
        name = models.CharField(max_length=50)
        mugshot = TestImageField(storage=temp_storage, upload_to='tests')

    class PersonWithHeight(models.Model):
    class AbsctractPersonWithHeight(models.Model):
        """
        Model that defines an ImageField with only one dimension field.
        Abstract model that defines an ImageField with only one dimension field
        to make sure the dimension update is correctly run on concrete subclass
        instance post-initialization.
        """
        name = models.CharField(max_length=50)
        mugshot = TestImageField(storage=temp_storage, upload_to='tests',
                                 height_field='mugshot_height')
        mugshot_height = models.PositiveSmallIntegerField()

        class Meta:
            abstract = True

    class PersonWithHeight(AbsctractPersonWithHeight):
        """
        Concrete model that subclass an abctract one with only on dimension
        field.
        """
        name = models.CharField(max_length=50)

    class PersonWithHeightAndWidth(models.Model):
        """
        Model that defines height and width fields after the ImageField.
+3 −3
Original line number Diff line number Diff line
@@ -134,7 +134,7 @@ class ImageFieldTests(ImageFieldTestMixin, TestCase):
        p = self.PersonModel.objects.get(name="Joan")
        path = p.mugshot.path
        shutil.move(path, path + '.moved')
        p2 = self.PersonModel.objects.get(name="Joan")
        self.PersonModel.objects.get(name="Joan")

    def test_delete_when_missing(self):
        """