mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
boulder-oracle-sprint: Made the iterator methods of _QuerySet, DateQuerySet and ValueQuerySet each call a resolve_columns method on each row, which may be implemented in the backend to do custom processing on the result rows. Oracle uses this to resolve lob objects and to correctly cast datetime objects.
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5013 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
531235c3bf
commit
9699399eee
@ -296,46 +296,12 @@ def get_query_set_class(DefaultQuerySet):
|
|||||||
# 1. retrieve each row in turn
|
# 1. retrieve each row in turn
|
||||||
# 2. convert NCLOBs
|
# 2. convert NCLOBs
|
||||||
|
|
||||||
def resolve_cols(row):
|
|
||||||
for field in row:
|
|
||||||
value = field
|
|
||||||
if isinstance(field, Database.LOB):
|
|
||||||
value = field.read()
|
|
||||||
# cx_Oracle always returns datetime.datetime objects for
|
|
||||||
# DATE and TIMESTAMP columns, but Django wants to see a
|
|
||||||
# python datetime.date, .time, or .datetime.
|
|
||||||
# As a workaround, we cast to date if all the time-related
|
|
||||||
# fields are 0, or to time if the date is 1/1/1900.
|
|
||||||
# A better fix would involve either patching cx_Oracle
|
|
||||||
# or checking the Model here, neither of which is good.
|
|
||||||
elif isinstance(field, datetime.datetime):
|
|
||||||
if field.hour == field.minute == field.second == field.microsecond == 0:
|
|
||||||
value = field.date()
|
|
||||||
elif field.year == 1900 and field.month == field.day == 1:
|
|
||||||
value = field.time()
|
|
||||||
# In Python 2.3, the cx_Oracle driver returns its own
|
|
||||||
# Timestamp object that we must convert to a datetime class.
|
|
||||||
elif isinstance(field, Database.Timestamp):
|
|
||||||
if field.hour == field.minute == field.second == field.fsecond == 0:
|
|
||||||
value = datetime.date(field.year, field.month, field.day)
|
|
||||||
elif field.year == 1900 and field.month == field.day == 1:
|
|
||||||
value = datetime.time(field.hour, field.minute, field.second, field.fsecond)
|
|
||||||
else:
|
|
||||||
value = datetime.datetime(field.year, field.month, field.day, field.hour,
|
|
||||||
field.minute, field.second, field.fsecond)
|
|
||||||
# Since Oracle won't distinguish between NULL and an empty
|
|
||||||
# string (''), we store empty strings as a space. Here is
|
|
||||||
# where we undo that treachery.
|
|
||||||
if value == ' ':
|
|
||||||
value = ''
|
|
||||||
yield value
|
|
||||||
|
|
||||||
while 1:
|
while 1:
|
||||||
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||||
if not rows:
|
if not rows:
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
for row in rows:
|
for row in rows:
|
||||||
row = list(resolve_cols(row))
|
row = self.resolve_columns(row)
|
||||||
if fill_cache:
|
if fill_cache:
|
||||||
obj, index_end = get_cached_row(klass=self.model, row=row,
|
obj, index_end = get_cached_row(klass=self.model, row=row,
|
||||||
index_start=0, max_depth=self._max_related_depth)
|
index_start=0, max_depth=self._max_related_depth)
|
||||||
@ -479,6 +445,46 @@ def get_query_set_class(DefaultQuerySet):
|
|||||||
else:
|
else:
|
||||||
return select, " ".join(sql), params
|
return select, " ".join(sql), params
|
||||||
|
|
||||||
|
def resolve_columns(self, row, fields=()):
|
||||||
|
from django.db.models.fields import DateField, DateTimeField, TimeField
|
||||||
|
values = []
|
||||||
|
for value, field in map(None, row, fields):
|
||||||
|
if isinstance(value, Database.LOB):
|
||||||
|
value = value.read()
|
||||||
|
# Since Oracle won't distinguish between NULL and an empty
|
||||||
|
# string (''), we store empty strings as a space. Here is
|
||||||
|
# where we undo that treachery.
|
||||||
|
if value == ' ':
|
||||||
|
value = ''
|
||||||
|
# cx_Oracle always returns datetime.datetime objects for
|
||||||
|
# DATE and TIMESTAMP columns, but Django wants to see a
|
||||||
|
# python datetime.date, .time, or .datetime. We use the type
|
||||||
|
# of the Field to determine which to cast to, but it's not
|
||||||
|
# always available.
|
||||||
|
# As a workaround, we cast to date if all the time-related
|
||||||
|
# values are 0, or to time if the date is 1/1/1900.
|
||||||
|
# This could be cleaned a bit by adding a method to the Field
|
||||||
|
# classes to normalize values from the database (the to_python
|
||||||
|
# method is used for validation and isn't what we want here).
|
||||||
|
elif isinstance(value, Database.Timestamp):
|
||||||
|
# In Python 2.3, the cx_Oracle driver returns its own
|
||||||
|
# Timestamp object that we must convert to a datetime class.
|
||||||
|
if not isinstance(value, datetime.datetime):
|
||||||
|
value = datetime.datetime(value.year, value.month, value.day, value.hour,
|
||||||
|
value.minute, value.second, value.fsecond)
|
||||||
|
if isinstance(field, DateTimeField):
|
||||||
|
pass # DateTimeField subclasses DateField so must be checked first.
|
||||||
|
elif isinstance(field, DateField):
|
||||||
|
value = value.date()
|
||||||
|
elif isinstance(field, TimeField):
|
||||||
|
value = value.time()
|
||||||
|
elif value.hour == value.minute == value.second == value.microsecond == 0:
|
||||||
|
value = value.date()
|
||||||
|
elif value.year == 1900 and value.month == value.day == 1:
|
||||||
|
value = value.time()
|
||||||
|
values.append(value)
|
||||||
|
return values
|
||||||
|
|
||||||
return OracleQuerySet
|
return OracleQuerySet
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,11 +184,14 @@ class _QuerySet(object):
|
|||||||
|
|
||||||
fill_cache = self._select_related
|
fill_cache = self._select_related
|
||||||
index_end = len(self.model._meta.fields)
|
index_end = len(self.model._meta.fields)
|
||||||
|
has_resolve_columns = hasattr(self, 'resolve_columns')
|
||||||
while 1:
|
while 1:
|
||||||
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||||
if not rows:
|
if not rows:
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
for row in rows:
|
for row in rows:
|
||||||
|
if has_resolve_columns:
|
||||||
|
row = self.resolve_columns(row)
|
||||||
if fill_cache:
|
if fill_cache:
|
||||||
obj, index_end = get_cached_row(klass=self.model, row=row,
|
obj, index_end = get_cached_row(klass=self.model, row=row,
|
||||||
index_start=0, max_depth=self._max_related_depth)
|
index_start=0, max_depth=self._max_related_depth)
|
||||||
@ -573,20 +576,24 @@ class ValuesQuerySet(QuerySet):
|
|||||||
|
|
||||||
# self._fields is a list of field names to fetch.
|
# self._fields is a list of field names to fetch.
|
||||||
if self._fields:
|
if self._fields:
|
||||||
columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]
|
fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields]
|
||||||
field_names = self._fields
|
|
||||||
else: # Default to all fields.
|
else: # Default to all fields.
|
||||||
columns = [f.column for f in self.model._meta.fields]
|
fields = self.model._meta.fields
|
||||||
field_names = [f.attname for f in self.model._meta.fields]
|
columns = [f.column for f in fields]
|
||||||
|
field_names = [f.attname for f in fields]
|
||||||
|
|
||||||
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
|
||||||
|
|
||||||
|
has_resolve_columns = hasattr(self, 'resolve_columns')
|
||||||
while 1:
|
while 1:
|
||||||
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||||
if not rows:
|
if not rows:
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
for row in rows:
|
for row in rows:
|
||||||
|
if has_resolve_columns:
|
||||||
|
row = self.resolve_columns(row, fields)
|
||||||
yield dict(zip(field_names, row))
|
yield dict(zip(field_names, row))
|
||||||
|
|
||||||
def _clone(self, klass=None, **kwargs):
|
def _clone(self, klass=None, **kwargs):
|
||||||
@ -597,6 +604,7 @@ class ValuesQuerySet(QuerySet):
|
|||||||
class DateQuerySet(QuerySet):
|
class DateQuerySet(QuerySet):
|
||||||
def iterator(self):
|
def iterator(self):
|
||||||
from django.db.backends.util import typecast_timestamp
|
from django.db.backends.util import typecast_timestamp
|
||||||
|
from django.db.models.fields import DateTimeField
|
||||||
self._order_by = () # Clear this because it'll mess things up otherwise.
|
self._order_by = () # Clear this because it'll mess things up otherwise.
|
||||||
if self._field.null:
|
if self._field.null:
|
||||||
self._where.append('%s.%s IS NOT NULL' % \
|
self._where.append('%s.%s IS NOT NULL' % \
|
||||||
@ -620,10 +628,25 @@ class DateQuerySet(QuerySet):
|
|||||||
backend.quote_name(self._field.column))), sql, group_by, self._order)
|
backend.quote_name(self._field.column))), sql, group_by, self._order)
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute(sql, params)
|
cursor.execute(sql, params)
|
||||||
if backend.needs_datetime_string_cast:
|
|
||||||
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
|
has_resolve_columns = hasattr(self, 'resolve_columns')
|
||||||
else:
|
needs_datetime_string_cast = backend.needs_datetime_string_cast
|
||||||
return [row[0] for row in cursor.fetchall()]
|
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
|
||||||
|
# datetime.datetime.
|
||||||
|
fields = [DateTimeField()]
|
||||||
|
while 1:
|
||||||
|
rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
|
||||||
|
if not rows:
|
||||||
|
return dates
|
||||||
|
for row in rows:
|
||||||
|
date = row[0]
|
||||||
|
if has_resolve_columns:
|
||||||
|
date = self.resolve_columns([date], fields)[0]
|
||||||
|
elif needs_datetime_string_cast:
|
||||||
|
date = typecast_timestamp(str(date))
|
||||||
|
dates.append(date)
|
||||||
|
|
||||||
def _clone(self, klass=None, **kwargs):
|
def _clone(self, klass=None, **kwargs):
|
||||||
c = super(DateQuerySet, self)._clone(klass, **kwargs)
|
c = super(DateQuerySet, self)._clone(klass, **kwargs)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user