Django

Code

Changeset 5013

Show
Ignore:
Timestamp:
04/16/07 17:31:55 (2 years ago)
Author:
bouldersprinters
Message:

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.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/boulder-oracle-sprint/django/db/backends/oracle/base.py

    r5002 r5013  
    297297            # 2. convert NCLOBs 
    298298 
    299             def resolve_cols(row): 
    300                 for field in row: 
    301                     value = field 
    302                     if isinstance(field, Database.LOB): 
    303                         value = field.read() 
    304                     # cx_Oracle always returns datetime.datetime objects for 
    305                     # DATE and TIMESTAMP columns, but Django wants to see a 
    306                     # python datetime.date, .time, or .datetime. 
    307                     # As a workaround, we cast to date if all the time-related 
    308                     # fields are 0, or to time if the date is 1/1/1900. 
    309                     # A better fix would involve either patching cx_Oracle 
    310                     # or checking the Model here, neither of which is good. 
    311                     elif isinstance(field, datetime.datetime): 
    312                         if field.hour == field.minute == field.second == field.microsecond == 0: 
    313                             value = field.date() 
    314                         elif field.year == 1900 and field.month == field.day == 1: 
    315                             value = field.time() 
    316                     # In Python 2.3, the cx_Oracle driver returns its own 
    317                     # Timestamp object that we must convert to a datetime class. 
    318                     elif isinstance(field, Database.Timestamp): 
    319                         if field.hour == field.minute == field.second == field.fsecond == 0: 
    320                             value = datetime.date(field.year, field.month, field.day) 
    321                         elif field.year == 1900 and field.month == field.day == 1: 
    322                             value = datetime.time(field.hour, field.minute, field.second, field.fsecond) 
    323                         else: 
    324                             value = datetime.datetime(field.year, field.month, field.day, field.hour, 
    325                                                       field.minute, field.second, field.fsecond) 
    326                     # Since Oracle won't distinguish between NULL and an empty 
    327                     # string (''), we store empty strings as a space.  Here is 
    328                     # where we undo that treachery. 
    329                     if value == ' ': 
    330                         value = '' 
    331                     yield value 
    332  
    333299            while 1: 
    334300                rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     
    336302                    raise StopIteration 
    337303                for row in rows: 
    338                     row = list(resolve_cols(row)
     304                    row = self.resolve_columns(row
    339305                    if fill_cache: 
    340306                        obj, index_end = get_cached_row(klass=self.model, row=row, 
     
    480446                return select, " ".join(sql), params 
    481447 
     448        def resolve_columns(self, row, fields=()): 
     449            from django.db.models.fields import DateField, DateTimeField, TimeField 
     450            values = [] 
     451            for value, field in map(None, row, fields): 
     452                if isinstance(value, Database.LOB): 
     453                    value = value.read() 
     454                # Since Oracle won't distinguish between NULL and an empty 
     455                # string (''), we store empty strings as a space.  Here is 
     456                # where we undo that treachery. 
     457                if value == ' ': 
     458                    value = '' 
     459                # cx_Oracle always returns datetime.datetime objects for 
     460                # DATE and TIMESTAMP columns, but Django wants to see a 
     461                # python datetime.date, .time, or .datetime.  We use the type 
     462                # of the Field to determine which to cast to, but it's not 
     463                # always available. 
     464                # As a workaround, we cast to date if all the time-related 
     465                # values are 0, or to time if the date is 1/1/1900. 
     466                # This could be cleaned a bit by adding a method to the Field 
     467                # classes to normalize values from the database (the to_python 
     468                # method is used for validation and isn't what we want here). 
     469                elif isinstance(value, Database.Timestamp): 
     470                    # In Python 2.3, the cx_Oracle driver returns its own 
     471                    # Timestamp object that we must convert to a datetime class. 
     472                    if not isinstance(value, datetime.datetime): 
     473                        value = datetime.datetime(value.year, value.month, value.day, value.hour, 
     474                                                  value.minute, value.second, value.fsecond) 
     475                    if isinstance(field, DateTimeField): 
     476                        pass  # DateTimeField subclasses DateField so must be checked first. 
     477                    elif isinstance(field, DateField): 
     478                        value = value.date() 
     479                    elif isinstance(field, TimeField): 
     480                        value = value.time() 
     481                    elif value.hour == value.minute == value.second == value.microsecond == 0: 
     482                        value = value.date() 
     483                    elif value.year == 1900 and value.month == value.day == 1: 
     484                        value = value.time() 
     485                values.append(value) 
     486            return values 
     487 
    482488    return OracleQuerySet 
    483489 
  • django/branches/boulder-oracle-sprint/django/db/models/query.py

    r4990 r5013  
    185185        fill_cache = self._select_related 
    186186        index_end = len(self.model._meta.fields) 
     187        has_resolve_columns = hasattr(self, 'resolve_columns') 
    187188        while 1: 
    188189            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     
    190191                raise StopIteration 
    191192            for row in rows: 
     193                if has_resolve_columns: 
     194                    row = self.resolve_columns(row) 
    192195                if fill_cache: 
    193196                    obj, index_end = get_cached_row(klass=self.model, row=row, 
     
    574577        # self._fields is a list of field names to fetch. 
    575578        if self._fields: 
    576             columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 
    577             field_names = self._fields 
     579            fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 
    578580        else: # Default to all fields. 
    579             columns = [f.column for f in self.model._meta.fields] 
    580             field_names = [f.attname for f in self.model._meta.fields] 
     581            fields = self.model._meta.fields 
     582        columns = [f.column for f in fields] 
     583        field_names = [f.attname for f in fields] 
    581584 
    582585        select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 
    583586        cursor = connection.cursor() 
    584587        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     588 
     589        has_resolve_columns = hasattr(self, 'resolve_columns') 
    585590        while 1: 
    586591            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     
    588593                raise StopIteration 
    589594            for row in rows: 
     595                if has_resolve_columns: 
     596                    row = self.resolve_columns(row, fields) 
    590597                yield dict(zip(field_names, row)) 
    591598 
     
    598605    def iterator(self): 
    599606        from django.db.backends.util import typecast_timestamp 
     607        from django.db.models.fields import DateTimeField 
    600608        self._order_by = () # Clear this because it'll mess things up otherwise. 
    601609        if self._field.null: 
     
    621629        cursor = connection.cursor() 
    622630        cursor.execute(sql, params) 
    623         if backend.needs_datetime_string_cast: 
    624             return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] 
    625         else: 
    626             return [row[0] for row in cursor.fetchall()] 
     631 
     632        has_resolve_columns = hasattr(self, 'resolve_columns') 
     633        needs_datetime_string_cast = backend.needs_datetime_string_cast 
     634        dates = [] 
     635        # It would be better to use self._field here instead of DateTimeField(), 
     636        # but in Oracle that will result in a list of datetime.date instead of 
     637        # datetime.datetime. 
     638        fields = [DateTimeField()] 
     639        while 1: 
     640            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     641            if not rows: 
     642                return dates 
     643            for row in rows: 
     644                date = row[0] 
     645                if has_resolve_columns: 
     646                    date = self.resolve_columns([date], fields)[0] 
     647                elif needs_datetime_string_cast: 
     648                    date = typecast_timestamp(str(date)) 
     649                dates.append(date) 
    627650 
    628651    def _clone(self, klass=None, **kwargs):