summaryrefslogtreecommitdiff
path: root/django/db/models
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2007-07-01 01:17:51 +0000
committerAdrian Holovaty <adrian@holovaty.com>2007-07-01 01:17:51 +0000
commit6f4e933fcc78da11ec43214efd3472192030eced (patch)
treed92140118d3afa00bcd909f475dd1a694c15c08f /django/db/models
parent7a981380def046f753ff70a7c25efcd781ff6ac6 (diff)
newforms-admin: Merged to [5571]
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5572 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__.py36
-rw-r--r--django/db/models/fields/related.py12
-rw-r--r--django/db/models/options.py7
-rw-r--r--django/db/models/query.py100
5 files changed, 131 insertions, 43 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 899b1086c9..7a1110a718 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())
@@ -215,17 +215,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 9ba3ac96c6..34cf2f0eee 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -72,12 +72,17 @@ class Field(object):
maxlength=None, unique=False, blank=False, null=False, db_index=False,
core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True,
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):
+ validator_list=None, choices=None, radio_admin=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
@@ -88,6 +93,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
@@ -166,7 +172,7 @@ class Field(object):
def get_db_prep_lookup(self, lookup_type, value):
"Returns field's value prepared for database lookup."
- if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):
+ if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):
return [value]
elif lookup_type in ('range', 'in'):
return value
@@ -198,7 +204,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 ""
@@ -792,6 +798,7 @@ class IntegerField(Field):
return super(IntegerField, self).formfield(**defaults)
class IPAddressField(Field):
+ empty_strings_allowed = False
def __init__(self, *args, **kwargs):
kwargs['maxlength'] = 15
Field.__init__(self, *args, **kwargs)
@@ -803,6 +810,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)
@@ -877,10 +885,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):
@@ -898,7 +914,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 1b5716eb00..347ec8feb8 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 d75bfd495d..d804cc944d 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -12,7 +12,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):
@@ -26,6 +26,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
@@ -58,6 +59,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',)
@@ -72,6 +75,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
diff --git a/django/db/models/query.py b/django/db/models/query.py
index a6e702be18..92bc9d78ed 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -1,15 +1,18 @@
+from django.conf import settings
from django.db import backend, connection, transaction
from django.db.models.fields import DateField, FieldDoesNotExist
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 = '__'
@@ -20,6 +23,7 @@ QUERY_TERMS = (
'gt', 'gte', 'lt', 'lte', 'in',
'startswith', 'istartswith', 'endswith', 'iendswith',
'range', 'year', 'month', 'day', 'isnull', 'search',
+ 'regex', 'iregex',
)
# Size of each "chunk" for get_iterator calls.
@@ -77,7 +81,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 +185,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 +560,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 +580,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 +622,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 +772,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':
@@ -748,6 +799,15 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))
elif lookup_type == 'search':
return backend.get_fulltext_search_sql(table_prefix + field_name)
+ elif lookup_type in ('regex', 'iregex'):
+ if settings.DATABASE_ENGINE == 'oracle':
+ if lookup_type == 'regex':
+ match_option = 'c'
+ else:
+ match_option = 'i'
+ return "REGEXP_LIKE(%s%s, %s, '%s')" % (table_prefix, field_name, cast_sql, match_option)
+ else:
+ raise NotImplementedError
raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type)
def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0):