summaryrefslogtreecommitdiff
path: root/django/db/models
diff options
context:
space:
mode:
authorJeremy Dunck <jdunck@gmail.com>2007-06-25 19:33:37 +0000
committerJeremy Dunck <jdunck@gmail.com>2007-06-25 19:33:37 +0000
commitfc779fe55aec84994e7e761c743716ba03484bcc (patch)
treed139f5ce44133e630c7bb1b965baa3120ba23c99 /django/db/models
parentb0a56a9919d2304fa08b71373b53fdfb5ca72de9 (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.py19
-rw-r--r--django/db/models/fields/__init__.py32
-rw-r--r--django/db/models/fields/related.py12
-rw-r--r--django/db/models/options.py11
-rw-r--r--django/db/models/query.py89
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':