Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixes #18896. Add tests verifying that you can get IntegrityErrors us…

…ing get_or_create through relations like M2M, and it also adds a note into the documentation warning about it
  • Loading branch information...
commit 65f9e0affd8ca04e2c597c43c1547ef7c888ec2a 1 parent d34b1c2
Pablo Recio authored May 19, 2013
35  docs/ref/models/querysets.txt
@@ -1409,6 +1409,41 @@ has a side effect on your data. For more, see `Safe methods`_ in the HTTP spec.
1409 1409
 
1410 1410
 .. _Safe methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
1411 1411
 
  1412
+.. warning::
  1413
+
  1414
+  You can use ``get_or_create()`` through :class:`~django.db.models.ManyToManyField`
  1415
+  attributes and reverse relations. In that case you will restrict the queries
  1416
+  inside the context of that relation. That could lead you to some integrity
  1417
+  problems if you don't use it consistently.
  1418
+
  1419
+  Being the following models::
  1420
+
  1421
+      class Chapter(models.Model):
  1422
+          title = models.CharField(max_length=255, unique=True)
  1423
+
  1424
+      class Book(models.Model):
  1425
+          title = models.CharField(max_length=256)
  1426
+          chapters = models.ManyToManyField(Chapter)
  1427
+
  1428
+  You can use ``get_or_create()`` through Book's chapters field, but it only
  1429
+  fetches inside the context of that book::
  1430
+
  1431
+      >>> book = Book.objects.create(title="Ulysses")
  1432
+      >>> book.chapters.get_or_create(title="Telemachus")
  1433
+      (<Chapter: Telemachus>, True)
  1434
+      >>> book.chapters.get_or_create(title="Telemachus")
  1435
+      (<Chapter: Telemachus>, False)
  1436
+      >>> Chapter.objects.create(title="Chapter 1")
  1437
+      <Chapter: Chapter 1>
  1438
+      >>> book.chapters.get_or_create(title="Chapter 1")
  1439
+      # Raises IntegrityError
  1440
+
  1441
+  This is happening because it's trying to get or create "Chapter 1" through the
  1442
+  book "Ulysses", but it can't do any of them: the relation can't fetch that
  1443
+  chapter because it isn't related to that book, but it can't create it either
  1444
+  because ``title`` field should be unique.
  1445
+
  1446
+
1412 1447
 bulk_create
1413 1448
 ~~~~~~~~~~~
1414 1449
 
9  tests/get_or_create/models.py
@@ -28,3 +28,12 @@ class ManualPrimaryKeyTest(models.Model):
28 28
 
29 29
 class Profile(models.Model):
30 30
     person = models.ForeignKey(Person, primary_key=True)
  31
+
  32
+
  33
+class Tag(models.Model):
  34
+    text = models.CharField(max_length=256, unique=True)
  35
+
  36
+
  37
+class Thing(models.Model):
  38
+    name = models.CharField(max_length=256)
  39
+    tags = models.ManyToManyField(Tag)
27  tests/get_or_create/tests.py
@@ -6,7 +6,7 @@
6 6
 from django.db import IntegrityError
7 7
 from django.test import TestCase, TransactionTestCase
8 8
 
9  
-from .models import Person, ManualPrimaryKeyTest, Profile
  9
+from .models import Person, ManualPrimaryKeyTest, Profile, Tag, Thing
10 10
 
11 11
 
12 12
 class GetOrCreateTests(TestCase):
@@ -77,3 +77,28 @@ def test_get_or_create_integrityerror(self):
77 77
             pass
78 78
         else:
79 79
             self.skipTest("This backend does not support integrity checks.")
  80
+
  81
+
  82
+class GetOrCreateThroughManyToMany(TestCase):
  83
+
  84
+    def test_get_get_or_create(self):
  85
+        tag = Tag.objects.create(text='foo')
  86
+        a_thing = Thing.objects.create(name='a')
  87
+        a_thing.tags.add(tag)
  88
+        obj, created = a_thing.tags.get_or_create(text='foo')
  89
+
  90
+        self.assertFalse(created)
  91
+        self.assertEqual(obj.pk, tag.pk)
  92
+
  93
+    def test_create_get_or_create(self):
  94
+        a_thing = Thing.objects.create(name='a')
  95
+        obj, created = a_thing.tags.get_or_create(text='foo')
  96
+
  97
+        self.assertTrue(created)
  98
+        self.assertEqual(obj.text, 'foo')
  99
+        self.assertIn(obj, a_thing.tags.all())
  100
+
  101
+    def test_something(self):
  102
+        Tag.objects.create(text='foo')
  103
+        a_thing = Thing.objects.create(name='a')
  104
+        self.assertRaises(IntegrityError, a_thing.tags.get_or_create, text='foo')

0 notes on commit 65f9e0a

Please sign in to comment.
Something went wrong with that request. Please try again.