summaryrefslogtreecommitdiff
path: root/django/db/backends
diff options
context:
space:
mode:
authorMarc Tamlyn <marc.tamlyn@gmail.com>2015-01-10 18:13:28 +0000
committerTim Graham <timograham@gmail.com>2015-01-16 16:15:16 -0500
commit39d95fb6ada99c59d47fa0eae6d3128abafe2d58 (patch)
treef514f85027835d6504a80982184467c41da601c7 /django/db/backends
parenta17724b791275578334bcdc66b3a8113eb86605e (diff)
Fixed #24092 -- Widened base field support for ArrayField.
Several issues resolved here, following from a report that a base_field of GenericIpAddressField was failing. We were using get_prep_value instead of get_db_prep_value in ArrayField which was bypassing any extra modifications to the value being made in the base field's get_db_prep_value. Changing this broke datetime support, so the postgres backend has gained the relevant operation methods to send dates/times/datetimes directly to the db backend instead of casting them to strings. Similarly, a new database feature has been added allowing the uuid to be passed directly to the backend, as we do with timedeltas. On the other side, psycopg2 expects an Inet() instance for IP address fields, so we add a value_to_db_ipaddress method to wrap the strings on postgres. We also have to manually add a database adapter to psycopg2, as we do not wish to use the built in adapter which would turn everything into Inet() instances. Thanks to smclenithan for the report.
Diffstat (limited to 'django/db/backends')
-rw-r--r--django/db/backends/base/features.py3
-rw-r--r--django/db/backends/base/operations.py17
-rw-r--r--django/db/backends/oracle/operations.py2
-rw-r--r--django/db/backends/postgresql_psycopg2/base.py10
-rw-r--r--django/db/backends/postgresql_psycopg2/features.py1
-rw-r--r--django/db/backends/postgresql_psycopg2/operations.py28
6 files changed, 48 insertions, 13 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py
index fe13827b77..0f6ee0efe3 100644
--- a/django/db/backends/base/features.py
+++ b/django/db/backends/base/features.py
@@ -59,6 +59,9 @@ class BaseDatabaseFeatures(object):
supports_subqueries_in_group_by = True
supports_bitwise_or = True
+ # Is there a true datatype for uuid?
+ has_native_uuid_field = False
+
# Is there a true datatype for timedeltas?
has_native_duration_field = False
diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py
index c4e78e719e..24bcbb3d08 100644
--- a/django/db/backends/base/operations.py
+++ b/django/db/backends/base/operations.py
@@ -219,7 +219,7 @@ class BaseDatabaseOperations(object):
"""
return cursor.lastrowid
- def lookup_cast(self, lookup_type):
+ def lookup_cast(self, lookup_type, internal_type=None):
"""
Returns the string to use in a query when performing lookups
("contains", "like", etc). The resulting string should contain a '%s'
@@ -442,7 +442,7 @@ class BaseDatabaseOperations(object):
def value_to_db_date(self, value):
"""
- Transform a date value to an object compatible with what is expected
+ Transforms a date value to an object compatible with what is expected
by the backend driver for date columns.
"""
if value is None:
@@ -451,7 +451,7 @@ class BaseDatabaseOperations(object):
def value_to_db_datetime(self, value):
"""
- Transform a datetime value to an object compatible with what is expected
+ Transforms a datetime value to an object compatible with what is expected
by the backend driver for datetime columns.
"""
if value is None:
@@ -460,7 +460,7 @@ class BaseDatabaseOperations(object):
def value_to_db_time(self, value):
"""
- Transform a time value to an object compatible with what is expected
+ Transforms a time value to an object compatible with what is expected
by the backend driver for time columns.
"""
if value is None:
@@ -471,11 +471,18 @@ class BaseDatabaseOperations(object):
def value_to_db_decimal(self, value, max_digits, decimal_places):
"""
- Transform a decimal.Decimal value to an object compatible with what is
+ Transforms a decimal.Decimal value to an object compatible with what is
expected by the backend driver for decimal (numeric) columns.
"""
return utils.format_number(value, max_digits, decimal_places)
+ def value_to_db_ipaddress(self, value):
+ """
+ Transforms a string representation of an IP address into the expected
+ type for the backend driver.
+ """
+ return value
+
def year_lookup_bounds_for_date_field(self, value):
"""
Returns a two-elements list with the lower and upper bound to be used
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index f00fd3fbea..fe9c93ba90 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -246,7 +246,7 @@ WHEN (new.%(col_name)s IS NULL)
cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
return cursor.fetchone()[0]
- def lookup_cast(self, lookup_type):
+ def lookup_cast(self, lookup_type, internal_type=None):
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
return "UPPER(%s)"
return "%s"
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index 34b2870773..37433c3987 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -38,6 +38,16 @@ psycopg2.extensions.register_adapter(SafeBytes, psycopg2.extensions.QuotedString
psycopg2.extensions.register_adapter(SafeText, psycopg2.extensions.QuotedString)
psycopg2.extras.register_uuid()
+# Register support for inet[] manually so we don't have to handle the Inet()
+# object on load all the time.
+INETARRAY_OID = 1041
+INETARRAY = psycopg2.extensions.new_array_type(
+ (INETARRAY_OID,),
+ 'INETARRAY',
+ psycopg2.extensions.UNICODE,
+)
+psycopg2.extensions.register_type(INETARRAY)
+
class DatabaseWrapper(BaseDatabaseWrapper):
vendor = 'postgresql'
diff --git a/django/db/backends/postgresql_psycopg2/features.py b/django/db/backends/postgresql_psycopg2/features.py
index 64acd0570a..6bb6de1a96 100644
--- a/django/db/backends/postgresql_psycopg2/features.py
+++ b/django/db/backends/postgresql_psycopg2/features.py
@@ -6,6 +6,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
needs_datetime_string_cast = False
can_return_id_from_insert = True
has_real_datatype = True
+ has_native_uuid_field = True
has_native_duration_field = True
driver_supports_timedelta_args = True
can_defer_constraint_checks = True
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
index 8e90a4020b..27b19db459 100644
--- a/django/db/backends/postgresql_psycopg2/operations.py
+++ b/django/db/backends/postgresql_psycopg2/operations.py
@@ -3,6 +3,8 @@ from __future__ import unicode_literals
from django.conf import settings
from django.db.backends.base.operations import BaseDatabaseOperations
+from psycopg2.extras import Inet
+
class DatabaseOperations(BaseDatabaseOperations):
def unification_cast_sql(self, output_field):
@@ -57,13 +59,16 @@ class DatabaseOperations(BaseDatabaseOperations):
def deferrable_sql(self):
return " DEFERRABLE INITIALLY DEFERRED"
- def lookup_cast(self, lookup_type):
+ def lookup_cast(self, lookup_type, internal_type=None):
lookup = '%s'
# Cast text lookups to text to allow things like filter(x__contains=4)
if lookup_type in ('iexact', 'contains', 'icontains', 'startswith',
'istartswith', 'endswith', 'iendswith', 'regex', 'iregex'):
- lookup = "%s::text"
+ if internal_type in ('IPAddressField', 'GenericIPAddressField'):
+ lookup = "HOST(%s)"
+ else:
+ lookup = "%s::text"
# Use UPPER(x) for case-insensitive lookups; it's faster.
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
@@ -71,11 +76,6 @@ class DatabaseOperations(BaseDatabaseOperations):
return lookup
- def field_cast_sql(self, db_type, internal_type):
- if internal_type == "GenericIPAddressField" or internal_type == "IPAddressField":
- return 'HOST(%s)'
- return '%s'
-
def last_insert_id(self, cursor, table_name, pk_name):
# Use pg_get_serial_sequence to get the underlying sequence name
# from the table name and column name (available since PostgreSQL 8)
@@ -224,3 +224,17 @@ class DatabaseOperations(BaseDatabaseOperations):
def bulk_insert_sql(self, fields, num_values):
items_sql = "(%s)" % ", ".join(["%s"] * len(fields))
return "VALUES " + ", ".join([items_sql] * num_values)
+
+ def value_to_db_date(self, value):
+ return value
+
+ def value_to_db_datetime(self, value):
+ return value
+
+ def value_to_db_time(self, value):
+ return value
+
+ def value_to_db_ipaddress(self, value):
+ if value:
+ return Inet(value)
+ return None