Loading docs/ref/models/querysets.txt +35 −0 Original line number Diff line number Diff line Loading @@ -1409,6 +1409,41 @@ has a side effect on your data. For more, see `Safe methods`_ in the HTTP spec. .. _Safe methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 .. warning:: You can use ``get_or_create()`` through :class:`~django.db.models.ManyToManyField` attributes and reverse relations. In that case you will restrict the queries inside the context of that relation. That could lead you to some integrity problems if you don't use it consistently. Being the following models:: class Chapter(models.Model): title = models.CharField(max_length=255, unique=True) class Book(models.Model): title = models.CharField(max_length=256) chapters = models.ManyToManyField(Chapter) You can use ``get_or_create()`` through Book's chapters field, but it only fetches inside the context of that book:: >>> book = Book.objects.create(title="Ulysses") >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, True) >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, False) >>> Chapter.objects.create(title="Chapter 1") <Chapter: Chapter 1> >>> book.chapters.get_or_create(title="Chapter 1") # Raises IntegrityError This is happening because it's trying to get or create "Chapter 1" through the book "Ulysses", but it can't do any of them: the relation can't fetch that chapter because it isn't related to that book, but it can't create it either because ``title`` field should be unique. bulk_create ~~~~~~~~~~~ Loading tests/get_or_create/models.py +9 −0 Original line number Diff line number Diff line Loading @@ -28,3 +28,12 @@ class ManualPrimaryKeyTest(models.Model): class Profile(models.Model): person = models.ForeignKey(Person, primary_key=True) class Tag(models.Model): text = models.CharField(max_length=256, unique=True) class Thing(models.Model): name = models.CharField(max_length=256) tags = models.ManyToManyField(Tag) tests/get_or_create/tests.py +26 −1 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ import traceback from django.db import IntegrityError from django.test import TestCase, TransactionTestCase from .models import Person, ManualPrimaryKeyTest, Profile from .models import Person, ManualPrimaryKeyTest, Profile, Tag, Thing class GetOrCreateTests(TestCase): Loading Loading @@ -77,3 +77,28 @@ class GetOrCreateTransactionTests(TransactionTestCase): pass else: self.skipTest("This backend does not support integrity checks.") class GetOrCreateThroughManyToMany(TestCase): def test_get_get_or_create(self): tag = Tag.objects.create(text='foo') a_thing = Thing.objects.create(name='a') a_thing.tags.add(tag) obj, created = a_thing.tags.get_or_create(text='foo') self.assertFalse(created) self.assertEqual(obj.pk, tag.pk) def test_create_get_or_create(self): a_thing = Thing.objects.create(name='a') obj, created = a_thing.tags.get_or_create(text='foo') self.assertTrue(created) self.assertEqual(obj.text, 'foo') self.assertIn(obj, a_thing.tags.all()) def test_something(self): Tag.objects.create(text='foo') a_thing = Thing.objects.create(name='a') self.assertRaises(IntegrityError, a_thing.tags.get_or_create, text='foo') Loading
docs/ref/models/querysets.txt +35 −0 Original line number Diff line number Diff line Loading @@ -1409,6 +1409,41 @@ has a side effect on your data. For more, see `Safe methods`_ in the HTTP spec. .. _Safe methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 .. warning:: You can use ``get_or_create()`` through :class:`~django.db.models.ManyToManyField` attributes and reverse relations. In that case you will restrict the queries inside the context of that relation. That could lead you to some integrity problems if you don't use it consistently. Being the following models:: class Chapter(models.Model): title = models.CharField(max_length=255, unique=True) class Book(models.Model): title = models.CharField(max_length=256) chapters = models.ManyToManyField(Chapter) You can use ``get_or_create()`` through Book's chapters field, but it only fetches inside the context of that book:: >>> book = Book.objects.create(title="Ulysses") >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, True) >>> book.chapters.get_or_create(title="Telemachus") (<Chapter: Telemachus>, False) >>> Chapter.objects.create(title="Chapter 1") <Chapter: Chapter 1> >>> book.chapters.get_or_create(title="Chapter 1") # Raises IntegrityError This is happening because it's trying to get or create "Chapter 1" through the book "Ulysses", but it can't do any of them: the relation can't fetch that chapter because it isn't related to that book, but it can't create it either because ``title`` field should be unique. bulk_create ~~~~~~~~~~~ Loading
tests/get_or_create/models.py +9 −0 Original line number Diff line number Diff line Loading @@ -28,3 +28,12 @@ class ManualPrimaryKeyTest(models.Model): class Profile(models.Model): person = models.ForeignKey(Person, primary_key=True) class Tag(models.Model): text = models.CharField(max_length=256, unique=True) class Thing(models.Model): name = models.CharField(max_length=256) tags = models.ManyToManyField(Tag)
tests/get_or_create/tests.py +26 −1 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ import traceback from django.db import IntegrityError from django.test import TestCase, TransactionTestCase from .models import Person, ManualPrimaryKeyTest, Profile from .models import Person, ManualPrimaryKeyTest, Profile, Tag, Thing class GetOrCreateTests(TestCase): Loading Loading @@ -77,3 +77,28 @@ class GetOrCreateTransactionTests(TransactionTestCase): pass else: self.skipTest("This backend does not support integrity checks.") class GetOrCreateThroughManyToMany(TestCase): def test_get_get_or_create(self): tag = Tag.objects.create(text='foo') a_thing = Thing.objects.create(name='a') a_thing.tags.add(tag) obj, created = a_thing.tags.get_or_create(text='foo') self.assertFalse(created) self.assertEqual(obj.pk, tag.pk) def test_create_get_or_create(self): a_thing = Thing.objects.create(name='a') obj, created = a_thing.tags.get_or_create(text='foo') self.assertTrue(created) self.assertEqual(obj.text, 'foo') self.assertIn(obj, a_thing.tags.all()) def test_something(self): Tag.objects.create(text='foo') a_thing = Thing.objects.create(name='a') self.assertRaises(IntegrityError, a_thing.tags.get_or_create, text='foo')