Fixed #30254 -- Allowed model metaclasses to access the attribute dict in __init__().

Regression in a68ea23101.
This commit is contained in:
Matt Westcott 2019-03-14 18:23:50 +01:00 committed by Tim Graham
parent f976ab1b11
commit 58ad030d05
2 changed files with 27 additions and 5 deletions

View File

@ -84,9 +84,12 @@ class ModelBase(type):
# Pass all attrs without a (Django-specific) contribute_to_class() # Pass all attrs without a (Django-specific) contribute_to_class()
# method to type.__new__() so that they're properly initialized # method to type.__new__() so that they're properly initialized
# (i.e. __set_name__()). # (i.e. __set_name__()).
contributable_attrs = {}
for obj_name, obj in list(attrs.items()): for obj_name, obj in list(attrs.items()):
if not _has_contribute_to_class(obj): if _has_contribute_to_class(obj):
new_attrs[obj_name] = attrs.pop(obj_name) contributable_attrs[obj_name] = obj
else:
new_attrs[obj_name] = obj
new_class = super_new(cls, name, bases, new_attrs, **kwargs) new_class = super_new(cls, name, bases, new_attrs, **kwargs)
abstract = getattr(attr_meta, 'abstract', False) abstract = getattr(attr_meta, 'abstract', False)
@ -146,8 +149,9 @@ class ModelBase(type):
if is_proxy and base_meta and base_meta.swapped: if is_proxy and base_meta and base_meta.swapped:
raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped)) raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped))
# Add all attributes to the class. # Add remaining attributes (those with a contribute_to_class() method)
for obj_name, obj in attrs.items(): # to the class.
for obj_name, obj in contributable_attrs.items():
new_class.add_to_class(obj_name, obj) new_class.add_to_class(obj_name, obj)
# All the fields of any type declared on this model # All the fields of any type declared on this model

View File

@ -2,9 +2,10 @@ import datetime
from operator import attrgetter from operator import attrgetter
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import router from django.db import models, router
from django.db.models.sql import InsertQuery from django.db.models.sql import InsertQuery
from django.test import TestCase, skipUnlessDBFeature from django.test import TestCase, skipUnlessDBFeature
from django.test.utils import isolate_apps
from django.utils.timezone import get_fixed_timezone from django.utils.timezone import get_fixed_timezone
from .models import ( from .models import (
@ -217,6 +218,23 @@ class ModelTests(TestCase):
m3 = Model3.objects.get(model2=1000) m3 = Model3.objects.get(model2=1000)
m3.model2 m3.model2
@isolate_apps('model_regress')
def test_metaclass_can_access_attribute_dict(self):
"""
Model metaclasses have access to the class attribute dict in
__init__() (#30254).
"""
class HorseBase(models.base.ModelBase):
def __init__(cls, name, bases, attrs):
super(HorseBase, cls).__init__(name, bases, attrs)
cls.horns = (1 if 'magic' in attrs else 0)
class Horse(models.Model, metaclass=HorseBase):
name = models.CharField(max_length=255)
magic = True
self.assertEqual(Horse.horns, 1)
class ModelValidationTest(TestCase): class ModelValidationTest(TestCase):
def test_pk_validation(self): def test_pk_validation(self):