Fixed #7109 -- Quote certain values before passing them for substitution in
Field.db_type(). This fixes a problem with using reserved words for field names in Oracle. Only affects Oracle at the moment, but the same changes could easily be used by other backends if they are required (requires changing creation.py, only). This commit also reverts [7501] so that if the fix doesn't work, it will show up in the tests (and if it does work, the tests will prevent us from breaking it again). git-svn-id: http://code.djangoproject.com/svn/django/trunk@7743 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
002dbd16b4
commit
915001ad0b
|
@ -5,9 +5,13 @@ from django.core import management
|
||||||
# types, as strings. Column-type strings can contain format strings; they'll
|
# types, as strings. Column-type strings can contain format strings; they'll
|
||||||
# be interpolated against the values of Field.__dict__ before being output.
|
# be interpolated against the values of Field.__dict__ before being output.
|
||||||
# If a column type is set to None, it won't be included in the output.
|
# If a column type is set to None, it won't be included in the output.
|
||||||
|
#
|
||||||
|
# Any format strings starting with "qn_" are quoted before being used in the
|
||||||
|
# output (the "qn_" prefix is stripped before the lookup is performed.
|
||||||
|
|
||||||
DATA_TYPES = {
|
DATA_TYPES = {
|
||||||
'AutoField': 'NUMBER(11)',
|
'AutoField': 'NUMBER(11)',
|
||||||
'BooleanField': 'NUMBER(1) CHECK (%(column)s IN (0,1))',
|
'BooleanField': 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
|
||||||
'CharField': 'NVARCHAR2(%(max_length)s)',
|
'CharField': 'NVARCHAR2(%(max_length)s)',
|
||||||
'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)',
|
'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)',
|
||||||
'DateField': 'DATE',
|
'DateField': 'DATE',
|
||||||
|
@ -19,11 +23,11 @@ DATA_TYPES = {
|
||||||
'ImageField': 'NVARCHAR2(%(max_length)s)',
|
'ImageField': 'NVARCHAR2(%(max_length)s)',
|
||||||
'IntegerField': 'NUMBER(11)',
|
'IntegerField': 'NUMBER(11)',
|
||||||
'IPAddressField': 'VARCHAR2(15)',
|
'IPAddressField': 'VARCHAR2(15)',
|
||||||
'NullBooleanField': 'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))',
|
'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(column)s IS NULL))',
|
||||||
'OneToOneField': 'NUMBER(11)',
|
'OneToOneField': 'NUMBER(11)',
|
||||||
'PhoneNumberField': 'VARCHAR2(20)',
|
'PhoneNumberField': 'VARCHAR2(20)',
|
||||||
'PositiveIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)',
|
'PositiveIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)',
|
||||||
'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)',
|
'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)',
|
||||||
'SlugField': 'NVARCHAR2(50)',
|
'SlugField': 'NVARCHAR2(50)',
|
||||||
'SmallIntegerField': 'NUMBER(11)',
|
'SmallIntegerField': 'NUMBER(11)',
|
||||||
'TextField': 'NCLOB',
|
'TextField': 'NCLOB',
|
||||||
|
|
|
@ -16,6 +16,7 @@ from django.core import validators
|
||||||
from django import oldforms
|
from django import oldforms
|
||||||
from django import newforms as forms
|
from django import newforms as forms
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.utils.datastructures import DictWrapper
|
||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
from django.utils.itercompat import tee
|
from django.utils.itercompat import tee
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
|
@ -161,8 +162,9 @@ class Field(object):
|
||||||
# mapped to one of the built-in Django field types. In this case, you
|
# mapped to one of the built-in Django field types. In this case, you
|
||||||
# can implement db_type() instead of get_internal_type() to specify
|
# can implement db_type() instead of get_internal_type() to specify
|
||||||
# exactly which wacky database column type you want to use.
|
# exactly which wacky database column type you want to use.
|
||||||
|
data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
|
||||||
try:
|
try:
|
||||||
return get_creation_module().DATA_TYPES[self.get_internal_type()] % self.__dict__
|
return get_creation_module().DATA_TYPES[self.get_internal_type()] % data
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -343,3 +343,34 @@ class FileDict(dict):
|
||||||
d = dict(self, content='<omitted>')
|
d = dict(self, content='<omitted>')
|
||||||
return dict.__repr__(d)
|
return dict.__repr__(d)
|
||||||
return dict.__repr__(self)
|
return dict.__repr__(self)
|
||||||
|
|
||||||
|
class DictWrapper(dict):
|
||||||
|
"""
|
||||||
|
Wraps accesses to a dictionary so that certain values (those starting with
|
||||||
|
the specified prefix) are passed through a function before being returned.
|
||||||
|
The prefix is removed before looking up the real value.
|
||||||
|
|
||||||
|
Used by the SQL construction code to ensure that values are correctly
|
||||||
|
quoted before being used.
|
||||||
|
"""
|
||||||
|
def __init__(self, data, func, prefix):
|
||||||
|
super(DictWrapper, self).__init__(data)
|
||||||
|
self.func = func
|
||||||
|
self.prefix = prefix
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
"""
|
||||||
|
Retrieves the real value after stripping the prefix string (if
|
||||||
|
present). If the prefix is present, pass the value through self.func
|
||||||
|
before returning, otherwise return the raw value.
|
||||||
|
"""
|
||||||
|
if key.startswith(self.prefix):
|
||||||
|
use_func = True
|
||||||
|
key = key[len(self.prefix):]
|
||||||
|
else:
|
||||||
|
use_func = False
|
||||||
|
value = super(DictWrapper, self).__getitem__(key)
|
||||||
|
if use_func:
|
||||||
|
return self.func(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
|
@ -122,12 +122,12 @@ class LoopZ(models.Model):
|
||||||
class CustomManager(models.Manager):
|
class CustomManager(models.Manager):
|
||||||
def get_query_set(self):
|
def get_query_set(self):
|
||||||
qs = super(CustomManager, self).get_query_set()
|
qs = super(CustomManager, self).get_query_set()
|
||||||
return qs.filter(is_public=True, tag__name='t1')
|
return qs.filter(public=True, tag__name='t1')
|
||||||
|
|
||||||
class ManagedModel(models.Model):
|
class ManagedModel(models.Model):
|
||||||
data = models.CharField(max_length=10)
|
data = models.CharField(max_length=10)
|
||||||
tag = models.ForeignKey(Tag)
|
tag = models.ForeignKey(Tag)
|
||||||
is_public = models.BooleanField(default=True)
|
public = models.BooleanField(default=True)
|
||||||
|
|
||||||
objects = CustomManager()
|
objects = CustomManager()
|
||||||
normal_manager = models.Manager()
|
normal_manager = models.Manager()
|
||||||
|
@ -730,7 +730,7 @@ More twisted cases, involving nested negations.
|
||||||
Bug #7095
|
Bug #7095
|
||||||
Updates that are filtered on the model being updated are somewhat tricky to get
|
Updates that are filtered on the model being updated are somewhat tricky to get
|
||||||
in MySQL. This exercises that case.
|
in MySQL. This exercises that case.
|
||||||
>>> mm = ManagedModel.objects.create(data='mm1', tag=t1, is_public=True)
|
>>> mm = ManagedModel.objects.create(data='mm1', tag=t1, public=True)
|
||||||
>>> ManagedModel.objects.update(data='mm')
|
>>> ManagedModel.objects.update(data='mm')
|
||||||
|
|
||||||
A values() or values_list() query across joined models must use outer joins
|
A values() or values_list() query across joined models must use outer joins
|
||||||
|
|
Loading…
Reference in New Issue