diff options
| author | Jeremy Dunck <jdunck@gmail.com> | 2007-06-25 19:33:37 +0000 |
|---|---|---|
| committer | Jeremy Dunck <jdunck@gmail.com> | 2007-06-25 19:33:37 +0000 |
| commit | fc779fe55aec84994e7e761c743716ba03484bcc (patch) | |
| tree | d139f5ce44133e630c7bb1b965baa3120ba23c99 /django/db/models | |
| parent | b0a56a9919d2304fa08b71373b53fdfb5ca72de9 (diff) | |
gis: Merged revisions 5491-5539 via svnmerge from
http://code.djangoproject.com/svn/django/trunk
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5540 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/db/models')
| -rw-r--r-- | django/db/models/base.py | 19 | ||||
| -rw-r--r-- | django/db/models/fields/__init__.py | 32 | ||||
| -rw-r--r-- | django/db/models/fields/related.py | 12 | ||||
| -rw-r--r-- | django/db/models/options.py | 11 | ||||
| -rw-r--r-- | django/db/models/query.py | 89 |
5 files changed, 119 insertions, 44 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py index e02d6de861..b1c4a43628 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -96,9 +96,9 @@ class Model(object): def __init__(self, *args, **kwargs): dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) - + # There is a rather weird disparity here; if kwargs, it's set, then args - # overrides it. It should be one or the other; don't duplicate the work + # overrides it. It should be one or the other; don't duplicate the work # The reason for the kwargs check is that standard iterator passes in by # args, and nstantiation for iteration is 33% faster. args_len = len(args) @@ -122,10 +122,10 @@ class Model(object): # Maintain compatibility with existing calls. if isinstance(field.rel, ManyToOneRel): kwargs.pop(field.attname, None) - + # Now we're left with the unprocessed fields that *must* come from # keywords, or default. - + for field in fields_iter: if kwargs: if isinstance(field.rel, ManyToOneRel): @@ -147,7 +147,7 @@ class Model(object): try: val = getattr(rel_obj, field.rel.get_related_field().attname) except AttributeError: - raise TypeError("Invalid value: %r should be a %s instance, not a %s" % + raise TypeError("Invalid value: %r should be a %s instance, not a %s" % (field.name, field.rel.to, type(rel_obj))) else: val = kwargs.pop(field.attname, field.get_default()) @@ -210,17 +210,18 @@ class Model(object): record_exists = True if pk_set: # Determine whether a record with the primary key already exists. - cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \ - (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) + cursor.execute("SELECT COUNT(*) FROM %s WHERE %s=%%s" % \ + (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), + self._meta.pk.get_db_prep_lookup('exact', pk_val)) # If it does already exist, do an UPDATE. - if cursor.fetchone(): + if cursor.fetchone()[0] > 0: db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks] if db_values: cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ (backend.quote_name(self._meta.db_table), ','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]), backend.quote_name(self._meta.pk.column)), - db_values + [pk_val]) + db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val)) else: record_exists = False if not pk_set or not record_exists: diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 136ce31b8b..016e26099b 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -74,12 +74,16 @@ class Field(object): core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, prepopulate_from=None, unique_for_date=None, unique_for_month=None, unique_for_year=None, validator_list=None, choices=None, radio_admin=None, - help_text='', db_column=None): + help_text='', db_column=None, db_tablespace=None): self.name = name self.verbose_name = verbose_name self.primary_key = primary_key self.maxlength, self.unique = maxlength, unique self.blank, self.null = blank, null + # Oracle treats the empty string ('') as null, so coerce the null + # option whenever '' is a possible value. + if self.empty_strings_allowed and settings.DATABASE_ENGINE == 'oracle': + self.null = True self.core, self.rel, self.default = core, rel, default self.editable = editable self.serialize = serialize @@ -91,6 +95,7 @@ class Field(object): self.radio_admin = radio_admin self.help_text = help_text self.db_column = db_column + self.db_tablespace = db_tablespace # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. self.db_index = db_index @@ -201,7 +206,7 @@ class Field(object): if callable(self.default): return self.default() return self.default - if not self.empty_strings_allowed or self.null: + if not self.empty_strings_allowed or (self.null and settings.DATABASE_ENGINE != 'oracle'): return None return "" @@ -806,6 +811,7 @@ class IPAddressField(Field): validators.isValidIPAddress4(field_data, None) class NullBooleanField(Field): + empty_strings_allowed = False def __init__(self, *args, **kwargs): kwargs['null'] = True Field.__init__(self, *args, **kwargs) @@ -875,10 +881,18 @@ class TimeField(Field): Field.__init__(self, verbose_name, name, **kwargs) def get_db_prep_lookup(self, lookup_type, value): + if settings.DATABASE_ENGINE == 'oracle': + # Oracle requires a date in order to parse. + def prep(value): + if isinstance(value, datetime.time): + value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) + return str(value) + else: + prep = str if lookup_type == 'range': - value = [str(v) for v in value] + value = [prep(v) for v in value] else: - value = str(value) + value = prep(value) return Field.get_db_prep_lookup(self, lookup_type, value) def pre_save(self, model_instance, add): @@ -896,7 +910,15 @@ class TimeField(Field): # doesn't support microseconds. if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): value = value.replace(microsecond=0) - value = str(value) + if settings.DATABASE_ENGINE == 'oracle': + # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. + if isinstance(value, datetime.time): + value = datetime.datetime(1900, 1, 1, value.hour, value.minute, + value.second, value.microsecond) + elif isinstance(value, basestring): + value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) + else: + value = str(value) return Field.get_db_prep_save(self, value) def get_manipulator_field_objs(self): diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 42eec6b4f0..60503ce14f 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -10,9 +10,10 @@ from django import oldforms from django import newforms as forms from django.dispatch import dispatcher -# For Python 2.3 -if not hasattr(__builtins__, 'set'): - from sets import Set as set +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback # Values for Relation.edit_inline. TABULAR, STACKED = 1, 2 @@ -335,10 +336,7 @@ def create_many_related_manager(superclass): (target_col_name, self.join_table, source_col_name, target_col_name, ",".join(['%s'] * len(new_ids))), [self._pk_val] + list(new_ids)) - if cursor.rowcount is not None and cursor.rowcount != 0: - existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) - else: - existing_ids = set() + existing_ids = set([row[0] for row in cursor.fetchall()]) # Add the ones that aren't there already for obj_id in (new_ids - existing_ids): diff --git a/django/db/models/options.py b/django/db/models/options.py index dd6c586ddd..93627f7b72 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -13,7 +13,7 @@ get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]| DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 'unique_together', 'permissions', 'get_latest_by', - 'order_with_respect_to', 'app_label') + 'order_with_respect_to', 'app_label', 'db_tablespace') class Options(object): def __init__(self, meta): @@ -27,6 +27,7 @@ class Options(object): self.object_name, self.app_label = None, None self.get_latest_by = None self.order_with_respect_to = None + self.db_tablespace = None self.admin = None self.meta = meta self.pk = None @@ -59,6 +60,8 @@ class Options(object): del self.meta def _prepare(self, model): + from django.db import backend + from django.db.backends.util import truncate_name if self.order_with_respect_to: self.order_with_respect_to = self.get_field(self.order_with_respect_to) self.ordering = ('_order',) @@ -73,6 +76,8 @@ class Options(object): # If the db_table wasn't provided, use the app_label + module_name. if not self.db_table: self.db_table = "%s_%s" % (self.app_label, self.module_name) + self.db_table = truncate_name(self.db_table, + backend.get_max_name_length()) def add_field(self, field): # Insert the given field in the order in which it was created, using @@ -88,10 +93,10 @@ class Options(object): def __repr__(self): return '<Options for %s>' % self.object_name - + def __str__(self): return "%s.%s" % (self.app_label, self.module_name) - + def get_field(self, name, many_to_many=True): "Returns the requested field by name. Raises FieldDoesNotExist on error." to_search = many_to_many and (self.fields + self.many_to_many) or self.fields diff --git a/django/db/models/query.py b/django/db/models/query.py index a6e702be18..24d701b10d 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -4,12 +4,14 @@ from django.db.models import signals, loading from django.dispatch import dispatcher from django.utils.datastructures import SortedDict from django.contrib.contenttypes import generic +import datetime import operator import re -# For Python 2.3 -if not hasattr(__builtins__, 'set'): - from sets import Set as set +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback # The string constant used to separate query parts LOOKUP_SEPARATOR = '__' @@ -77,7 +79,7 @@ def quote_only_if_word(word): else: return backend.quote_name(word) -class QuerySet(object): +class _QuerySet(object): "Represents a lazy database lookup for a set of objects" def __init__(self, model=None): self.model = model @@ -181,13 +183,18 @@ class QuerySet(object): cursor = connection.cursor() cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) + fill_cache = self._select_related - index_end = len(self.model._meta.fields) + fields = self.model._meta.fields + index_end = len(fields) + has_resolve_columns = hasattr(self, 'resolve_columns') while 1: rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) if not rows: raise StopIteration for row in rows: + if has_resolve_columns: + row = self.resolve_columns(row, fields) if fill_cache: obj, index_end = get_cached_row(klass=self.model, row=row, index_start=0, max_depth=self._max_related_depth) @@ -551,6 +558,12 @@ class QuerySet(object): return select, " ".join(sql), params +# Use the backend's QuerySet class if it defines one, otherwise use _QuerySet. +if hasattr(backend, 'get_query_set_class'): + QuerySet = backend.get_query_set_class(_QuerySet) +else: + QuerySet = _QuerySet + class ValuesQuerySet(QuerySet): def __init__(self, *args, **kwargs): super(ValuesQuerySet, self).__init__(*args, **kwargs) @@ -565,35 +578,38 @@ class ValuesQuerySet(QuerySet): # self._fields is a list of field names to fetch. if self._fields: - #columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] if not self._select: - 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] else: - columns = [] + fields = [] for f in self._fields: if f in [field.name for field in self.model._meta.fields]: - columns.append( self.model._meta.get_field(f, many_to_many=False).column ) + fields.append(self.model._meta.get_field(f, many_to_many=False)) elif not self._select.has_key( f ): raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f ) field_names = self._fields else: # Default to all fields. - columns = [f.column for f in self.model._meta.fields] - field_names = [f.attname for f in self.model._meta.fields] + fields = self.model._meta.fields + field_names = [f.attname for f in fields] + columns = [f.column for f in fields] select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] - # Add any additional SELECTs. if self._select: select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) cursor = connection.cursor() cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) + + has_resolve_columns = hasattr(self, 'resolve_columns') while 1: rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) if not rows: raise StopIteration for row in rows: + if has_resolve_columns: + row = self.resolve_columns(row, fields) yield dict(zip(field_names, row)) def _clone(self, klass=None, **kwargs): @@ -604,25 +620,49 @@ class ValuesQuerySet(QuerySet): class DateQuerySet(QuerySet): def iterator(self): 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. if self._field.null: self._where.append('%s.%s IS NOT NULL' % \ (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) - try: select, sql, params = self._get_sql_clause() except EmptyResultSet: raise StopIteration - sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ + table_name = backend.quote_name(self.model._meta.db_table) + field_name = backend.quote_name(self._field.column) + + if backend.allows_group_by_ordinal: + group_by = '1' + else: + group_by = backend.get_date_trunc_sql(self._kind, + '%s.%s' % (table_name, field_name)) + + sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), - backend.quote_name(self._field.column))), sql, self._order) + backend.quote_name(self._field.column))), sql, group_by, self._order) cursor = connection.cursor() cursor.execute(sql, params) - # We have to manually run typecast_timestamp(str()) on the results, because - # MySQL doesn't automatically cast the result of date functions as datetime - # objects -- MySQL returns the values as strings, instead. - return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] + + has_resolve_columns = hasattr(self, 'resolve_columns') + needs_datetime_string_cast = backend.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 + # 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): c = super(DateQuerySet, self)._clone(klass, **kwargs) @@ -730,8 +770,17 @@ def get_where_clause(lookup_type, table_prefix, field_name, value): if table_prefix.endswith('.'): table_prefix = backend.quote_name(table_prefix[:-1])+'.' field_name = backend.quote_name(field_name) + if type(value) == datetime.datetime and backend.get_datetime_cast_sql(): + cast_sql = backend.get_datetime_cast_sql() + else: + cast_sql = '%s' + if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: + format = 'UPPER(%s%s) %s' + else: + format = '%s%s %s' try: - return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) + return format % (table_prefix, field_name, + backend.OPERATOR_MAPPING[lookup_type] % cast_sql) except KeyError: pass if lookup_type == 'in': |
