Fixed #29970, #30041 -- Made ModelBase.__new__() pass attrs without contribute_to_class() to type.__new__().
This commit is contained in:
parent
ce8b65ac5e
commit
a68ea23101
|
@ -58,6 +58,11 @@ def subclass_exception(name, bases, module, attached_to):
|
|||
})
|
||||
|
||||
|
||||
def _has_contribute_to_class(value):
|
||||
# Only call contribute_to_class() if it's bound.
|
||||
return not inspect.isclass(value) and hasattr(value, 'contribute_to_class')
|
||||
|
||||
|
||||
class ModelBase(type):
|
||||
"""Metaclass for all models."""
|
||||
def __new__(cls, name, bases, attrs, **kwargs):
|
||||
|
@ -75,8 +80,15 @@ class ModelBase(type):
|
|||
classcell = attrs.pop('__classcell__', None)
|
||||
if classcell is not None:
|
||||
new_attrs['__classcell__'] = classcell
|
||||
new_class = super_new(cls, name, bases, new_attrs, **kwargs)
|
||||
attr_meta = attrs.pop('Meta', None)
|
||||
# Pass all attrs without a (Django-specific) contribute_to_class()
|
||||
# method to type.__new__() so that they're properly initialized
|
||||
# (i.e. __set_name__()).
|
||||
for obj_name, obj in list(attrs.items()):
|
||||
if not _has_contribute_to_class(obj):
|
||||
new_attrs[obj_name] = attrs.pop(obj_name)
|
||||
new_class = super_new(cls, name, bases, new_attrs, **kwargs)
|
||||
|
||||
abstract = getattr(attr_meta, 'abstract', False)
|
||||
meta = attr_meta or getattr(new_class, 'Meta', None)
|
||||
base_meta = getattr(new_class, '_meta', None)
|
||||
|
@ -300,8 +312,7 @@ class ModelBase(type):
|
|||
return new_class
|
||||
|
||||
def add_to_class(cls, name, value):
|
||||
# We should call the contribute_to_class method only if it's bound
|
||||
if not inspect.isclass(value) and hasattr(value, 'contribute_to_class'):
|
||||
if _has_contribute_to_class(value):
|
||||
value.contribute_to_class(cls, name)
|
||||
else:
|
||||
setattr(cls, name, value)
|
||||
|
|
|
@ -181,18 +181,33 @@ class ModelInheritanceTests(TestCase):
|
|||
def test_init_subclass(self):
|
||||
saved_kwargs = {}
|
||||
|
||||
class A:
|
||||
class A(models.Model):
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__()
|
||||
saved_kwargs.update(kwargs)
|
||||
|
||||
kwargs = {'x': 1, 'y': 2, 'z': 3}
|
||||
|
||||
class B(A, models.Model, **kwargs):
|
||||
class B(A, **kwargs):
|
||||
pass
|
||||
|
||||
self.assertEqual(saved_kwargs, kwargs)
|
||||
|
||||
@unittest.skipUnless(PY36, '__set_name__ is new in Python 3.6')
|
||||
@isolate_apps('model_inheritance')
|
||||
def test_set_name(self):
|
||||
class ClassAttr:
|
||||
called = None
|
||||
|
||||
def __set_name__(self_, owner, name):
|
||||
self.assertIsNone(self_.called)
|
||||
self_.called = (owner, name)
|
||||
|
||||
class A(models.Model):
|
||||
attr = ClassAttr()
|
||||
|
||||
self.assertEqual(A.attr.called, (A, 'attr'))
|
||||
|
||||
|
||||
class ModelInheritanceDataTests(TestCase):
|
||||
@classmethod
|
||||
|
|
Loading…
Reference in New Issue