Commit 39d95fb6 authored by Marc Tamlyn's avatar Marc Tamlyn Committed by Tim Graham
Browse files

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.
parent a17724b7
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -70,9 +70,9 @@ class ArrayField(Field):
        size = self.size or ''
        return '%s[%s]' % (self.base_field.db_type(connection), size)

    def get_prep_value(self, value):
    def get_db_prep_value(self, value, connection, prepared=False):
        if isinstance(value, list) or isinstance(value, tuple):
            return [self.base_field.get_prep_value(i) for i in value]
            return [self.base_field.get_db_prep_value(i, connection, prepared) for i in value]
        return value

    def deconstruct(self):
+3 −0
Original line number Diff line number Diff line
@@ -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

+12 −5
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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"
+10 −0
Original line number Diff line number Diff line
@@ -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'
Loading