diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 5e47a37d3fc71..12631785bc579 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -153,6 +153,13 @@ def _cursor(self): cursor.execute("SET client_encoding to 'UNICODE'") return UnicodeCursorWrapper(cursor, 'utf-8') + def _commit(self): + if self.connection is not None: + try: + return self.connection.commit() + except Database.IntegrityError, e: + raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2] + def typecast_string(s): """ Cast all returned strings to unicode strings. diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 9e5fb53d5624d..71ec58153a717 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -192,3 +192,10 @@ def _set_isolation_level(self, level): finally: self.isolation_level = level self.features.uses_savepoints = bool(level) + + def _commit(self): + if self.connection is not None: + try: + return self.connection.commit() + except Database.IntegrityError, e: + raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2] diff --git a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py index 3ad4a60c0279d..89e3fd27ffb02 100644 --- a/tests/regressiontests/backends/models.py +++ b/tests/regressiontests/backends/models.py @@ -1,8 +1,7 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType -from django.conf import settings from django.db import models -from django.db import connection, DEFAULT_DB_ALIAS +from django.db import connection class Square(models.Model): @@ -55,3 +54,18 @@ class Meta: db_table = 'CaseSensitive_Post' +class Reporter(models.Model): + first_name = models.CharField(max_length=30) + last_name = models.CharField(max_length=30) + + def __unicode__(self): + return u"%s %s" % (self.first_name, self.last_name) + + +class Article(models.Model): + headline = models.CharField(max_length=100) + pub_date = models.DateField() + reporter = models.ForeignKey(Reporter) + + def __unicode__(self): + return self.headline diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py index c5d13285d3239..e457629880e43 100644 --- a/tests/regressiontests/backends/tests.py +++ b/tests/regressiontests/backends/tests.py @@ -2,13 +2,11 @@ # Unit and doctests for specific database backends. import datetime -from django.conf import settings -from django.core import management from django.core.management.color import no_style -from django.db import backend, connection, connections, DEFAULT_DB_ALIAS +from django.db import backend, connection, connections, DEFAULT_DB_ALIAS, IntegrityError from django.db.backends.signals import connection_created from django.db.backends.postgresql import version as pg_version -from django.test import TestCase, skipUnlessDBFeature +from django.test import TestCase, skipUnlessDBFeature, TransactionTestCase from django.utils import unittest from regressiontests.backends import models @@ -225,3 +223,43 @@ def test_unicode_fetches(self): self.assertEqual(list(cursor.fetchmany(2)), [(u'Jane', u'Doe'), (u'John', u'Doe')]) self.assertEqual(list(cursor.fetchall()), [(u'Mary', u'Agnelline'), (u'Peter', u'Parker')]) + +# We don't make these tests conditional because that means we would need to +# check and differentiate between: +# * MySQL+InnoDB, MySQL+MYISAM (something we currently can't do). +# * if sqlite3 (if/once we get #14204 fixed) has referential integrity turned +# on or not, something that would be controlled by runtime support and user +# preference. +# verify if its type is django.database.db.IntegrityError. + +class FkConstraintsTests(TransactionTestCase): + + def setUp(self): + # Create a Reporter. + self.r = models.Reporter.objects.create(first_name='John', last_name='Smith') + + def test_integrity_checks_on_creation(self): + """ + Try to create a model instance that violates a FK constraint. If it + fails it should fail with IntegrityError. + """ + a = models.Article(headline="This is a test", pub_date=datetime.datetime(2005, 7, 27), reporter_id=30) + try: + a.save() + except IntegrityError: + pass + + def test_integrity_checks_on_update(self): + """ + Try to update a model instance introducing a FK constraint violation. + If it fails it should fail with IntegrityError. + """ + # Create an Article. + models.Article.objects.create(headline="Test article", pub_date=datetime.datetime(2010, 9, 4), reporter=self.r) + # Retrive it from the DB + a = models.Article.objects.get(headline="Test article") + a.reporter_id = 30 + try: + a.save() + except IntegrityError: + pass