Improved Model.__init__() properties loop.

This improves readability, accumulates unrecognized arguments raise an
exception with all of them, and avoids refetching the values.
This commit is contained in:
Adam Johnson 2021-12-13 15:44:07 +00:00 committed by Mariusz Felisiak
parent 0a4a5e5bac
commit 08d8bccbf1
3 changed files with 33 additions and 14 deletions

View File

@ -513,18 +513,27 @@ class Model(metaclass=ModelBase):
if kwargs: if kwargs:
property_names = opts._property_names property_names = opts._property_names
for prop in tuple(kwargs): unexpected = ()
try: for prop, value in kwargs.items():
# Any remaining kwargs must correspond to properties or # Any remaining kwargs must correspond to properties or virtual
# virtual fields. # fields.
if prop in property_names or opts.get_field(prop): if prop in property_names:
if kwargs[prop] is not _DEFERRED: if value is not _DEFERRED:
_setattr(self, prop, kwargs[prop]) _setattr(self, prop, value)
del kwargs[prop] else:
except (AttributeError, FieldDoesNotExist): try:
pass opts.get_field(prop)
for kwarg in kwargs: except FieldDoesNotExist:
raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.__name__, kwarg)) unexpected += (prop,)
else:
if value is not _DEFERRED:
_setattr(self, prop, value)
if unexpected:
unexpected_names = ', '.join(repr(n) for n in unexpected)
raise TypeError(
f'{cls.__name__}() got unexpected keyword arguments: '
f'{unexpected_names}'
)
super().__init__() super().__init__()
post_init.send(sender=cls, instance=self) post_init.send(sender=cls, instance=self)

View File

@ -78,13 +78,23 @@ class ModelInstanceCreationTests(TestCase):
Article(None, 'Seventh article', datetime(2021, 3, 1), pub_date=None) Article(None, 'Seventh article', datetime(2021, 3, 1), pub_date=None)
def test_cannot_create_instance_with_invalid_kwargs(self): def test_cannot_create_instance_with_invalid_kwargs(self):
with self.assertRaisesMessage(TypeError, "Article() got an unexpected keyword argument 'foo'"): msg = "Article() got unexpected keyword arguments: 'foo'"
with self.assertRaisesMessage(TypeError, msg):
Article( Article(
id=None, id=None,
headline='Some headline', headline='Some headline',
pub_date=datetime(2005, 7, 31), pub_date=datetime(2005, 7, 31),
foo='bar', foo='bar',
) )
msg = "Article() got unexpected keyword arguments: 'foo', 'bar'"
with self.assertRaisesMessage(TypeError, msg):
Article(
id=None,
headline='Some headline',
pub_date=datetime(2005, 7, 31),
foo='bar',
bar='baz',
)
def test_can_leave_off_value_for_autofield_and_it_gets_value_on_save(self): def test_can_leave_off_value_for_autofield_and_it_gets_value_on_save(self):
""" """

View File

@ -18,7 +18,7 @@ class PropertyTests(TestCase):
setattr(self.a, 'full_name', 'Paul McCartney') setattr(self.a, 'full_name', 'Paul McCartney')
# And cannot be used to initialize the class. # And cannot be used to initialize the class.
with self.assertRaisesMessage(TypeError, "Person() got an unexpected keyword argument 'full_name'"): with self.assertRaises(AttributeError):
Person(full_name='Paul McCartney') Person(full_name='Paul McCartney')
# But "full_name_2" has, and it can be used to initialize the class. # But "full_name_2" has, and it can be used to initialize the class.