Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #28198 -- Prevented model attributes from overriding deferred fields. #9126

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions django/db/models/base.py
Expand Up @@ -75,6 +75,23 @@ def __new__(cls, name, bases, attrs, **kwargs):
classcell = attrs.pop('__classcell__', None)
if classcell is not None:
new_attrs['__classcell__'] = classcell

# Fixed #28198 -- Model attributes does not overridden by
# attributes from parent classes
# For each parent class collect and remove the attributes which
# present in `attrs` and has implementation in this parent class
duplicate_attributes = {}
for parent in parents:
attributes_per_parent_class = {}
for attr_name in attrs:
if(hasattr(parent, attr_name) and not
attr_name.startswith(LOOKUP_SEP) and not
hasattr(getattr(parent, attr_name), '__dict__')
):
attributes_per_parent_class[attr_name] = getattr(parent, attr_name)
delattr(parent, attr_name)
if len(attributes_per_parent_class) > 0:
duplicate_attributes[parent] = attributes_per_parent_class
new_class = super_new(cls, name, bases, new_attrs, **kwargs)
attr_meta = attrs.pop('Meta', None)
abstract = getattr(attr_meta, 'abstract', False)
Expand Down Expand Up @@ -287,6 +304,14 @@ def __new__(cls, name, bases, attrs, **kwargs):
# abstract model.
new_class._meta.indexes = [copy.deepcopy(idx) for idx in new_class._meta.indexes]

# Fixed #28198 -- Model attributes does not overridden by
# attributes from parent classes
# Restore the attributes for each model parent class
if len(duplicate_attributes) > 0:
for parent in parents:
if parent in duplicate_attributes:
for attribute in duplicate_attributes[parent]:
setattr(parent, attribute, duplicate_attributes[parent][attribute])
if abstract:
# Abstract base models can't be instantiated and don't appear in
# the list of models for an app. We do the final setup for them a
Expand Down
1 change: 1 addition & 0 deletions tests/defer/models.py
Expand Up @@ -14,6 +14,7 @@ class Primary(models.Model):
name = models.CharField(max_length=50)
value = models.CharField(max_length=50)
related = models.ForeignKey(Secondary, models.CASCADE)
other = True

def __str__(self):
return self.name
Expand Down
20 changes: 20 additions & 0 deletions tests/defer/tests.py
Expand Up @@ -271,3 +271,23 @@ def test_custom_refresh_on_deferred_loading(self):
# access of any of them.
self.assertEqual(rf2.name, 'new foo')
self.assertEqual(rf2.value, 'new bar')

def test_field_not_overridden_by_parent(self):
"""
Fixed #28198 -- Model attributes does not overridden by
attributes from parent classes

The defer field `other` must return value from db, instead
attribute value from parent model.
The attribute `other` for Child must be the same
as in parent model
"""
s = Secondary.objects.create()
Child.objects.create(name='foo', value='bar', related=s)
BigChild.objects.create(other="False", related=s)

primary = Child.objects.get()
big_child = BigChild.objects.defer('other').get()
self.assertEqual(len(big_child.get_deferred_fields()), 1)
self.assertIs(primary.other, True)
self.assertEqual(big_child.other, "False")