summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authortschilling <schillingt@better-simple.com>2023-01-30 20:39:15 -0600
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-02-14 11:50:35 +0100
commitc5808470aaffda661cb911b06d5b848dd7b75467 (patch)
treefd4e8b25bf9a7fee05cd61f27c6536a57f134a16 /tests
parentecafcaf634fcef93f9da8cb12795273dd1c3a576 (diff)
Fixed #34280 -- Allowed specifying different field values for create operation in QuerySet.update_or_create().
Diffstat (limited to 'tests')
-rw-r--r--tests/async/test_async_queryset.py7
-rw-r--r--tests/async/test_async_related_managers.py12
-rw-r--r--tests/generic_relations/tests.py60
-rw-r--r--tests/get_or_create/models.py1
-rw-r--r--tests/get_or_create/tests.py105
5 files changed, 171 insertions, 14 deletions
diff --git a/tests/async/test_async_queryset.py b/tests/async/test_async_queryset.py
index 253183ea10..e83157f5bd 100644
--- a/tests/async/test_async_queryset.py
+++ b/tests/async/test_async_queryset.py
@@ -99,10 +99,17 @@ class AsyncQuerySetTest(TestCase):
id=self.s1.id, defaults={"field": 2}
)
self.assertEqual(instance, self.s1)
+ self.assertEqual(instance.field, 2)
self.assertIs(created, False)
instance, created = await SimpleModel.objects.aupdate_or_create(field=4)
self.assertEqual(await SimpleModel.objects.acount(), 4)
self.assertIs(created, True)
+ instance, created = await SimpleModel.objects.aupdate_or_create(
+ field=5, defaults={"field": 7}, create_defaults={"field": 6}
+ )
+ self.assertEqual(await SimpleModel.objects.acount(), 5)
+ self.assertIs(created, True)
+ self.assertEqual(instance.field, 6)
@skipUnlessDBFeature("has_bulk_insert")
@async_to_sync
diff --git a/tests/async/test_async_related_managers.py b/tests/async/test_async_related_managers.py
index c475b54899..56f86339ef 100644
--- a/tests/async/test_async_related_managers.py
+++ b/tests/async/test_async_related_managers.py
@@ -44,12 +44,18 @@ class AsyncRelatedManagersOperationTest(TestCase):
self.assertIs(created, True)
self.assertEqual(await self.mtm1.simples.acount(), 1)
self.assertEqual(new_simple.field, 2)
- new_simple, created = await self.mtm1.simples.aupdate_or_create(
+ new_simple1, created = await self.mtm1.simples.aupdate_or_create(
id=new_simple.id, defaults={"field": 3}
)
self.assertIs(created, False)
- self.assertEqual(await self.mtm1.simples.acount(), 1)
- self.assertEqual(new_simple.field, 3)
+ self.assertEqual(new_simple1.field, 3)
+
+ new_simple2, created = await self.mtm1.simples.aupdate_or_create(
+ field=4, defaults={"field": 6}, create_defaults={"field": 5}
+ )
+ self.assertIs(created, True)
+ self.assertEqual(new_simple2.field, 5)
+ self.assertEqual(await self.mtm1.simples.acount(), 2)
async def test_aupdate_or_create_reverse(self):
new_relatedmodel, created = await self.s1.relatedmodel_set.aupdate_or_create()
diff --git a/tests/generic_relations/tests.py b/tests/generic_relations/tests.py
index 18e3578f60..fab23dfde5 100644
--- a/tests/generic_relations/tests.py
+++ b/tests/generic_relations/tests.py
@@ -59,6 +59,19 @@ class GenericRelationsTests(TestCase):
self.assertTrue(created)
self.assertEqual(count + 1, self.bacon.tags.count())
+ def test_generic_update_or_create_when_created_with_create_defaults(self):
+ count = self.bacon.tags.count()
+ tag, created = self.bacon.tags.update_or_create(
+ # Since, the "stinky" tag doesn't exist create
+ # a "juicy" tag.
+ create_defaults={"tag": "juicy"},
+ defaults={"tag": "uncured"},
+ tag="stinky",
+ )
+ self.assertEqual(tag.tag, "juicy")
+ self.assertIs(created, True)
+ self.assertEqual(count + 1, self.bacon.tags.count())
+
def test_generic_update_or_create_when_updated(self):
"""
Should be able to use update_or_create from the generic related manager
@@ -74,6 +87,17 @@ class GenericRelationsTests(TestCase):
self.assertEqual(count + 1, self.bacon.tags.count())
self.assertEqual(tag.tag, "juicy")
+ def test_generic_update_or_create_when_updated_with_defaults(self):
+ count = self.bacon.tags.count()
+ tag = self.bacon.tags.create(tag="stinky")
+ self.assertEqual(count + 1, self.bacon.tags.count())
+ tag, created = self.bacon.tags.update_or_create(
+ create_defaults={"tag": "uncured"}, defaults={"tag": "juicy"}, id=tag.id
+ )
+ self.assertIs(created, False)
+ self.assertEqual(count + 1, self.bacon.tags.count())
+ self.assertEqual(tag.tag, "juicy")
+
async def test_generic_async_aupdate_or_create(self):
tag, created = await self.bacon.tags.aupdate_or_create(
id=self.fatty.id, defaults={"tag": "orange"}
@@ -86,6 +110,22 @@ class GenericRelationsTests(TestCase):
self.assertEqual(await self.bacon.tags.acount(), 3)
self.assertEqual(tag.tag, "pink")
+ async def test_generic_async_aupdate_or_create_with_create_defaults(self):
+ tag, created = await self.bacon.tags.aupdate_or_create(
+ id=self.fatty.id,
+ create_defaults={"tag": "pink"},
+ defaults={"tag": "orange"},
+ )
+ self.assertIs(created, False)
+ self.assertEqual(tag.tag, "orange")
+ self.assertEqual(await self.bacon.tags.acount(), 2)
+ tag, created = await self.bacon.tags.aupdate_or_create(
+ tag="pink", create_defaults={"tag": "brown"}
+ )
+ self.assertIs(created, True)
+ self.assertEqual(await self.bacon.tags.acount(), 3)
+ self.assertEqual(tag.tag, "brown")
+
def test_generic_get_or_create_when_created(self):
"""
Should be able to use get_or_create from the generic related manager
@@ -550,6 +590,26 @@ class GenericRelationsTests(TestCase):
self.assertFalse(created)
self.assertEqual(tag.content_object.id, diamond.id)
+ def test_update_or_create_defaults_with_create_defaults(self):
+ # update_or_create() should work with virtual fields (content_object).
+ quartz = Mineral.objects.create(name="Quartz", hardness=7)
+ diamond = Mineral.objects.create(name="Diamond", hardness=7)
+ tag, created = TaggedItem.objects.update_or_create(
+ tag="shiny",
+ create_defaults={"content_object": quartz},
+ defaults={"content_object": diamond},
+ )
+ self.assertIs(created, True)
+ self.assertEqual(tag.content_object.id, quartz.id)
+
+ tag, created = TaggedItem.objects.update_or_create(
+ tag="shiny",
+ create_defaults={"content_object": quartz},
+ defaults={"content_object": diamond},
+ )
+ self.assertIs(created, False)
+ self.assertEqual(tag.content_object.id, diamond.id)
+
def test_query_content_type(self):
msg = "Field 'content_object' does not generate an automatic reverse relation"
with self.assertRaisesMessage(FieldError, msg):
diff --git a/tests/get_or_create/models.py b/tests/get_or_create/models.py
index 6875671501..c5ba90a231 100644
--- a/tests/get_or_create/models.py
+++ b/tests/get_or_create/models.py
@@ -6,6 +6,7 @@ class Person(models.Model):
last_name = models.CharField(max_length=100)
birthday = models.DateField()
defaults = models.TextField()
+ create_defaults = models.TextField()
class DefaultPerson(models.Model):
diff --git a/tests/get_or_create/tests.py b/tests/get_or_create/tests.py
index c3a3fb0e11..0b56d6b1a2 100644
--- a/tests/get_or_create/tests.py
+++ b/tests/get_or_create/tests.py
@@ -330,15 +330,24 @@ class UpdateOrCreateTests(TestCase):
self.assertEqual(p.birthday, date(1940, 10, 10))
def test_create_twice(self):
- params = {
- "first_name": "John",
- "last_name": "Lennon",
- "birthday": date(1940, 10, 10),
- }
- Person.objects.update_or_create(**params)
- # If we execute the exact same statement, it won't create a Person.
- p, created = Person.objects.update_or_create(**params)
- self.assertFalse(created)
+ p, created = Person.objects.update_or_create(
+ first_name="John",
+ last_name="Lennon",
+ create_defaults={"birthday": date(1940, 10, 10)},
+ defaults={"birthday": date(1950, 2, 2)},
+ )
+ self.assertIs(created, True)
+ self.assertEqual(p.birthday, date(1940, 10, 10))
+ # If we execute the exact same statement, it won't create a Person, but
+ # will update the birthday.
+ p, created = Person.objects.update_or_create(
+ first_name="John",
+ last_name="Lennon",
+ create_defaults={"birthday": date(1940, 10, 10)},
+ defaults={"birthday": date(1950, 2, 2)},
+ )
+ self.assertIs(created, False)
+ self.assertEqual(p.birthday, date(1950, 2, 2))
def test_integrity(self):
"""
@@ -391,8 +400,14 @@ class UpdateOrCreateTests(TestCase):
"""
p = Publisher.objects.create(name="Acme Publishing")
book, created = p.books.update_or_create(name="The Book of Ed & Fred")
- self.assertTrue(created)
+ self.assertIs(created, True)
self.assertEqual(p.books.count(), 1)
+ book, created = p.books.update_or_create(
+ name="Basics of Django", create_defaults={"name": "Advanced Django"}
+ )
+ self.assertIs(created, True)
+ self.assertEqual(book.name, "Advanced Django")
+ self.assertEqual(p.books.count(), 2)
def test_update_with_related_manager(self):
"""
@@ -406,6 +421,14 @@ class UpdateOrCreateTests(TestCase):
book, created = p.books.update_or_create(defaults={"name": name}, id=book.id)
self.assertFalse(created)
self.assertEqual(book.name, name)
+ # create_defaults should be ignored.
+ book, created = p.books.update_or_create(
+ create_defaults={"name": "Basics of Django"},
+ defaults={"name": name},
+ id=book.id,
+ )
+ self.assertIs(created, False)
+ self.assertEqual(book.name, name)
self.assertEqual(p.books.count(), 1)
def test_create_with_many(self):
@@ -418,8 +441,16 @@ class UpdateOrCreateTests(TestCase):
book, created = author.books.update_or_create(
name="The Book of Ed & Fred", publisher=p
)
- self.assertTrue(created)
+ self.assertIs(created, True)
self.assertEqual(author.books.count(), 1)
+ book, created = author.books.update_or_create(
+ name="Basics of Django",
+ publisher=p,
+ create_defaults={"name": "Advanced Django"},
+ )
+ self.assertIs(created, True)
+ self.assertEqual(book.name, "Advanced Django")
+ self.assertEqual(author.books.count(), 2)
def test_update_with_many(self):
"""
@@ -437,6 +468,14 @@ class UpdateOrCreateTests(TestCase):
)
self.assertFalse(created)
self.assertEqual(book.name, name)
+ # create_defaults should be ignored.
+ book, created = author.books.update_or_create(
+ create_defaults={"name": "Basics of Django"},
+ defaults={"name": name},
+ id=book.id,
+ )
+ self.assertIs(created, False)
+ self.assertEqual(book.name, name)
self.assertEqual(author.books.count(), 1)
def test_defaults_exact(self):
@@ -467,6 +506,34 @@ class UpdateOrCreateTests(TestCase):
self.assertFalse(created)
self.assertEqual(obj.defaults, "another testing")
+ def test_create_defaults_exact(self):
+ """
+ If you have a field named create_defaults and want to use it as an
+ exact lookup, you need to use 'create_defaults__exact'.
+ """
+ obj, created = Person.objects.update_or_create(
+ first_name="George",
+ last_name="Harrison",
+ create_defaults__exact="testing",
+ create_defaults={
+ "birthday": date(1943, 2, 25),
+ "create_defaults": "testing",
+ },
+ )
+ self.assertIs(created, True)
+ self.assertEqual(obj.create_defaults, "testing")
+ obj, created = Person.objects.update_or_create(
+ first_name="George",
+ last_name="Harrison",
+ create_defaults__exact="testing",
+ create_defaults={
+ "birthday": date(1943, 2, 25),
+ "create_defaults": "another testing",
+ },
+ )
+ self.assertIs(created, False)
+ self.assertEqual(obj.create_defaults, "testing")
+
def test_create_callable_default(self):
obj, created = Person.objects.update_or_create(
first_name="George",
@@ -476,6 +543,16 @@ class UpdateOrCreateTests(TestCase):
self.assertIs(created, True)
self.assertEqual(obj.birthday, date(1943, 2, 25))
+ def test_create_callable_create_defaults(self):
+ obj, created = Person.objects.update_or_create(
+ first_name="George",
+ last_name="Harrison",
+ defaults={},
+ create_defaults={"birthday": lambda: date(1943, 2, 25)},
+ )
+ self.assertIs(created, True)
+ self.assertEqual(obj.birthday, date(1943, 2, 25))
+
def test_update_callable_default(self):
Person.objects.update_or_create(
first_name="George",
@@ -694,6 +771,12 @@ class InvalidCreateArgumentsTests(TransactionTestCase):
with self.assertRaisesMessage(FieldError, self.msg):
Thing.objects.update_or_create(name="a", defaults={"nonexistent": "b"})
+ def test_update_or_create_with_invalid_create_defaults(self):
+ with self.assertRaisesMessage(FieldError, self.msg):
+ Thing.objects.update_or_create(
+ name="a", create_defaults={"nonexistent": "b"}
+ )
+
def test_update_or_create_with_invalid_kwargs(self):
with self.assertRaisesMessage(FieldError, self.bad_field_msg):
Thing.objects.update_or_create(name="a", nonexistent="b")