diff --git a/AUTHORS b/AUTHORS index dbdc38230c..0ddbc36804 100644 --- a/AUTHORS +++ b/AUTHORS @@ -646,6 +646,7 @@ answer newbie questions, and generally made Django that much better: Rick Wagner Gavin Wahl wam-djangobug@wamber.net + Albert Wang Wang Chun Filip Wasilewski Dan Watson diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index f490066516..98ec36b656 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -1692,11 +1692,12 @@ class ForeignKey(ForeignObject): return field_default def get_db_prep_save(self, value, connection): - if value == '' or value is None: + if value is None or (value == '' and + (not self.related_field.empty_strings_allowed or + connection.features.interprets_empty_strings_as_nulls)): return None else: - return self.related_field.get_db_prep_save(value, - connection=connection) + return self.related_field.get_db_prep_save(value, connection=connection) def value_to_string(self, obj): if not obj: diff --git a/django/db/models/query.py b/django/db/models/query.py index 6051b9f859..725c04caca 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1416,9 +1416,12 @@ def get_cached_row(row, index_start, using, klass_info, offset=0, klass, field_names, field_count, related_fields, reverse_related_fields, pk_idx = klass_info fields = row[index_start:index_start + field_count] - # If the pk column is None (or the Oracle equivalent ''), then the related + # If the pk column is None (or the equivalent '' in the case the + # connection interprets empty strings as nulls), then the related # object must be non-existent - set the relation to None. - if fields[pk_idx] is None or fields[pk_idx] == '': + if (fields[pk_idx] is None or + (connections[using].features.interprets_empty_strings_as_nulls and + fields[pk_idx] == '')): obj = None elif field_names: fields = list(fields) diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py index 3651d99ad8..e110578ce9 100644 --- a/tests/model_fields/models.py +++ b/tests/model_fields/models.py @@ -72,12 +72,21 @@ class BooleanModel(models.Model): string = models.CharField(max_length=10, default='abc') +class PrimaryKeyCharModel(models.Model): + string = models.CharField(max_length=10, primary_key=True) + + class FksToBooleans(models.Model): """Model wih FKs to models with {Null,}BooleanField's, #15040""" bf = models.ForeignKey(BooleanModel) nbf = models.ForeignKey(NullBooleanModel) +class FkToChar(models.Model): + """Model with FK to a model with a CharField primary key, #19299""" + out = models.ForeignKey(PrimaryKeyCharModel) + + class RenamedField(models.Model): modelname = models.IntegerField(name="fieldname", choices=((1, 'One'),)) diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index 45ffe44e7a..54c13dd0ee 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -22,8 +22,8 @@ from django.utils.functional import lazy from .models import ( Foo, Bar, Whiz, BigD, BigS, BigInt, Post, NullBooleanModel, - BooleanModel, DataModel, Document, RenamedField, - VerboseNameField, FksToBooleans) + BooleanModel, PrimaryKeyCharModel, DataModel, Document, RenamedField, + VerboseNameField, FksToBooleans, FkToChar) class BasicFieldTests(test.TestCase): @@ -146,6 +146,17 @@ class ForeignKeyTests(test.TestCase): b = Bar.objects.create(b="bcd") self.assertEqual(b.a, a) + @test.skipIfDBFeature('interprets_empty_strings_as_nulls') + def test_empty_string_fk(self): + """ + Test that foreign key values to empty strings don't get converted + to None (#19299) + """ + char_model_empty = PrimaryKeyCharModel.objects.create(string='') + fk_model_empty = FkToChar.objects.create(out=char_model_empty) + fk_model_empty = FkToChar.objects.select_related('out').get(id=fk_model_empty.pk) + self.assertEqual(fk_model_empty.out, char_model_empty) + class DateTimeFieldTests(unittest.TestCase): def test_datetimefield_to_python_usecs(self):