mirror of
https://github.com/django/django.git
synced 2025-06-05 11:39:13 +00:00
Fixed #21217 -- Avoid connecting (pre|post)_init
signals to abstract senders.
This commit is contained in:
parent
dc3d2ac98c
commit
948d209ada
@ -14,7 +14,7 @@ from django.db.models.fields.related import ForeignObject, ForeignObjectRel
|
|||||||
from django.db.models.related import PathInfo
|
from django.db.models.related import PathInfo
|
||||||
from django.db.models.sql.where import Constraint
|
from django.db.models.sql.where import Constraint
|
||||||
from django.forms import ModelForm, ALL_FIELDS
|
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)
|
modelform_defines_fields)
|
||||||
from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
|
from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
@ -46,10 +46,10 @@ class GenericForeignKey(six.with_metaclass(RenameGenericForeignKeyMethods)):
|
|||||||
self.cache_attr = "_%s_cache" % name
|
self.cache_attr = "_%s_cache" % name
|
||||||
cls._meta.add_virtual_field(self)
|
cls._meta.add_virtual_field(self)
|
||||||
|
|
||||||
# For some reason I don't totally understand, using weakrefs here doesn't work.
|
# Only run pre-initialization field assignment on non-abstract models
|
||||||
signals.pre_init.connect(self.instance_pre_init, sender=cls, weak=False)
|
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)
|
setattr(cls, name, self)
|
||||||
|
|
||||||
def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
|
def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
|
||||||
|
@ -358,7 +358,9 @@ class ImageField(FileField):
|
|||||||
# Attach update_dimension_fields so that dimension fields declared
|
# Attach update_dimension_fields so that dimension fields declared
|
||||||
# after their corresponding image field don't stay cleared by
|
# after their corresponding image field don't stay cleared by
|
||||||
# Model.__init__, see bug #11196.
|
# Model.__init__, see bug #11196.
|
||||||
signals.post_init.connect(self.update_dimension_fields, sender=cls)
|
# 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):
|
def update_dimension_fields(self, instance, force=False, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -32,28 +32,35 @@ class TaggedItem(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.tag
|
return self.tag
|
||||||
|
|
||||||
|
|
||||||
class ValuableTaggedItem(TaggedItem):
|
class ValuableTaggedItem(TaggedItem):
|
||||||
value = models.PositiveIntegerField()
|
value = models.PositiveIntegerField()
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Comparison(models.Model):
|
class AbstractComparison(models.Model):
|
||||||
"""
|
|
||||||
A model that tests having multiple GenericForeignKeys
|
|
||||||
"""
|
|
||||||
comparative = models.CharField(max_length=50)
|
comparative = models.CharField(max_length=50)
|
||||||
|
|
||||||
content_type1 = models.ForeignKey(ContentType, related_name="comparative1_set")
|
content_type1 = models.ForeignKey(ContentType, related_name="comparative1_set")
|
||||||
object_id1 = models.PositiveIntegerField()
|
object_id1 = models.PositiveIntegerField()
|
||||||
|
|
||||||
content_type2 = models.ForeignKey(ContentType, related_name="comparative2_set")
|
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()
|
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")
|
other_obj = generic.GenericForeignKey(ct_field="content_type2", fk_field="object_id2")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s is %s than %s" % (self.first_obj, self.comparative, self.other_obj)
|
return "%s is %s than %s" % (self.first_obj, self.comparative, self.other_obj)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Animal(models.Model):
|
class Animal(models.Model):
|
||||||
common_name = models.CharField(max_length=150)
|
common_name = models.CharField(max_length=150)
|
||||||
@ -67,6 +74,7 @@ class Animal(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.common_name
|
return self.common_name
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Vegetable(models.Model):
|
class Vegetable(models.Model):
|
||||||
name = models.CharField(max_length=150)
|
name = models.CharField(max_length=150)
|
||||||
@ -77,6 +85,7 @@ class Vegetable(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Mineral(models.Model):
|
class Mineral(models.Model):
|
||||||
name = models.CharField(max_length=150)
|
name = models.CharField(max_length=150)
|
||||||
@ -87,18 +96,22 @@ class Mineral(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class GeckoManager(models.Manager):
|
class GeckoManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super(GeckoManager, self).get_queryset().filter(has_tail=True)
|
return super(GeckoManager, self).get_queryset().filter(has_tail=True)
|
||||||
|
|
||||||
|
|
||||||
class Gecko(models.Model):
|
class Gecko(models.Model):
|
||||||
has_tail = models.BooleanField(default=False)
|
has_tail = models.BooleanField(default=False)
|
||||||
objects = GeckoManager()
|
objects = GeckoManager()
|
||||||
|
|
||||||
|
|
||||||
# To test fix for #11263
|
# To test fix for #11263
|
||||||
class Rock(Mineral):
|
class Rock(Mineral):
|
||||||
tags = generic.GenericRelation(TaggedItem)
|
tags = generic.GenericRelation(TaggedItem)
|
||||||
|
|
||||||
|
|
||||||
class ManualPK(models.Model):
|
class ManualPK(models.Model):
|
||||||
id = models.IntegerField(primary_key=True)
|
id = models.IntegerField(primary_key=True)
|
||||||
tags = generic.GenericRelation(TaggedItem)
|
tags = generic.GenericRelation(TaggedItem)
|
||||||
@ -110,14 +123,17 @@ class ForProxyModelModel(models.Model):
|
|||||||
obj = generic.GenericForeignKey(for_concrete_model=False)
|
obj = generic.GenericForeignKey(for_concrete_model=False)
|
||||||
title = models.CharField(max_length=255, null=True)
|
title = models.CharField(max_length=255, null=True)
|
||||||
|
|
||||||
|
|
||||||
class ForConcreteModelModel(models.Model):
|
class ForConcreteModelModel(models.Model):
|
||||||
content_type = models.ForeignKey(ContentType)
|
content_type = models.ForeignKey(ContentType)
|
||||||
object_id = models.PositiveIntegerField()
|
object_id = models.PositiveIntegerField()
|
||||||
obj = generic.GenericForeignKey()
|
obj = generic.GenericForeignKey()
|
||||||
|
|
||||||
|
|
||||||
class ConcreteRelatedModel(models.Model):
|
class ConcreteRelatedModel(models.Model):
|
||||||
bases = generic.GenericRelation(ForProxyModelModel, for_concrete_model=False)
|
bases = generic.GenericRelation(ForProxyModelModel, for_concrete_model=False)
|
||||||
|
|
||||||
|
|
||||||
class ProxyRelatedModel(ConcreteRelatedModel):
|
class ProxyRelatedModel(ConcreteRelatedModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
@ -18,57 +18,69 @@ class Foo(models.Model):
|
|||||||
a = models.CharField(max_length=10)
|
a = models.CharField(max_length=10)
|
||||||
d = models.DecimalField(max_digits=5, decimal_places=3)
|
d = models.DecimalField(max_digits=5, decimal_places=3)
|
||||||
|
|
||||||
|
|
||||||
def get_foo():
|
def get_foo():
|
||||||
return Foo.objects.get(id=1)
|
return Foo.objects.get(id=1)
|
||||||
|
|
||||||
|
|
||||||
class Bar(models.Model):
|
class Bar(models.Model):
|
||||||
b = models.CharField(max_length=10)
|
b = models.CharField(max_length=10)
|
||||||
a = models.ForeignKey(Foo, default=get_foo)
|
a = models.ForeignKey(Foo, default=get_foo)
|
||||||
|
|
||||||
|
|
||||||
class Whiz(models.Model):
|
class Whiz(models.Model):
|
||||||
CHOICES = (
|
CHOICES = (
|
||||||
('Group 1', (
|
('Group 1', (
|
||||||
(1,'First'),
|
(1, 'First'),
|
||||||
(2,'Second'),
|
(2, 'Second'),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
('Group 2', (
|
('Group 2', (
|
||||||
(3,'Third'),
|
(3, 'Third'),
|
||||||
(4,'Fourth'),
|
(4, 'Fourth'),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
(0,'Other'),
|
(0, 'Other'),
|
||||||
)
|
)
|
||||||
c = models.IntegerField(choices=CHOICES, null=True)
|
c = models.IntegerField(choices=CHOICES, null=True)
|
||||||
|
|
||||||
|
|
||||||
class BigD(models.Model):
|
class BigD(models.Model):
|
||||||
d = models.DecimalField(max_digits=38, decimal_places=30)
|
d = models.DecimalField(max_digits=38, decimal_places=30)
|
||||||
|
|
||||||
|
|
||||||
class BigS(models.Model):
|
class BigS(models.Model):
|
||||||
s = models.SlugField(max_length=255)
|
s = models.SlugField(max_length=255)
|
||||||
|
|
||||||
|
|
||||||
class BigInt(models.Model):
|
class BigInt(models.Model):
|
||||||
value = models.BigIntegerField()
|
value = models.BigIntegerField()
|
||||||
null_value = models.BigIntegerField(null = True, blank = True)
|
null_value = models.BigIntegerField(null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
class Post(models.Model):
|
class Post(models.Model):
|
||||||
title = models.CharField(max_length=100)
|
title = models.CharField(max_length=100)
|
||||||
body = models.TextField()
|
body = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
class NullBooleanModel(models.Model):
|
class NullBooleanModel(models.Model):
|
||||||
nbfield = models.NullBooleanField()
|
nbfield = models.NullBooleanField()
|
||||||
|
|
||||||
|
|
||||||
class BooleanModel(models.Model):
|
class BooleanModel(models.Model):
|
||||||
bfield = models.BooleanField(default=None)
|
bfield = models.BooleanField(default=None)
|
||||||
string = models.CharField(max_length=10, default='abc')
|
string = models.CharField(max_length=10, default='abc')
|
||||||
|
|
||||||
|
|
||||||
class FksToBooleans(models.Model):
|
class FksToBooleans(models.Model):
|
||||||
"""Model wih FKs to models with {Null,}BooleanField's, #15040"""
|
"""Model wih FKs to models with {Null,}BooleanField's, #15040"""
|
||||||
bf = models.ForeignKey(BooleanModel)
|
bf = models.ForeignKey(BooleanModel)
|
||||||
nbf = models.ForeignKey(NullBooleanModel)
|
nbf = models.ForeignKey(NullBooleanModel)
|
||||||
|
|
||||||
|
|
||||||
class RenamedField(models.Model):
|
class RenamedField(models.Model):
|
||||||
modelname = models.IntegerField(name="fieldname", choices=((1,'One'),))
|
modelname = models.IntegerField(name="fieldname", choices=((1, 'One'),))
|
||||||
|
|
||||||
|
|
||||||
class VerboseNameField(models.Model):
|
class VerboseNameField(models.Model):
|
||||||
id = models.AutoField("verbose pk", primary_key=True)
|
id = models.AutoField("verbose pk", primary_key=True)
|
||||||
@ -99,11 +111,13 @@ class VerboseNameField(models.Model):
|
|||||||
field21 = models.TimeField("verbose field21")
|
field21 = models.TimeField("verbose field21")
|
||||||
field22 = models.URLField("verbose field22")
|
field22 = models.URLField("verbose field22")
|
||||||
|
|
||||||
|
|
||||||
# This model isn't used in any test, just here to ensure it validates successfully.
|
# This model isn't used in any test, just here to ensure it validates successfully.
|
||||||
# See ticket #16570.
|
# See ticket #16570.
|
||||||
class DecimalLessThanOne(models.Model):
|
class DecimalLessThanOne(models.Model):
|
||||||
d = models.DecimalField(max_digits=3, decimal_places=3)
|
d = models.DecimalField(max_digits=3, decimal_places=3)
|
||||||
|
|
||||||
|
|
||||||
class DataModel(models.Model):
|
class DataModel(models.Model):
|
||||||
short_data = models.BinaryField(max_length=10, default=b'\x08')
|
short_data = models.BinaryField(max_length=10, default=b'\x08')
|
||||||
data = models.BinaryField()
|
data = models.BinaryField()
|
||||||
@ -111,6 +125,7 @@ class DataModel(models.Model):
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
# FileField
|
# FileField
|
||||||
|
|
||||||
|
|
||||||
class Document(models.Model):
|
class Document(models.Model):
|
||||||
myfile = models.FileField(upload_to='unused')
|
myfile = models.FileField(upload_to='unused')
|
||||||
|
|
||||||
@ -126,7 +141,8 @@ if Image:
|
|||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.was_opened = False
|
self.was_opened = False
|
||||||
super(TestImageFieldFile, self).__init__(*args,**kwargs)
|
super(TestImageFieldFile, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
self.was_opened = True
|
self.was_opened = True
|
||||||
super(TestImageFieldFile, self).open()
|
super(TestImageFieldFile, self).open()
|
||||||
@ -146,15 +162,26 @@ if Image:
|
|||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
mugshot = TestImageField(storage=temp_storage, upload_to='tests')
|
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',
|
mugshot = TestImageField(storage=temp_storage, upload_to='tests',
|
||||||
height_field='mugshot_height')
|
height_field='mugshot_height')
|
||||||
mugshot_height = models.PositiveSmallIntegerField()
|
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):
|
class PersonWithHeightAndWidth(models.Model):
|
||||||
"""
|
"""
|
||||||
Model that defines height and width fields after the ImageField.
|
Model that defines height and width fields after the ImageField.
|
||||||
|
@ -134,7 +134,7 @@ class ImageFieldTests(ImageFieldTestMixin, TestCase):
|
|||||||
p = self.PersonModel.objects.get(name="Joan")
|
p = self.PersonModel.objects.get(name="Joan")
|
||||||
path = p.mugshot.path
|
path = p.mugshot.path
|
||||||
shutil.move(path, path + '.moved')
|
shutil.move(path, path + '.moved')
|
||||||
p2 = self.PersonModel.objects.get(name="Joan")
|
self.PersonModel.objects.get(name="Joan")
|
||||||
|
|
||||||
def test_delete_when_missing(self):
|
def test_delete_when_missing(self):
|
||||||
"""
|
"""
|
||||||
@ -412,7 +412,7 @@ class TwoImageFieldTests(ImageFieldTestMixin, TestCase):
|
|||||||
# was opened.
|
# was opened.
|
||||||
self.assertEqual(p.mugshot.was_opened, False)
|
self.assertEqual(p.mugshot.was_opened, False)
|
||||||
self.assertEqual(p.headshot.was_opened, False)
|
self.assertEqual(p.headshot.was_opened, False)
|
||||||
self.check_dimensions(p, 4, 8,'mugshot')
|
self.check_dimensions(p, 4, 8, 'mugshot')
|
||||||
self.check_dimensions(p, 8, 4, 'headshot')
|
self.check_dimensions(p, 8, 4, 'headshot')
|
||||||
# After checking dimensions on the image fields, the files will
|
# After checking dimensions on the image fields, the files will
|
||||||
# have been opened.
|
# have been opened.
|
||||||
@ -422,7 +422,7 @@ class TwoImageFieldTests(ImageFieldTestMixin, TestCase):
|
|||||||
# check dimensions again, the file should not have opened.
|
# check dimensions again, the file should not have opened.
|
||||||
p.mugshot.was_opened = False
|
p.mugshot.was_opened = False
|
||||||
p.headshot.was_opened = False
|
p.headshot.was_opened = False
|
||||||
self.check_dimensions(p, 4, 8,'mugshot')
|
self.check_dimensions(p, 4, 8, 'mugshot')
|
||||||
self.check_dimensions(p, 8, 4, 'headshot')
|
self.check_dimensions(p, 8, 4, 'headshot')
|
||||||
self.assertEqual(p.mugshot.was_opened, False)
|
self.assertEqual(p.mugshot.was_opened, False)
|
||||||
self.assertEqual(p.headshot.was_opened, False)
|
self.assertEqual(p.headshot.was_opened, False)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user