Implemented BaseDatabaseFeatures and changed all code to access it -- connection.features.foo instead of backend.foo
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5974 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
4f82250512
commit
1a8f9b2b97
|
@ -34,7 +34,7 @@ class Command(NoArgsCommand):
|
|||
# Get a list of all existing database tables,
|
||||
# so we know what needs to be added.
|
||||
table_list = table_list()
|
||||
if backend.uses_case_insensitive_names:
|
||||
if connection.features.uses_case_insensitive_names:
|
||||
table_name_converter = str.upper
|
||||
else:
|
||||
table_name_converter = lambda x: x
|
||||
|
|
|
@ -15,12 +15,12 @@ def table_list():
|
|||
|
||||
def installed_models(table_list):
|
||||
"Returns a set of all models that are installed, given a list of existing table names."
|
||||
from django.db import backend, models
|
||||
from django.db import connection, models
|
||||
all_models = []
|
||||
for app in models.get_apps():
|
||||
for model in models.get_models(app):
|
||||
all_models.append(model)
|
||||
if backend.uses_case_insensitive_names:
|
||||
if connection.features.uses_case_insensitive_names:
|
||||
converter = lambda x: x.upper()
|
||||
else:
|
||||
converter = lambda x: x
|
||||
|
@ -110,7 +110,7 @@ def sql_delete(app, style):
|
|||
table_names = introspection.get_table_list(cursor)
|
||||
else:
|
||||
table_names = []
|
||||
if backend.uses_case_insensitive_names:
|
||||
if connection.features.uses_case_insensitive_names:
|
||||
table_name_converter = str.upper
|
||||
else:
|
||||
table_name_converter = lambda x: x
|
||||
|
@ -138,7 +138,7 @@ def sql_delete(app, style):
|
|||
# Drop the table now
|
||||
output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
|
||||
style.SQL_TABLE(qn(model._meta.db_table))))
|
||||
if backend.supports_constraints and model in references_to_delete:
|
||||
if connection.features.supports_constraints and model in references_to_delete:
|
||||
for rel_class, f in references_to_delete[model]:
|
||||
table = rel_class._meta.db_table
|
||||
col = f.column
|
||||
|
@ -232,11 +232,11 @@ def sql_model_create(model, style, known_models=set()):
|
|||
field_output = [style.SQL_FIELD(qn(f.column)),
|
||||
style.SQL_COLTYPE(col_type)]
|
||||
field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
|
||||
if f.unique and (not f.primary_key or backend.allows_unique_and_pk):
|
||||
if f.unique and (not f.primary_key or connection.features.allows_unique_and_pk):
|
||||
field_output.append(style.SQL_KEYWORD('UNIQUE'))
|
||||
if f.primary_key:
|
||||
field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
|
||||
if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys:
|
||||
if tablespace and connection.features.supports_tablespaces and (f.unique or f.primary_key) and connection.features.autoindexes_primary_keys:
|
||||
# We must specify the index tablespace inline, because we
|
||||
# won't be generating a CREATE INDEX statement for this field.
|
||||
field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))
|
||||
|
@ -264,7 +264,7 @@ def sql_model_create(model, style, known_models=set()):
|
|||
for i, line in enumerate(table_output): # Combine and add commas.
|
||||
full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
|
||||
full_statement.append(')')
|
||||
if opts.db_tablespace and backend.supports_tablespaces:
|
||||
if opts.db_tablespace and connection.features.supports_tablespaces:
|
||||
full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
|
||||
full_statement.append(';')
|
||||
final_output.append('\n'.join(full_statement))
|
||||
|
@ -287,7 +287,7 @@ def sql_for_pending_references(model, style, pending_references):
|
|||
|
||||
qn = connection.ops.quote_name
|
||||
final_output = []
|
||||
if backend.supports_constraints:
|
||||
if connection.features.supports_constraints:
|
||||
opts = model._meta
|
||||
if model in pending_references:
|
||||
for rel_class, f in pending_references[model]:
|
||||
|
@ -316,7 +316,7 @@ def many_to_many_sql_for_model(model, style):
|
|||
for f in opts.many_to_many:
|
||||
if not isinstance(f.rel, generic.GenericRel):
|
||||
tablespace = f.db_tablespace or opts.db_tablespace
|
||||
if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys:
|
||||
if tablespace and connection.features.supports_tablespaces and connection.features.autoindexes_primary_keys:
|
||||
tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True)
|
||||
else:
|
||||
tablespace_sql = ''
|
||||
|
@ -347,7 +347,7 @@ def many_to_many_sql_for_model(model, style):
|
|||
style.SQL_FIELD(qn(f.m2m_reverse_name())),
|
||||
tablespace_sql))
|
||||
table_output.append(')')
|
||||
if opts.db_tablespace and backend.supports_tablespaces:
|
||||
if opts.db_tablespace and connection.features.supports_tablespaces:
|
||||
# f.db_tablespace is only for indices, so ignore its value here.
|
||||
table_output.append(connection.ops.tablespace_sql(opts.db_tablespace))
|
||||
table_output.append(';')
|
||||
|
@ -395,10 +395,10 @@ def sql_indexes_for_model(model, style):
|
|||
|
||||
qn = connection.ops.quote_name
|
||||
for f in model._meta.fields:
|
||||
if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys):
|
||||
if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
|
||||
unique = f.unique and 'UNIQUE ' or ''
|
||||
tablespace = f.db_tablespace or model._meta.db_tablespace
|
||||
if tablespace and backend.supports_tablespaces:
|
||||
if tablespace and connection.features.supports_tablespaces:
|
||||
tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
|
||||
else:
|
||||
tablespace_sql = ''
|
||||
|
|
|
@ -39,6 +39,16 @@ class BaseDatabaseWrapper(local):
|
|||
from django.db.backends import util
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
|
||||
class BaseDatabaseFeatures(object):
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = True
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
class BaseDatabaseOperations(object):
|
||||
"""
|
||||
This class encapsulates all backend-specific differences, such as the way
|
||||
|
|
|
@ -4,7 +4,7 @@ ADO MSSQL database backend for Django.
|
|||
Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
|
||||
"""
|
||||
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||
try:
|
||||
import adodbapi as Database
|
||||
except ImportError, e:
|
||||
|
@ -48,6 +48,9 @@ def variantToPython(variant, adType):
|
|||
return res
|
||||
Database.convertVariantToPython = variantToPython
|
||||
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
supports_tablespaces = True
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
return "DATEPART(%s, %s)" % (lookup_type, field_name)
|
||||
|
@ -79,6 +82,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return "ON %s" % self.quote_name(tablespace)
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
|
||||
def _cursor(self, settings):
|
||||
|
@ -93,15 +97,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
self.connection = Database.connect(conn_string)
|
||||
return self.connection.cursor()
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = True
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = True
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
OPERATOR_MAPPING = {
|
||||
'exact': '= %s',
|
||||
'iexact': 'LIKE %s',
|
||||
|
|
|
@ -21,12 +21,13 @@ class DatabaseError(Exception):
|
|||
class IntegrityError(DatabaseError):
|
||||
pass
|
||||
|
||||
class DatabaseOperations(object):
|
||||
class ComplainOnGetattr(object):
|
||||
def __getattr__(self, *args, **kwargs):
|
||||
complain()
|
||||
|
||||
class DatabaseWrapper(object):
|
||||
ops = DatabaseOperations()
|
||||
ops = ComplainOnGetattr()
|
||||
features = ComplainOnGetattr()
|
||||
cursor = complain
|
||||
_commit = complain
|
||||
_rollback = ignore
|
||||
|
@ -37,7 +38,4 @@ class DatabaseWrapper(object):
|
|||
def close(self):
|
||||
pass # close()
|
||||
|
||||
supports_constraints = False
|
||||
supports_tablespaces = False
|
||||
|
||||
OPERATOR_MAPPING = {}
|
||||
|
|
|
@ -4,7 +4,7 @@ MySQL database backend for Django.
|
|||
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
|
||||
"""
|
||||
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||
try:
|
||||
import MySQLdb as Database
|
||||
except ImportError, e:
|
||||
|
@ -53,6 +53,9 @@ server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})')
|
|||
# standard util.CursorDebugWrapper can be used. Also, using sql_mode
|
||||
# TRADITIONAL will automatically cause most warnings to be treated as errors.
|
||||
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
autoindexes_primary_keys = False
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
|
||||
|
@ -116,6 +119,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return []
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -175,15 +179,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
self.server_version = tuple([int(x) for x in m.groups()])
|
||||
return self.server_version
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = False
|
||||
needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
OPERATOR_MAPPING = {
|
||||
'exact': '= %s',
|
||||
'iexact': 'LIKE %s',
|
||||
|
|
|
@ -4,7 +4,7 @@ MySQL database backend for Django.
|
|||
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
|
||||
"""
|
||||
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||
from django.utils.encoding import force_unicode
|
||||
try:
|
||||
import MySQLdb as Database
|
||||
|
@ -63,6 +63,9 @@ class MysqlDebugWrapper:
|
|||
else:
|
||||
return getattr(self.cursor, attr)
|
||||
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
autoindexes_primary_keys = False
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
|
||||
|
@ -126,6 +129,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return []
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -194,15 +198,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
self.server_version = tuple([int(x) for x in m.groups()])
|
||||
return self.server_version
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = False
|
||||
needs_datetime_string_cast = True # MySQLdb requires a typecast for dates
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
OPERATOR_MAPPING = {
|
||||
'exact': '= %s',
|
||||
'iexact': 'LIKE %s',
|
||||
|
|
|
@ -4,7 +4,7 @@ Oracle database backend for Django.
|
|||
Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
|
||||
"""
|
||||
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.utils.encoding import smart_str, force_unicode
|
||||
import datetime
|
||||
|
@ -21,6 +21,14 @@ except ImportError, e:
|
|||
DatabaseError = Database.Error
|
||||
IntegrityError = Database.IntegrityError
|
||||
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
allows_group_by_ordinal = False
|
||||
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
|
||||
needs_datetime_string_cast = False
|
||||
needs_upper_for_iops = True
|
||||
supports_tablespaces = True
|
||||
uses_case_insensitive_names = True
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def autoinc_sql(self, table):
|
||||
# To simulate auto-incrementing primary keys in Oracle, we have to
|
||||
|
@ -128,6 +136,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
|
||||
def _valid_connection(self):
|
||||
|
@ -151,15 +160,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
|
||||
return cursor
|
||||
|
||||
allows_group_by_ordinal = False
|
||||
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = False
|
||||
needs_upper_for_iops = True
|
||||
supports_constraints = True
|
||||
supports_tablespaces = True
|
||||
uses_case_insensitive_names = True
|
||||
|
||||
class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
"""
|
||||
Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
|
||||
|
|
|
@ -5,7 +5,7 @@ Requires psycopg 1: http://initd.org/projects/psycopg1
|
|||
"""
|
||||
|
||||
from django.utils.encoding import smart_str, smart_unicode
|
||||
from django.db.backends import BaseDatabaseWrapper, util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
|
||||
from django.db.backends.postgresql.operations import DatabaseOperations
|
||||
try:
|
||||
import psycopg as Database
|
||||
|
@ -56,7 +56,11 @@ class UnicodeCursorWrapper(object):
|
|||
else:
|
||||
return getattr(self.cursor, attr)
|
||||
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
pass # This backend uses all the defaults.
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
|
||||
def _cursor(self, settings):
|
||||
|
@ -87,15 +91,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
||||
return cursor
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = True
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
def typecast_string(s):
|
||||
"""
|
||||
Cast all returned strings to unicode strings.
|
||||
|
|
|
@ -4,7 +4,7 @@ PostgreSQL database backend for Django.
|
|||
Requires psycopg 2: http://initd.org/projects/psycopg2
|
||||
"""
|
||||
|
||||
from django.db.backends import BaseDatabaseWrapper
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
|
||||
from django.db.backends.postgresql.operations import DatabaseOperations
|
||||
try:
|
||||
import psycopg2 as Database
|
||||
|
@ -18,7 +18,11 @@ IntegrityError = Database.IntegrityError
|
|||
|
||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
needs_datetime_string_cast = False
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
|
||||
def _cursor(self, settings):
|
||||
|
@ -49,15 +53,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
self.ops.postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]
|
||||
return cursor
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = False
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = True
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
OPERATOR_MAPPING = {
|
||||
'exact': '= %s',
|
||||
'iexact': 'ILIKE %s',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/).
|
||||
"""
|
||||
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseOperations, util
|
||||
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
|
||||
try:
|
||||
try:
|
||||
from sqlite3 import dbapi2 as Database
|
||||
|
@ -34,6 +34,9 @@ Database.register_converter("TIMESTAMP", util.typecast_timestamp)
|
|||
Database.register_converter("decimal", util.typecast_decimal)
|
||||
Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal)
|
||||
|
||||
class DatabaseFeatures(BaseDatabaseFeatures):
|
||||
supports_constraints = False
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
def date_extract_sql(self, lookup_type, field_name):
|
||||
# sqlite doesn't support extract, so we fake it with the user-defined
|
||||
|
@ -70,6 +73,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return sql
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
features = DatabaseFeatures()
|
||||
ops = DatabaseOperations()
|
||||
|
||||
def _cursor(self, settings):
|
||||
|
@ -111,15 +115,6 @@ class SQLiteCursorWrapper(Database.Cursor):
|
|||
def convert_query(self, query, num_params):
|
||||
return query % tuple("?" * num_params)
|
||||
|
||||
allows_group_by_ordinal = True
|
||||
allows_unique_and_pk = True
|
||||
autoindexes_primary_keys = True
|
||||
needs_datetime_string_cast = True
|
||||
needs_upper_for_iops = False
|
||||
supports_constraints = False
|
||||
supports_tablespaces = False
|
||||
uses_case_insensitive_names = False
|
||||
|
||||
def _sqlite_extract(lookup_type, dt):
|
||||
try:
|
||||
dt = util.typecast_timestamp(dt)
|
||||
|
|
|
@ -651,7 +651,7 @@ class DateQuerySet(QuerySet):
|
|||
table_name = qn(self.model._meta.db_table)
|
||||
field_name = qn(self._field.column)
|
||||
|
||||
if backend.allows_group_by_ordinal:
|
||||
if connection.features.allows_group_by_ordinal:
|
||||
group_by = '1'
|
||||
else:
|
||||
group_by = connection.ops.date_trunc_sql(self._kind, '%s.%s' % (table_name, field_name))
|
||||
|
@ -663,7 +663,7 @@ class DateQuerySet(QuerySet):
|
|||
cursor.execute(sql, params)
|
||||
|
||||
has_resolve_columns = hasattr(self, 'resolve_columns')
|
||||
needs_datetime_string_cast = backend.needs_datetime_string_cast
|
||||
needs_datetime_string_cast = connection.features.needs_datetime_string_cast
|
||||
dates = []
|
||||
# It would be better to use self._field here instead of DateTimeField(),
|
||||
# but in Oracle that will result in a list of datetime.date instead of
|
||||
|
@ -796,7 +796,7 @@ def get_where_clause(lookup_type, table_prefix, field_name, value, db_type):
|
|||
else:
|
||||
field_cast_sql = '%s%s'
|
||||
field_sql = field_cast_sql % (table_prefix, field_name)
|
||||
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops:
|
||||
if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and connection.features.needs_upper_for_iops:
|
||||
format = 'UPPER(%s) %s'
|
||||
else:
|
||||
format = '%s %s'
|
||||
|
|
Loading…
Reference in New Issue