diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index f84ad1da61..5f4d791f02 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -150,6 +150,13 @@ class DatabaseWrapper(BaseDatabaseWrapper): 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 ce4e48330e..c9f1af1669 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -189,3 +189,10 @@ class DatabaseWrapper(BaseDatabaseWrapper): 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 4ab00414db..ea7ff96b91 100644 --- a/tests/regressiontests/backends/models.py +++ b/tests/regressiontests/backends/models.py @@ -1,5 +1,4 @@ from django.db import models -from django.db import connection class Square(models.Model): root = models.IntegerField() @@ -21,3 +20,18 @@ class SchoolClass(models.Model): last_updated = models.DateTimeField() +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 d955a098af..005fc4d86e 100644 --- a/tests/regressiontests/backends/tests.py +++ b/tests/regressiontests/backends/tests.py @@ -1,13 +1,15 @@ # -*- coding: utf-8 -*- # Unit and doctests for specific database backends. import datetime -import models import unittest -from django.db import backend, connection, DEFAULT_DB_ALIAS + +from django.conf import settings +from django.db import backend, connection, DEFAULT_DB_ALIAS, IntegrityError from django.db.backends.signals import connection_created from django.db.backends.postgresql import version as pg_version -from django.conf import settings -from django.test import TestCase +from django.test import TestCase, TransactionTestCase + +import models class Callproc(unittest.TestCase): @@ -141,6 +143,46 @@ class BackendTestCase(TestCase): cursor.executemany(query, []) self.assertEqual(models.Square.objects.count(), 11) + +# 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 def test_unicode_fetches(self): #6254: fetchone, fetchmany, fetchall return strings as unicode objects qn = connection.ops.quote_name