1
0
mirror of https://github.com/django/django.git synced 2024-12-23 01:25:58 +00:00

Fixes #18896. Add tests verifying that you can get IntegrityErrors using get_or_create through relations like M2M, and it also adds a note into the documentation warning about it

This commit is contained in:
Pablo Recio 2013-05-19 14:15:36 +02:00
parent d34b1c29e2
commit 65f9e0affd
3 changed files with 70 additions and 1 deletions

View File

@ -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
~~~~~~~~~~~

View File

@ -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)

View File

@ -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):
@ -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')