summaryrefslogtreecommitdiff
path: root/django/db
diff options
context:
space:
mode:
authorBrian Rosner <brosner@gmail.com>2008-06-26 15:42:33 +0000
committerBrian Rosner <brosner@gmail.com>2008-06-26 15:42:33 +0000
commitc8da0874c78ed4c6e1ad08cc78228799a333f76c (patch)
treebef645a8eb2c1a17f013a1031ed34494547dced0 /django/db
parentf15845c573f019fc7f6d7404add122f9b7c52dc4 (diff)
newforms-admin: Merged from trunk up to [7766].
git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7770 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/db')
-rw-r--r--django/db/backends/__init__.py1
-rw-r--r--django/db/backends/oracle/base.py1
-rw-r--r--django/db/backends/oracle/creation.py12
-rw-r--r--django/db/models/fields/__init__.py4
-rw-r--r--django/db/models/fields/related.py9
-rw-r--r--django/db/models/options.py11
-rw-r--r--django/db/models/query.py36
-rw-r--r--django/db/models/sql/query.py72
-rw-r--r--django/db/models/sql/subqueries.py8
9 files changed, 103 insertions, 51 deletions
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 63a6435bdb..7a4e46a7d7 100644
--- a/django/db/backends/__init__.py
+++ b/django/db/backends/__init__.py
@@ -45,7 +45,6 @@ class BaseDatabaseFeatures(object):
autoindexes_primary_keys = True
inline_fk_references = True
needs_datetime_string_cast = True
- needs_upper_for_iops = False
supports_constraints = True
supports_tablespaces = False
uses_case_insensitive_names = False
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index a1a9fb1c73..1df6bac069 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -27,7 +27,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259)
empty_fetchmany_value = ()
needs_datetime_string_cast = False
- needs_upper_for_iops = True
supports_tablespaces = True
uses_case_insensitive_names = True
uses_custom_query_class = True
diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py
index f4ada55ac6..219686789f 100644
--- a/django/db/backends/oracle/creation.py
+++ b/django/db/backends/oracle/creation.py
@@ -5,9 +5,13 @@ from django.core import management
# types, as strings. Column-type strings can contain format strings; they'll
# be interpolated against the values of Field.__dict__ before being output.
# If a column type is set to None, it won't be included in the output.
+#
+# Any format strings starting with "qn_" are quoted before being used in the
+# output (the "qn_" prefix is stripped before the lookup is performed.
+
DATA_TYPES = {
'AutoField': 'NUMBER(11)',
- 'BooleanField': 'NUMBER(1) CHECK (%(column)s IN (0,1))',
+ 'BooleanField': 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
'CharField': 'NVARCHAR2(%(max_length)s)',
'CommaSeparatedIntegerField': 'VARCHAR2(%(max_length)s)',
'DateField': 'DATE',
@@ -19,11 +23,11 @@ DATA_TYPES = {
'ImageField': 'NVARCHAR2(%(max_length)s)',
'IntegerField': 'NUMBER(11)',
'IPAddressField': 'VARCHAR2(15)',
- 'NullBooleanField': 'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))',
+ 'NullBooleanField': 'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(column)s IS NULL))',
'OneToOneField': 'NUMBER(11)',
'PhoneNumberField': 'VARCHAR2(20)',
- 'PositiveIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)',
- 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(column)s >= 0)',
+ 'PositiveIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)',
+ 'PositiveSmallIntegerField': 'NUMBER(11) CHECK (%(qn_column)s >= 0)',
'SlugField': 'NVARCHAR2(50)',
'SmallIntegerField': 'NUMBER(11)',
'TextField': 'NCLOB',
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 8a51193a01..d906d5bdda 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -16,6 +16,7 @@ from django.core import validators
from django import oldforms
from django import newforms as forms
from django.core.exceptions import ObjectDoesNotExist
+from django.utils.datastructures import DictWrapper
from django.utils.functional import curry
from django.utils.itercompat import tee
from django.utils.text import capfirst
@@ -153,8 +154,9 @@ class Field(object):
# mapped to one of the built-in Django field types. In this case, you
# can implement db_type() instead of get_internal_type() to specify
# exactly which wacky database column type you want to use.
+ data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
try:
- return get_creation_module().DATA_TYPES[self.get_internal_type()] % self.__dict__
+ return get_creation_module().DATA_TYPES[self.get_internal_type()] % data
except KeyError:
return None
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 1c0466f97f..5ae94e02de 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -103,13 +103,15 @@ class RelatedField(object):
if hasattr(sup, 'contribute_to_class'):
sup.contribute_to_class(cls, name)
+
+ if not cls._meta.abstract and self.rel.related_name:
+ self.rel.related_name = self.rel.related_name % {'class': cls.__name__.lower()}
+
other = self.rel.to
if isinstance(other, basestring):
add_lazy_relation(cls, self, other)
else:
self.do_related_class(other, cls)
- if not cls._meta.abstract and self.rel.related_name:
- self.rel.related_name = self.rel.related_name % {'class': cls.__name__.lower()}
def set_attributes_from_rel(self):
self.name = self.name or (self.rel.to._meta.object_name.lower() + '_' + self.rel.to._meta.pk.name)
@@ -119,7 +121,8 @@ class RelatedField(object):
def do_related_class(self, other, cls):
self.set_attributes_from_rel()
related = RelatedObject(other, cls, self)
- self.contribute_to_related_class(other, related)
+ if not cls._meta.abstract:
+ self.contribute_to_related_class(other, related)
def get_db_prep_lookup(self, lookup_type, value):
# If we are doing a lookup on a Related Field, we must be
diff --git a/django/db/models/options.py b/django/db/models/options.py
index c78ab1cd48..1ac24fb311 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -273,14 +273,17 @@ class Options(object):
"""
Initialises the field name -> field object mapping.
"""
- cache = dict([(f.name, (f, m, True, False)) for f, m in
- self.get_fields_with_model()])
- for f, model in self.get_m2m_with_model():
- cache[f.name] = (f, model, True, True)
+ cache = {}
+ # We intentionally handle related m2m objects first so that symmetrical
+ # m2m accessor names can be overridden, if necessary.
for f, model in self.get_all_related_m2m_objects_with_model():
cache[f.field.related_query_name()] = (f, model, False, True)
for f, model in self.get_all_related_objects_with_model():
cache[f.field.related_query_name()] = (f, model, False, False)
+ for f, model in self.get_m2m_with_model():
+ cache[f.name] = (f, model, True, True)
+ for f, model in self.get_fields_with_model():
+ cache[f.name] = (f, model, True, False)
if self.order_with_respect_to:
cache['_order'] = OrderWrt(), None, True, False
if app_cache_ready():
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 8714cffb7f..98caaf004c 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -36,7 +36,7 @@ class CollectedObjects(object):
"""
Adds an item.
model is the class of the object being added,
- pk is the primary key, obj is the object itself,
+ pk is the primary key, obj is the object itself,
parent_model is the model of the parent object
that this object was reached through, nullable should
be True if this relation is nullable.
@@ -77,7 +77,7 @@ class CollectedObjects(object):
def ordered_keys(self):
"""
- Returns the models in the order that they should be
+ Returns the models in the order that they should be
dealth with i.e. models with no dependencies first.
"""
dealt_with = SortedDict()
@@ -92,7 +92,7 @@ class CollectedObjects(object):
found = True
if not found:
raise CyclicDependency("There is a cyclic dependency of items to be processed.")
-
+
return dealt_with.keys()
def unordered_keys(self):
@@ -218,6 +218,8 @@ class QuerySet(object):
def __and__(self, other):
self._merge_sanity_check(other)
+ if isinstance(other, EmptyQuerySet):
+ return other._clone()
combined = self._clone()
combined.query.combine(other.query, sql.AND)
return combined
@@ -225,6 +227,8 @@ class QuerySet(object):
def __or__(self, other):
self._merge_sanity_check(other)
combined = self._clone()
+ if isinstance(other, EmptyQuerySet):
+ return combined
combined.query.combine(other.query, sql.OR)
return combined
@@ -488,7 +492,9 @@ class QuerySet(object):
and usually it will be more natural to use other methods.
"""
if isinstance(filter_obj, Q) or hasattr(filter_obj, 'add_to_query'):
- return self._filter_or_exclude(None, filter_obj)
+ clone = self._clone()
+ clone.query.add_q(filter_obj)
+ return clone
else:
return self._filter_or_exclude(None, **filter_obj)
@@ -583,11 +589,11 @@ class QuerySet(object):
def _merge_sanity_check(self, other):
"""
- Checks that we are merging two comparable queryset classes.
+ Checks that we are merging two comparable queryset classes. By default
+ this does nothing, but see the ValuesQuerySet for an example of where
+ it's useful.
"""
- if self.__class__ is not other.__class__:
- raise TypeError("Cannot merge querysets of different types ('%s' and '%s'."
- % (self.__class__.__name__, other.__class__.__name__))
+ pass
class ValuesQuerySet(QuerySet):
def __init__(self, *args, **kwargs):
@@ -599,7 +605,7 @@ class ValuesQuerySet(QuerySet):
# names of the model fields to select.
def iterator(self):
- if (not self.extra_names and
+ if (not self.extra_names and
len(self.field_names) != len(self.model._meta.fields)):
self.query.trim_extra_select(self.extra_names)
names = self.query.extra_select.keys() + self.field_names
@@ -688,9 +694,9 @@ class DateQuerySet(QuerySet):
"""
self.query = self.query.clone(klass=sql.DateQuery, setup=True)
self.query.select = []
- self.query.add_date_select(self._field.column, self._kind, self._order)
+ self.query.add_date_select(self._field, self._kind, self._order)
if self._field.null:
- self.query.add_filter(('%s__isnull' % self._field.name, True))
+ self.query.add_filter(('%s__isnull' % self._field.name, False))
def _clone(self, klass=None, setup=False, **kwargs):
c = super(DateQuerySet, self)._clone(klass, False, **kwargs)
@@ -705,6 +711,12 @@ class EmptyQuerySet(QuerySet):
super(EmptyQuerySet, self).__init__(model, query)
self._result_cache = []
+ def __and__(self, other):
+ return self._clone()
+
+ def __or__(self, other):
+ return other._clone()
+
def count(self):
return 0
@@ -773,7 +785,7 @@ def delete_objects(seen_objs):
except CyclicDependency:
# if there is a cyclic dependency, we cannot in general delete
# the objects. However, if an appropriate transaction is set
- # up, or if the database is lax enough, it will succeed.
+ # up, or if the database is lax enough, it will succeed.
# So for now, we go ahead and try anway.
ordered_classes = seen_objs.unordered_keys()
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 3044882a86..e8d10bc55b 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -610,6 +610,10 @@ class Query(object):
alias = joins[-1]
col = target.column
+ # Must use left outer joins for nullable fields.
+ for join in joins:
+ self.promote_alias(join)
+
# If we get to this point and the field is a relation to another model,
# append the default ordering for that model.
if field.rel and len(joins) > 1 and opts.ordering:
@@ -631,8 +635,10 @@ class Query(object):
# We have to do the same "final join" optimisation as in
# add_filter, since the final column might not otherwise be part of
# the select set (so we can't order on it).
- join = self.alias_map[alias]
- if col == join[RHS_JOIN_COL]:
+ while 1:
+ join = self.alias_map[alias]
+ if col != join[RHS_JOIN_COL]:
+ break
self.unref_alias(alias)
alias = join[LHS_ALIAS]
col = join[LHS_JOIN_COL]
@@ -679,12 +685,16 @@ class Query(object):
for the join to contain NULL values on the left. If 'unconditional' is
False, the join is only promoted if it is nullable, otherwise it is
always promoted.
+
+ Returns True if the join was promoted.
"""
if ((unconditional or self.alias_map[alias][NULLABLE]) and
self.alias_map[alias] != self.LOUTER):
data = list(self.alias_map[alias])
data[JOIN_TYPE] = self.LOUTER
self.alias_map[alias] = tuple(data)
+ return True
+ return False
def change_aliases(self, change_map):
"""
@@ -826,6 +836,10 @@ class Query(object):
if not always_create:
for alias in self.join_map.get(t_ident, ()):
if alias not in exclusions:
+ if lhs_table and not self.alias_refcount[self.alias_map[alias][LHS_ALIAS]]:
+ # The LHS of this join tuple is no longer part of the
+ # query, so skip this possibility.
+ continue
self.ref_alias(alias)
if promote:
self.promote_alias(alias)
@@ -985,20 +999,22 @@ class Query(object):
col = target.column
alias = join_list[-1]
- if final > 1:
+ while final > 1:
# An optimization: if the final join is against the same column as
# we are comparing against, we can go back one step in the join
- # chain and compare against the lhs of the join instead. The result
- # (potentially) involves one less table join.
+ # chain and compare against the lhs of the join instead (and then
+ # repeat the optimization). The result, potentially, involves less
+ # table joins.
join = self.alias_map[alias]
- if col == join[RHS_JOIN_COL]:
- self.unref_alias(alias)
- alias = join[LHS_ALIAS]
- col = join[LHS_JOIN_COL]
- join_list = join_list[:-1]
- final -= 1
- if final == penultimate:
- penultimate = last.pop()
+ if col != join[RHS_JOIN_COL]:
+ break
+ self.unref_alias(alias)
+ alias = join[LHS_ALIAS]
+ col = join[LHS_JOIN_COL]
+ join_list = join_list[:-1]
+ final -= 1
+ if final == penultimate:
+ penultimate = last.pop()
if (lookup_type == 'isnull' and value is True and not negate and
final > 1):
@@ -1033,17 +1049,27 @@ class Query(object):
self.promote_alias(table)
self.where.add((alias, col, field, lookup_type, value), connector)
+
if negate:
for alias in join_list:
self.promote_alias(alias)
- if final > 1 and lookup_type != 'isnull':
- for alias in join_list:
- if self.alias_map[alias] == self.LOUTER:
- j_col = self.alias_map[alias][RHS_JOIN_COL]
- entry = Node([(alias, j_col, None, 'isnull', True)])
- entry.negate()
- self.where.add(entry, AND)
- break
+ if lookup_type != 'isnull':
+ if final > 1:
+ for alias in join_list:
+ if self.alias_map[alias][JOIN_TYPE] == self.LOUTER:
+ j_col = self.alias_map[alias][RHS_JOIN_COL]
+ entry = Node([(alias, j_col, None, 'isnull', True)])
+ entry.negate()
+ self.where.add(entry, AND)
+ break
+ elif not (lookup_type == 'in' and not value):
+ # Leaky abstraction artifact: We have to specifically
+ # exclude the "foo__in=[]" case from this handling, because
+ # it's short-circuited in the Where class.
+ entry = Node([(alias, col, field, 'isnull', True)])
+ entry.negate()
+ self.where.add(entry, AND)
+
if can_reuse is not None:
can_reuse.update(join_list)
@@ -1294,10 +1320,12 @@ class Query(object):
final_alias = join[LHS_ALIAS]
col = join[LHS_JOIN_COL]
joins = joins[:-1]
+ promote = False
for join in joins[1:]:
# Only nullable aliases are promoted, so we don't end up
# doing unnecessary left outer joins here.
- self.promote_alias(join)
+ if self.promote_alias(join, promote):
+ promote = True
self.select.append((final_alias, col))
self.select_fields.append(field)
except MultiJoin:
diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py
index 28436abede..0bb741d706 100644
--- a/django/db/models/sql/subqueries.py
+++ b/django/db/models/sql/subqueries.py
@@ -357,12 +357,14 @@ class DateQuery(Query):
date = typecast_timestamp(str(date))
yield date
- def add_date_select(self, column, lookup_type, order='ASC'):
+ def add_date_select(self, field, lookup_type, order='ASC'):
"""
Converts the query into a date extraction query.
"""
- alias = self.join((None, self.model._meta.db_table, None, None))
- select = Date((alias, column), lookup_type,
+ result = self.setup_joins([field.name], self.get_meta(),
+ self.get_initial_alias(), False)
+ alias = result[3][-1]
+ select = Date((alias, field.column), lookup_type,
self.connection.ops.date_trunc_sql)
self.select = [select]
self.select_fields = [None]