diff --git a/django/db/models/base.py b/django/db/models/base.py index 793cd936f1..37f6a3dd58 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -513,18 +513,27 @@ class Model(metaclass=ModelBase): if kwargs: property_names = opts._property_names - for prop in tuple(kwargs): - try: - # Any remaining kwargs must correspond to properties or - # virtual fields. - if prop in property_names or opts.get_field(prop): - if kwargs[prop] is not _DEFERRED: - _setattr(self, prop, kwargs[prop]) - del kwargs[prop] - except (AttributeError, FieldDoesNotExist): - pass - for kwarg in kwargs: - raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.__name__, kwarg)) + unexpected = () + for prop, value in kwargs.items(): + # Any remaining kwargs must correspond to properties or virtual + # fields. + if prop in property_names: + if value is not _DEFERRED: + _setattr(self, prop, value) + else: + try: + opts.get_field(prop) + except FieldDoesNotExist: + 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__() post_init.send(sender=cls, instance=self) diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 8b40f9c33c..a3aab7baa7 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -78,13 +78,23 @@ class ModelInstanceCreationTests(TestCase): Article(None, 'Seventh article', datetime(2021, 3, 1), pub_date=None) 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( id=None, headline='Some headline', pub_date=datetime(2005, 7, 31), 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): """ diff --git a/tests/properties/tests.py b/tests/properties/tests.py index ce29668644..dd7a6287d6 100644 --- a/tests/properties/tests.py +++ b/tests/properties/tests.py @@ -18,7 +18,7 @@ class PropertyTests(TestCase): setattr(self.a, 'full_name', 'Paul McCartney') # 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') # But "full_name_2" has, and it can be used to initialize the class.