Fixed #27118 -- Made QuerySet.get_or_create()/update_or_create() error for a non-field in their arguments.
This commit is contained in:
parent
b3330f52a8
commit
a5e13a0b92
|
@ -527,6 +527,18 @@ class QuerySet(object):
|
||||||
lookup[f.name] = lookup.pop(f.attname)
|
lookup[f.name] = lookup.pop(f.attname)
|
||||||
params = {k: v for k, v in kwargs.items() if LOOKUP_SEP not in k}
|
params = {k: v for k, v in kwargs.items() if LOOKUP_SEP not in k}
|
||||||
params.update(defaults)
|
params.update(defaults)
|
||||||
|
invalid_params = []
|
||||||
|
for param in params:
|
||||||
|
try:
|
||||||
|
self.model._meta.get_field(param)
|
||||||
|
except exceptions.FieldDoesNotExist:
|
||||||
|
invalid_params.append(param)
|
||||||
|
if invalid_params:
|
||||||
|
raise exceptions.FieldError(
|
||||||
|
"Invalid field name(s) for model %s: '%s'." % (
|
||||||
|
self.model._meta.object_name,
|
||||||
|
"', '".join(sorted(invalid_params)),
|
||||||
|
))
|
||||||
return lookup, params
|
return lookup, params
|
||||||
|
|
||||||
def _earliest_or_latest(self, field_name=None, direction="-"):
|
def _earliest_or_latest(self, field_name=None, direction="-"):
|
||||||
|
|
|
@ -446,6 +446,15 @@ Protection against insecure redirects in :mod:`django.contrib.auth` and ``i18n``
|
||||||
and :func:`~django.views.i18n.set_language` protect users from being redirected
|
and :func:`~django.views.i18n.set_language` protect users from being redirected
|
||||||
to non-HTTPS ``next`` URLs when the app is running over HTTPS.
|
to non-HTTPS ``next`` URLs when the app is running over HTTPS.
|
||||||
|
|
||||||
|
``QuerySet.get_or_create()`` and ``update_or_create()`` validate arguments
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
To prevent typos from passing silently,
|
||||||
|
:meth:`~django.db.models.query.QuerySet.get_or_create` and
|
||||||
|
:meth:`~django.db.models.query.QuerySet.update_or_create` check that their
|
||||||
|
arguments are model fields. This should be backwards-incompatible only in the
|
||||||
|
fact that it might expose a bug in your project.
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ import traceback
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
from django.core.exceptions import FieldError
|
||||||
from django.db import DatabaseError, IntegrityError, connection
|
from django.db import DatabaseError, IntegrityError, connection
|
||||||
from django.test import (
|
from django.test import (
|
||||||
TestCase, TransactionTestCase, ignore_warnings, skipUnlessDBFeature,
|
SimpleTestCase, TestCase, TransactionTestCase, ignore_warnings, skipUnlessDBFeature,
|
||||||
)
|
)
|
||||||
from django.utils.encoding import DjangoUnicodeDecodeError
|
from django.utils.encoding import DjangoUnicodeDecodeError
|
||||||
|
|
||||||
|
@ -484,3 +485,27 @@ class UpdateOrCreateTransactionTests(TransactionTestCase):
|
||||||
updated_person = Person.objects.get(first_name='John')
|
updated_person = Person.objects.get(first_name='John')
|
||||||
self.assertGreater(after_update - before_start, timedelta(seconds=0.5))
|
self.assertGreater(after_update - before_start, timedelta(seconds=0.5))
|
||||||
self.assertEqual(updated_person.last_name, 'NotLennon')
|
self.assertEqual(updated_person.last_name, 'NotLennon')
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidCreateArgumentsTests(SimpleTestCase):
|
||||||
|
msg = "Invalid field name(s) for model Thing: 'nonexistent'."
|
||||||
|
|
||||||
|
def test_get_or_create_with_invalid_defaults(self):
|
||||||
|
with self.assertRaisesMessage(FieldError, self.msg):
|
||||||
|
Thing.objects.get_or_create(name='a', defaults={'nonexistent': 'b'})
|
||||||
|
|
||||||
|
def test_get_or_create_with_invalid_kwargs(self):
|
||||||
|
with self.assertRaisesMessage(FieldError, self.msg):
|
||||||
|
Thing.objects.get_or_create(name='a', nonexistent='b')
|
||||||
|
|
||||||
|
def test_update_or_create_with_invalid_defaults(self):
|
||||||
|
with self.assertRaisesMessage(FieldError, self.msg):
|
||||||
|
Thing.objects.update_or_create(name='a', defaults={'nonexistent': 'b'})
|
||||||
|
|
||||||
|
def test_update_or_create_with_invalid_kwargs(self):
|
||||||
|
with self.assertRaisesMessage(FieldError, self.msg):
|
||||||
|
Thing.objects.update_or_create(name='a', nonexistent='b')
|
||||||
|
|
||||||
|
def test_multiple_invalid_fields(self):
|
||||||
|
with self.assertRaisesMessage(FieldError, "Invalid field name(s) for model Thing: 'invalid', 'nonexistent'"):
|
||||||
|
Thing.objects.update_or_create(name='a', nonexistent='b', defaults={'invalid': 'c'})
|
||||||
|
|
Loading…
Reference in New Issue