summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Johnson <me@adamj.eu>2021-12-13 15:44:07 +0000
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-01-13 11:09:37 +0100
commit08d8bccbf1b0764a0de68325569ee47da256e206 (patch)
treef5efe9496dedba0f7744ba87c5ef807b1b7d1fe8
parent0a4a5e5bacc354df3132d0fcf706839c21afb89d (diff)
Improved Model.__init__() properties loop.
This improves readability, accumulates unrecognized arguments raise an exception with all of them, and avoids refetching the values.
-rw-r--r--django/db/models/base.py33
-rw-r--r--tests/basic/tests.py12
-rw-r--r--tests/properties/tests.py2
3 files changed, 33 insertions, 14 deletions
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,12 +78,22 @@ 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.