diff options
| author | Brian Rosner <brosner@gmail.com> | 2008-06-26 15:42:33 +0000 |
|---|---|---|
| committer | Brian Rosner <brosner@gmail.com> | 2008-06-26 15:42:33 +0000 |
| commit | c8da0874c78ed4c6e1ad08cc78228799a333f76c (patch) | |
| tree | bef645a8eb2c1a17f013a1031ed34494547dced0 /django/db | |
| parent | f15845c573f019fc7f6d7404add122f9b7c52dc4 (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__.py | 1 | ||||
| -rw-r--r-- | django/db/backends/oracle/base.py | 1 | ||||
| -rw-r--r-- | django/db/backends/oracle/creation.py | 12 | ||||
| -rw-r--r-- | django/db/models/fields/__init__.py | 4 | ||||
| -rw-r--r-- | django/db/models/fields/related.py | 9 | ||||
| -rw-r--r-- | django/db/models/options.py | 11 | ||||
| -rw-r--r-- | django/db/models/query.py | 36 | ||||
| -rw-r--r-- | django/db/models/sql/query.py | 72 | ||||
| -rw-r--r-- | django/db/models/sql/subqueries.py | 8 |
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] |
