summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Tamlyn <marc.tamlyn@gmail.com>2015-02-14 19:37:12 +0000
committerMarc Tamlyn <marc.tamlyn@gmail.com>2015-02-20 11:46:57 +0000
commitc54d73ae01cd787315ba030da17e266c49f386b3 (patch)
treeca3f1ddf19fffe2be65b16c81853176571758f5d
parent82f39bfb1ac9a4e9c67cb26259bc689947f4c6d0 (diff)
[1.8.x] Fixed #24343 -- Ensure db converters are used for foreign keys.
Joint effort between myself, Josh, Anssi and Shai. Conflicts: django/db/models/query.py tests/model_fields/models.py Backport of 4755f8fc25331c739a6f932cc8aba0cc9e62e352 from master.
-rw-r--r--django/db/models/expressions.py13
-rw-r--r--django/db/models/fields/__init__.py10
-rw-r--r--django/db/models/fields/related.py14
-rw-r--r--django/db/models/query.py6
-rw-r--r--django/db/models/sql/query.py2
-rw-r--r--tests/migrations/test_nōn_ascii_path/app/__init__.py0
-rw-r--r--tests/model_fields/models.py4
-rw-r--r--tests/model_fields/test_uuid.py10
8 files changed, 44 insertions, 15 deletions
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 8e5c4aa533..2e41803a1a 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -585,10 +585,10 @@ class Random(ExpressionNode):
class Col(ExpressionNode):
- def __init__(self, alias, target, source=None):
- if source is None:
- source = target
- super(Col, self).__init__(output_field=source)
+ def __init__(self, alias, target, output_field=None):
+ if output_field is None:
+ output_field = target
+ super(Col, self).__init__(output_field=output_field)
self.alias, self.target = alias, target
def __repr__(self):
@@ -606,7 +606,10 @@ class Col(ExpressionNode):
return [self]
def get_db_converters(self, connection):
- return self.output_field.get_db_converters(connection)
+ if self.target == self.output_field:
+ return self.output_field.get_db_converters(connection)
+ return (self.output_field.get_db_converters(connection) +
+ self.target.get_db_converters(connection))
class Ref(ExpressionNode):
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 31b0f26141..19f090863b 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -333,12 +333,12 @@ class Field(RegisterLookupMixin):
]
return []
- def get_col(self, alias, source=None):
- if source is None:
- source = self
- if alias != self.model._meta.db_table or source != self:
+ def get_col(self, alias, output_field=None):
+ if output_field is None:
+ output_field = self
+ if alias != self.model._meta.db_table or output_field != self:
from django.db.models.expressions import Col
- return Col(alias, self, source)
+ return Col(alias, self, output_field)
else:
return self.cached_col
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index a8e4a3156d..3410e5c994 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -1991,6 +1991,20 @@ class ForeignKey(ForeignObject):
def db_parameters(self, connection):
return {"type": self.db_type(connection), "check": []}
+ def convert_empty_strings(self, value, connection, context):
+ if (not value) and isinstance(value, six.string_types):
+ return None
+ return value
+
+ def get_db_converters(self, connection):
+ converters = super(ForeignKey, self).get_db_converters(connection)
+ if connection.features.interprets_empty_strings_as_nulls:
+ converters += [self.convert_empty_strings]
+ return converters
+
+ def get_col(self, alias, output_field=None):
+ return super(ForeignKey, self).get_col(alias, output_field or self.related_field)
+
class OneToOneField(ForeignKey):
"""
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 0ec3cfd730..b055205772 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -243,7 +243,7 @@ class QuerySet(object):
model_cls = klass_info['model']
select_fields = klass_info['select_fields']
model_fields_start, model_fields_end = select_fields[0], select_fields[-1] + 1
- init_list = [f[0].output_field.attname
+ init_list = [f[0].target.attname
for f in select[model_fields_start:model_fields_end]]
if len(init_list) != len(model_cls._meta.concrete_fields):
init_set = set(init_list)
@@ -1699,7 +1699,7 @@ class RelatedPopulator(object):
self.cols_start = select_fields[0]
self.cols_end = select_fields[-1] + 1
self.init_list = [
- f[0].output_field.attname for f in select[self.cols_start:self.cols_end]
+ f[0].target.attname for f in select[self.cols_start:self.cols_end]
]
self.reorder_for_init = None
else:
@@ -1708,7 +1708,7 @@ class RelatedPopulator(object):
]
reorder_map = []
for idx in select_fields:
- field = select[idx][0].output_field
+ field = select[idx][0].target
init_pos = model_init_attnames.index(field.attname)
reorder_map.append((init_pos, field.attname, idx))
reorder_map.sort()
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index f99b58b84f..344b298688 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1542,7 +1542,7 @@ class Query(object):
# database from tripping over IN (...,NULL,...) selects and returning
# nothing
col = query.select[0]
- select_field = col.field
+ select_field = col.target
alias = col.alias
if self.is_nullable(select_field):
lookup_class = select_field.get_lookup('isnull')
diff --git a/tests/migrations/test_nōn_ascii_path/app/__init__.py b/tests/migrations/test_nōn_ascii_path/app/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/migrations/test_nōn_ascii_path/app/__init__.py
diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py
index 5f580624ba..3d25c89607 100644
--- a/tests/model_fields/models.py
+++ b/tests/model_fields/models.py
@@ -369,3 +369,7 @@ class NullableUUIDModel(models.Model):
class PrimaryKeyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
+
+
+class RelatedToUUIDModel(models.Model):
+ uuid_fk = models.ForeignKey('PrimaryKeyUUIDModel')
diff --git a/tests/model_fields/test_uuid.py b/tests/model_fields/test_uuid.py
index f06f4d02a7..28e11914a1 100644
--- a/tests/model_fields/test_uuid.py
+++ b/tests/model_fields/test_uuid.py
@@ -5,7 +5,9 @@ from django.core import exceptions, serializers
from django.db import models
from django.test import TestCase
-from .models import NullableUUIDModel, PrimaryKeyUUIDModel, UUIDModel
+from .models import (
+ NullableUUIDModel, PrimaryKeyUUIDModel, RelatedToUUIDModel, UUIDModel,
+)
class TestSaveLoad(TestCase):
@@ -121,3 +123,9 @@ class TestAsPrimaryKey(TestCase):
self.assertTrue(u1_found)
self.assertTrue(u2_found)
self.assertEqual(PrimaryKeyUUIDModel.objects.count(), 2)
+
+ def test_underlying_field(self):
+ pk_model = PrimaryKeyUUIDModel.objects.create()
+ RelatedToUUIDModel.objects.create(uuid_fk=pk_model)
+ related = RelatedToUUIDModel.objects.get()
+ self.assertEqual(related.uuid_fk.pk, related.uuid_fk_id)