Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merged the queryset-refactor branch into trunk.

This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.

Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658


git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 9c52d56f6f8a9cdafb231adf9f4110473099c9b5 1 parent c91a30f
Malcolm Tredinnick authored April 27, 2008

Showing 57 changed files with 5,722 additions and 1,744 deletions. Show diff stats Hide diff stats

  1. 18  django/contrib/admin/views/main.py
  2. 5  django/contrib/contenttypes/generic.py
  3. 5  django/core/exceptions.py
  4. 24  django/core/management/sql.py
  5. 30  django/core/management/validation.py
  6. 2  django/core/serializers/base.py
  7. 14  django/db/__init__.py
  8. 39  django/db/backends/__init__.py
  9. 5  django/db/backends/mysql/base.py
  10. 5  django/db/backends/mysql_old/base.py
  11. 274  django/db/backends/oracle/base.py
  12. 151  django/db/backends/oracle/query.py
  13. 3  django/db/backends/postgresql/operations.py
  14. 3  django/db/backends/sqlite3/base.py
  15. 381  django/db/models/base.py
  16. 43  django/db/models/fields/__init__.py
  17. 16  django/db/models/fields/proxy.py
  18. 255  django/db/models/fields/related.py
  19. 40  django/db/models/manager.py
  20. 318  django/db/models/options.py
  21. 1,411  django/db/models/query.py
  22. 50  django/db/models/query_utils.py
  23. 7  django/db/models/sql/__init__.py
  24. 36  django/db/models/sql/constants.py
  25. 103  django/db/models/sql/datastructures.py
  26. 1,504  django/db/models/sql/query.py
  27. 367  django/db/models/sql/subqueries.py
  28. 171  django/db/models/sql/where.py
  29. 134  django/utils/tree.py
  30. 438  docs/db-api.txt
  31. 276  docs/model-api.txt
  32. 8  tests/modeltests/basic/models.py
  33. 6  tests/modeltests/custom_columns/models.py
  34. 5  tests/modeltests/field_subclassing/models.py
  35. 32  tests/modeltests/lookup/models.py
  36. 5  tests/modeltests/many_to_many/models.py
  37. 17  tests/modeltests/many_to_one/models.py
  38. 5  tests/modeltests/many_to_one_null/models.py
  39. 237  tests/modeltests/model_inheritance/models.py
  40. 43  tests/modeltests/one_to_one/models.py
  41. 21  tests/modeltests/or_lookups/models.py
  42. 0  tests/modeltests/order_with_respect_to/__init__.py
  43. 78  tests/modeltests/order_with_respect_to/models.py
  44. 13  tests/modeltests/ordering/models.py
  45. 2  tests/modeltests/reserved_names/models.py
  46. 2  tests/modeltests/reverse_lookup/models.py
  47. 47  tests/modeltests/select_related/models.py
  48. 29  tests/modeltests/serializers/models.py
  49. 3  tests/modeltests/signals/models.py
  50. 2  tests/modeltests/transactions/models.py
  51. 0  tests/modeltests/update/__init__.py
  52. 67  tests/modeltests/update/models.py
  53. 15  tests/regressiontests/null_queries/models.py
  54. 0  tests/regressiontests/queries/__init__.py
  55. 658  tests/regressiontests/queries/models.py
  56. 27  tests/regressiontests/serializers_regress/models.py
  57. 16  tests/regressiontests/serializers_regress/tests.py
18  django/contrib/admin/views/main.py
@@ -8,7 +8,7 @@
8 8
 from django.core.paginator import QuerySetPaginator, InvalidPage
9 9
 from django.shortcuts import get_object_or_404, render_to_response
10 10
 from django.db import models
11  
-from django.db.models.query import handle_legacy_orderlist, QuerySet
  11
+from django.db.models.query import QuerySet
12 12
 from django.http import Http404, HttpResponse, HttpResponseRedirect
13 13
 from django.utils.html import escape
14 14
 from django.utils.text import capfirst, get_text_list
@@ -627,7 +627,7 @@ def get_results(self, request):
627 627
         # Perform a slight optimization: Check to see whether any filters were
628 628
         # given. If not, use paginator.hits to calculate the number of objects,
629 629
         # because we've already done paginator.hits and the value is cached.
630  
-        if isinstance(self.query_set._filters, models.Q) and not self.query_set._filters.kwargs:
  630
+        if not self.query_set.query.where:
631 631
             full_result_count = result_count
632 632
         else:
633 633
             full_result_count = self.manager.count()
@@ -653,15 +653,12 @@ def get_results(self, request):
653 653
 
654 654
     def get_ordering(self):
655 655
         lookup_opts, params = self.lookup_opts, self.params
656  
-        # For ordering, first check the "ordering" parameter in the admin options,
657  
-        # then check the object's default ordering. If neither of those exist,
658  
-        # order descending by ID by default. Finally, look for manually-specified
659  
-        # ordering from the query string.
  656
+        # For ordering, first check the "ordering" parameter in the admin
  657
+        # options, then check the object's default ordering. If neither of
  658
+        # those exist, order descending by ID by default. Finally, look for
  659
+        # manually-specified ordering from the query string.
660 660
         ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
661 661
 
662  
-        # Normalize it to new-style ordering.
663  
-        ordering = handle_legacy_orderlist(ordering)
664  
-
665 662
         if ordering[0].startswith('-'):
666 663
             order_field, order_type = ordering[0][1:], 'desc'
667 664
         else:
@@ -753,8 +750,7 @@ def construct_search(field_name):
753 750
             for bit in self.query.split():
754 751
                 or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields]
755 752
                 other_qs = QuerySet(self.model)
756  
-                if qs._select_related:
757  
-                    other_qs = other_qs.select_related()
  753
+                other_qs.dup_select_related(qs)
758 754
                 other_qs = other_qs.filter(reduce(operator.or_, or_queries))
759 755
                 qs = qs & other_qs
760 756
 
5  django/contrib/contenttypes/generic.py
@@ -154,6 +154,11 @@ def set_attributes_from_rel(self):
154 154
     def get_internal_type(self):
155 155
         return "ManyToManyField"
156 156
 
  157
+    def db_type(self):
  158
+        # Since we're simulating a ManyToManyField, in effect, best return the
  159
+        # same db_type as well.
  160
+        return None
  161
+
157 162
 class ReverseGenericRelatedObjectsDescriptor(object):
158 163
     """
159 164
     This class provides the functionality that makes the related-object
5  django/core/exceptions.py
@@ -27,3 +27,8 @@ class MiddlewareNotUsed(Exception):
27 27
 class ImproperlyConfigured(Exception):
28 28
     "Django is somehow improperly configured"
29 29
     pass
  30
+
  31
+class FieldError(Exception):
  32
+    """Some kind of problem with a model field."""
  33
+    pass
  34
+
24  django/core/management/sql.py
@@ -26,7 +26,7 @@ def django_table_list(only_existing=False):
26 26
     for app in models.get_apps():
27 27
         for model in models.get_models(app):
28 28
             tables.append(model._meta.db_table)
29  
-            tables.extend([f.m2m_db_table() for f in model._meta.many_to_many])
  29
+            tables.extend([f.m2m_db_table() for f in model._meta.local_many_to_many])
30 30
     if only_existing:
31 31
         existing = table_list()
32 32
         tables = [t for t in tables if t in existing]
@@ -54,12 +54,12 @@ def sequence_list():
54 54
 
55 55
     for app in apps:
56 56
         for model in models.get_models(app):
57  
-            for f in model._meta.fields:
  57
+            for f in model._meta.local_fields:
58 58
                 if isinstance(f, models.AutoField):
59 59
                     sequence_list.append({'table': model._meta.db_table, 'column': f.column})
60 60
                     break # Only one AutoField is allowed per model, so don't bother continuing.
61 61
 
62  
-            for f in model._meta.many_to_many:
  62
+            for f in model._meta.local_many_to_many:
63 63
                 sequence_list.append({'table': f.m2m_db_table(), 'column': None})
64 64
 
65 65
     return sequence_list
@@ -149,7 +149,7 @@ def sql_delete(app, style):
149 149
         if cursor and table_name_converter(model._meta.db_table) in table_names:
150 150
             # The table exists, so it needs to be dropped
151 151
             opts = model._meta
152  
-            for f in opts.fields:
  152
+            for f in opts.local_fields:
153 153
                 if f.rel and f.rel.to not in to_delete:
154 154
                     references_to_delete.setdefault(f.rel.to, []).append( (model, f) )
155 155
 
@@ -181,7 +181,7 @@ def sql_delete(app, style):
181 181
     # Output DROP TABLE statements for many-to-many tables.
182 182
     for model in app_models:
183 183
         opts = model._meta
184  
-        for f in opts.many_to_many:
  184
+        for f in opts.local_many_to_many:
185 185
             if isinstance(f.rel, generic.GenericRel):
186 186
                 continue
187 187
             if cursor and table_name_converter(f.m2m_db_table()) in table_names:
@@ -258,7 +258,7 @@ def sql_model_create(model, style, known_models=set()):
258 258
     pending_references = {}
259 259
     qn = connection.ops.quote_name
260 260
     inline_references = connection.features.inline_fk_references
261  
-    for f in opts.fields:
  261
+    for f in opts.local_fields:
262 262
         col_type = f.db_type()
263 263
         tablespace = f.db_tablespace or opts.db_tablespace
264 264
         if col_type is None:
@@ -294,14 +294,8 @@ def sql_model_create(model, style, known_models=set()):
294 294
             style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
295 295
             style.SQL_KEYWORD('NULL'))
296 296
     for field_constraints in opts.unique_together:
297  
-        constraint_output = [style.SQL_KEYWORD('UNIQUE')]
298  
-        constraint_output.append('(%s)' % \
  297
+        table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
299 298
             ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
300  
-        if opts.db_tablespace and connection.features.supports_tablespaces \
301  
-               and connection.features.autoindexes_primary_keys:
302  
-            constraint_output.append(connection.ops.tablespace_sql(
303  
-                opts.db_tablespace, inline=True))
304  
-        table_output.append(' '.join(constraint_output))
305 299
 
306 300
     full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
307 301
     for i, line in enumerate(table_output): # Combine and add commas.
@@ -359,7 +353,7 @@ def many_to_many_sql_for_model(model, style):
359 353
     final_output = []
360 354
     qn = connection.ops.quote_name
361 355
     inline_references = connection.features.inline_fk_references
362  
-    for f in opts.many_to_many:
  356
+    for f in opts.local_many_to_many:
363 357
         if not isinstance(f.rel, generic.GenericRel):
364 358
             tablespace = f.db_tablespace or opts.db_tablespace
365 359
             if tablespace and connection.features.supports_tablespaces and connection.features.autoindexes_primary_keys:
@@ -466,7 +460,7 @@ def sql_indexes_for_model(model, style):
466 460
     output = []
467 461
 
468 462
     qn = connection.ops.quote_name
469  
-    for f in model._meta.fields:
  463
+    for f in model._meta.local_fields:
470 464
         if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
471 465
             unique = f.unique and 'UNIQUE ' or ''
472 466
             tablespace = f.db_tablespace or model._meta.db_tablespace
30  django/core/management/validation.py
@@ -32,7 +32,7 @@ def get_validation_errors(outfile, app=None):
32 32
         opts = cls._meta
33 33
 
34 34
         # Do field-specific validation.
35  
-        for f in opts.fields:
  35
+        for f in opts.local_fields:
36 36
             if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
37 37
                 e.add(opts, '"%s": You can\'t use "id" as a field name, because each model automatically gets an "id" field if none of the fields have primary_key=True. You need to either remove/rename your "id" field or add primary_key=True to a field.' % f.name)
38 38
             if f.name.endswith('_'):
@@ -69,8 +69,8 @@ def get_validation_errors(outfile, app=None):
69 69
                 if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.max_length > 255:
70 70
                     e.add(opts, '"%s": %s cannot have a "max_length" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s).' % (f.name, f.__class__.__name__, '.'.join([str(n) for n in db_version[:3]])))
71 71
 
72  
-            # Check to see if the related field will clash with any
73  
-            # existing fields, m2m fields, m2m related objects or related objects
  72
+            # Check to see if the related field will clash with any existing
  73
+            # fields, m2m fields, m2m related objects or related objects
74 74
             if f.rel:
75 75
                 if f.rel.to not in models.get_models():
76 76
                     e.add(opts, "'%s' has relation with model %s, which has not been installed" % (f.name, f.rel.to))
@@ -87,7 +87,7 @@ def get_validation_errors(outfile, app=None):
87 87
                         e.add(opts, "Accessor for field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
88 88
                     if r.name == rel_query_name:
89 89
                         e.add(opts, "Reverse query name for field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
90  
-                for r in rel_opts.many_to_many:
  90
+                for r in rel_opts.local_many_to_many:
91 91
                     if r.name == rel_name:
92 92
                         e.add(opts, "Accessor for field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
93 93
                     if r.name == rel_query_name:
@@ -104,9 +104,10 @@ def get_validation_errors(outfile, app=None):
104 104
                         if r.get_accessor_name() == rel_query_name:
105 105
                             e.add(opts, "Reverse query name for field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
106 106
 
107  
-        for i, f in enumerate(opts.many_to_many):
  107
+        for i, f in enumerate(opts.local_many_to_many):
108 108
             # Check to see if the related m2m field will clash with any
109  
-            # existing fields, m2m fields, m2m related objects or related objects
  109
+            # existing fields, m2m fields, m2m related objects or related
  110
+            # objects
110 111
             if f.rel.to not in models.get_models():
111 112
                 e.add(opts, "'%s' has m2m relation with model %s, which has not been installed" % (f.name, f.rel.to))
112 113
                 # it is a string and we could not find the model it refers to
@@ -117,17 +118,17 @@ def get_validation_errors(outfile, app=None):
117 118
             rel_opts = f.rel.to._meta
118 119
             rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
119 120
             rel_query_name = f.related_query_name()
120  
-            # If rel_name is none, there is no reverse accessor.
121  
-            # (This only occurs for symmetrical m2m relations to self).
122  
-            # If this is the case, there are no clashes to check for this field, as
123  
-            # there are no reverse descriptors for this field.
  121
+            # If rel_name is none, there is no reverse accessor (this only
  122
+            # occurs for symmetrical m2m relations to self). If this is the
  123
+            # case, there are no clashes to check for this field, as there are
  124
+            # no reverse descriptors for this field.
124 125
             if rel_name is not None:
125 126
                 for r in rel_opts.fields:
126 127
                     if r.name == rel_name:
127 128
                         e.add(opts, "Accessor for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
128 129
                     if r.name == rel_query_name:
129 130
                         e.add(opts, "Reverse query name for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
130  
-                for r in rel_opts.many_to_many:
  131
+                for r in rel_opts.local_many_to_many:
131 132
                     if r.name == rel_name:
132 133
                         e.add(opts, "Accessor for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
133 134
                     if r.name == rel_query_name:
@@ -200,7 +201,10 @@ def get_validation_errors(outfile, app=None):
200 201
                     field_name = field_name[1:]
201 202
                 if opts.order_with_respect_to and field_name == '_order':
202 203
                     continue
203  
-                if '.' in field_name: continue # Skip ordering in the format 'table.field'.
  204
+                # Skip ordering in the format field1__field2 (FIXME: checking
  205
+                # this format would be nice, but it's a little fiddly).
  206
+                if '_' in field_name:
  207
+                    continue
204 208
                 try:
205 209
                     opts.get_field(field_name, many_to_many=False)
206 210
                 except models.FieldDoesNotExist:
@@ -228,5 +232,7 @@ def get_validation_errors(outfile, app=None):
228 232
                 else:
229 233
                     if isinstance(f.rel, models.ManyToManyRel):
230 234
                         e.add(opts, '"unique_together" refers to %s. ManyToManyFields are not supported in unique_together.' % f.name)
  235
+                    if f not in opts.local_fields:
  236
+                        e.add(opts, '"unique_together" refers to %s. This is not in the same model as the unique_together statement.' % f.name)
231 237
 
232 238
     return len(e.errors)
2  django/core/serializers/base.py
@@ -165,7 +165,7 @@ def save(self, save_m2m=True):
165 165
         # This ensures that the data that is deserialized is literally
166 166
         # what came from the file, not post-processed by pre_save/save
167 167
         # methods.
168  
-        models.Model.save(self.object, raw=True)
  168
+        models.Model.save_base(self.object, raw=True)
169 169
         if self.m2m_data and save_m2m:
170 170
             for accessor_name, object_list in self.m2m_data.items():
171 171
                 setattr(self.object, accessor_name, object_list)
14  django/db/__init__.py
@@ -11,16 +11,18 @@
11 11
     settings.DATABASE_ENGINE = 'dummy'
12 12
 
13 13
 try:
14  
-    # Most of the time, the database backend will be one of the official 
  14
+    # Most of the time, the database backend will be one of the official
15 15
     # backends that ships with Django, so look there first.
16 16
     _import_path = 'django.db.backends.'
17 17
     backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
  18
+    creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
18 19
 except ImportError, e:
19  
-    # If the import failed, we might be looking for a database backend 
  20
+    # If the import failed, we might be looking for a database backend
20 21
     # distributed external to Django. So we'll try that next.
21 22
     try:
22 23
         _import_path = ''
23 24
         backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
  25
+        creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])
24 26
     except ImportError, e_user:
25 27
         # The database backend wasn't found. Display a helpful error message
26 28
         # listing all possible (built-in) database backends.
@@ -37,10 +39,12 @@ def _import_database_module(import_path='', module_name=''):
37 39
     """Lazily import a database module when requested."""
38 40
     return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
39 41
 
40  
-# We don't want to import the introspect/creation modules unless 
41  
-# someone asks for 'em, so lazily load them on demmand.
  42
+# We don't want to import the introspect module unless someone asks for it, so
  43
+# lazily load it on demmand.
42 44
 get_introspection_module = curry(_import_database_module, _import_path, 'introspection')
43  
-get_creation_module = curry(_import_database_module, _import_path, 'creation')
  45
+
  46
+def get_creation_module():
  47
+    return creation
44 48
 
45 49
 # We want runshell() to work the same way, but we have to treat it a
46 50
 # little differently (since it just runs instead of returning a module like
39  django/db/backends/__init__.py
@@ -49,7 +49,8 @@ class BaseDatabaseFeatures(object):
49 49
     supports_constraints = True
50 50
     supports_tablespaces = False
51 51
     uses_case_insensitive_names = False
52  
-    uses_custom_queryset = False
  52
+    uses_custom_query_class = False
  53
+    empty_fetchmany_value = []
53 54
 
54 55
 class BaseDatabaseOperations(object):
55 56
     """
@@ -86,10 +87,9 @@ def datetime_cast_sql(self):
86 87
         Returns the SQL necessary to cast a datetime value so that it will be
87 88
         retrieved as a Python datetime object instead of a string.
88 89
 
89  
-        This SQL should include a '%s' in place of the field's name. This
90  
-        method should return None if no casting is necessary.
  90
+        This SQL should include a '%s' in place of the field's name.
91 91
         """
92  
-        return None
  92
+        return "%s"
93 93
 
94 94
     def deferrable_sql(self):
95 95
         """
@@ -169,6 +169,14 @@ def limit_offset_sql(self, limit, offset=None):
169 169
             sql += " OFFSET %s" % offset
170 170
         return sql
171 171
 
  172
+    def lookup_cast(self, lookup_type):
  173
+        """
  174
+        Returns the string to use in a query when performing lookups
  175
+        ("contains", "like", etc). The resulting string should contain a '%s'
  176
+        placeholder for the column being searched against.
  177
+        """
  178
+        return "%s"
  179
+
172 180
     def max_name_length(self):
173 181
         """
174 182
         Returns the maximum length of table and column names, or None if there
@@ -176,6 +184,14 @@ def max_name_length(self):
176 184
         """
177 185
         return None
178 186
 
  187
+    def no_limit_value(self):
  188
+        """
  189
+        Returns the value to use for the LIMIT when we are wanting "LIMIT
  190
+        infinity". Returns None if the limit clause can be omitted in this case.
  191
+        """
  192
+        # FIXME: API may need to change once Oracle backend is repaired.
  193
+        raise NotImplementedError()
  194
+
179 195
     def pk_default_value(self):
180 196
         """
181 197
         Returns the value to use during an INSERT statement to specify that
@@ -183,11 +199,11 @@ def pk_default_value(self):
183 199
         """
184 200
         return 'DEFAULT'
185 201
 
186  
-    def query_set_class(self, DefaultQuerySet):
  202
+    def query_class(self, DefaultQueryClass):
187 203
         """
188 204
         Given the default QuerySet class, returns a custom QuerySet class
189 205
         to use for this backend. Returns None if a custom QuerySet isn't used.
190  
-        See also BaseDatabaseFeatures.uses_custom_queryset, which regulates
  206
+        See also BaseDatabaseFeatures.uses_custom_query_class, which regulates
191 207
         whether this method is called at all.
192 208
         """
193 209
         return None
@@ -205,6 +221,17 @@ def random_function_sql(self):
205 221
         """
206 222
         return 'RANDOM()'
207 223
 
  224
+    def regex_lookup(self, lookup_type):
  225
+        """
  226
+        Returns the string to use in a query when performing regular expression
  227
+        lookups (using "regex" or "iregex"). The resulting string should
  228
+        contain a '%s' placeholder for the column being searched against.
  229
+
  230
+        If the feature is not supported (or part of it is not supported), a
  231
+        NotImplementedError exception can be raised.
  232
+        """
  233
+        raise NotImplementedError
  234
+
208 235
     def sql_flush(self, style, tables, sequences):
209 236
         """
210 237
         Returns a list of SQL statements required to remove all data from
5  django/db/backends/mysql/base.py
@@ -62,6 +62,7 @@
62 62
 class DatabaseFeatures(BaseDatabaseFeatures):
63 63
     autoindexes_primary_keys = False
64 64
     inline_fk_references = False
  65
+    empty_fetchmany_value = ()
65 66
 
66 67
 class DatabaseOperations(BaseDatabaseOperations):
67 68
     def date_extract_sql(self, lookup_type, field_name):
@@ -94,6 +95,10 @@ def limit_offset_sql(self, limit, offset=None):
94 95
             sql += "%s," % offset
95 96
         return sql + str(limit)
96 97
 
  98
+    def no_limit_value(self):
  99
+        # 2**64 - 1, as recommended by the MySQL documentation
  100
+        return 18446744073709551615L
  101
+
97 102
     def quote_name(self, name):
98 103
         if name.startswith("`") and name.endswith("`"):
99 104
             return name # Quoting once is enough.
5  django/db/backends/mysql_old/base.py
@@ -66,6 +66,7 @@ def __getattr__(self, attr):
66 66
 class DatabaseFeatures(BaseDatabaseFeatures):
67 67
     autoindexes_primary_keys = False
68 68
     inline_fk_references = False
  69
+    empty_fetchmany_value = ()
69 70
 
70 71
 class DatabaseOperations(BaseDatabaseOperations):
71 72
     def date_extract_sql(self, lookup_type, field_name):
@@ -98,6 +99,10 @@ def limit_offset_sql(self, limit, offset=None):
98 99
             sql += "%s," % offset
99 100
         return sql + str(limit)
100 101
 
  102
+    def no_limit_value(self):
  103
+        # 2**64 - 1, as recommended by the MySQL documentation
  104
+        return 18446744073709551615L
  105
+
101 106
     def quote_name(self, name):
102 107
         if name.startswith("`") and name.endswith("`"):
103 108
             return name # Quoting once is enough.
274  django/db/backends/oracle/base.py
@@ -4,11 +4,12 @@
4 4
 Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
5 5
 """
6 6
 
  7
+import os
  8
+
7 9
 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
  10
+from django.db.backends.oracle import query
8 11
 from django.utils.datastructures import SortedDict
9 12
 from django.utils.encoding import smart_str, force_unicode
10  
-import datetime
11  
-import os
12 13
 
13 14
 # Oracle takes client-side character set encoding from the environment.
14 15
 os.environ['NLS_LANG'] = '.UTF8'
@@ -24,11 +25,12 @@
24 25
 class DatabaseFeatures(BaseDatabaseFeatures):
25 26
     allows_group_by_ordinal = False
26 27
     allows_unique_and_pk = False        # Suppress UNIQUE/PK for Oracle (ORA-02259)
  28
+    empty_fetchmany_value = ()
27 29
     needs_datetime_string_cast = False
28 30
     needs_upper_for_iops = True
29 31
     supports_tablespaces = True
30 32
     uses_case_insensitive_names = True
31  
-    uses_custom_queryset = True
  33
+    uses_custom_query_class = True
32 34
 
33 35
 class DatabaseOperations(BaseDatabaseOperations):
34 36
     def autoinc_sql(self, table, column):
@@ -89,243 +91,16 @@ def limit_offset_sql(self, limit, offset=None):
89 91
         # Instead, they are handled in django/db/backends/oracle/query.py.
90 92
         return ""
91 93
 
  94
+    def lookup_cast(self, lookup_type):
  95
+        if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
  96
+            return "UPPER(%s)"
  97
+        return "%s"
  98
+
92 99
     def max_name_length(self):
93 100
         return 30
94 101
 
95  
-    def query_set_class(self, DefaultQuerySet):
96  
-        from django.db import connection
97  
-        from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word
98  
-
99  
-        class OracleQuerySet(DefaultQuerySet):
100  
-
101  
-            def iterator(self):
102  
-                "Performs the SELECT database lookup of this QuerySet."
103  
-
104  
-                from django.db.models.query import get_cached_row
105  
-
106  
-                # self._select is a dictionary, and dictionaries' key order is
107  
-                # undefined, so we convert it to a list of tuples.
108  
-                extra_select = self._select.items()
109  
-
110  
-                full_query = None
111  
-
112  
-                try:
113  
-                    try:
114  
-                        select, sql, params, full_query = self._get_sql_clause(get_full_query=True)
115  
-                    except TypeError:
116  
-                        select, sql, params = self._get_sql_clause()
117  
-                except EmptyResultSet:
118  
-                    raise StopIteration
119  
-                if not full_query:
120  
-                    full_query = "SELECT %s%s\n%s" % ((self._distinct and "DISTINCT " or ""), ', '.join(select), sql)
121  
-
122  
-                cursor = connection.cursor()
123  
-                cursor.execute(full_query, params)
124  
-
125  
-                fill_cache = self._select_related
126  
-                fields = self.model._meta.fields
127  
-                index_end = len(fields)
128  
-
129  
-                # so here's the logic;
130  
-                # 1. retrieve each row in turn
131  
-                # 2. convert NCLOBs
132  
-
133  
-                while 1:
134  
-                    rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
135  
-                    if not rows:
136  
-                        raise StopIteration
137  
-                    for row in rows:
138  
-                        row = self.resolve_columns(row, fields)
139  
-                        if fill_cache:
140  
-                            obj, index_end = get_cached_row(klass=self.model, row=row,
141  
-                                                            index_start=0, max_depth=self._max_related_depth)
142  
-                        else:
143  
-                            obj = self.model(*row[:index_end])
144  
-                        for i, k in enumerate(extra_select):
145  
-                            setattr(obj, k[0], row[index_end+i])
146  
-                        yield obj
147  
-
148  
-
149  
-            def _get_sql_clause(self, get_full_query=False):
150  
-                from django.db.models.query import fill_table_cache, \
151  
-                    handle_legacy_orderlist, orderfield2column
152  
-
153  
-                opts = self.model._meta
154  
-                qn = connection.ops.quote_name
155  
-
156  
-                # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
157  
-                select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields]
158  
-                tables = [quote_only_if_word(t) for t in self._tables]
159  
-                joins = SortedDict()
160  
-                where = self._where[:]
161  
-                params = self._params[:]
162  
-
163  
-                # Convert self._filters into SQL.
164  
-                joins2, where2, params2 = self._filters.get_sql(opts)
165  
-                joins.update(joins2)
166  
-                where.extend(where2)
167  
-                params.extend(params2)
168  
-
169  
-                # Add additional tables and WHERE clauses based on select_related.
170  
-                if self._select_related:
171  
-                    fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table])
172  
-
173  
-                # Add any additional SELECTs.
174  
-                if self._select:
175  
-                    select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()])
176  
-
177  
-                # Start composing the body of the SQL statement.
178  
-                sql = [" FROM", qn(opts.db_table)]
179  
-
180  
-                # Compose the join dictionary into SQL describing the joins.
181  
-                if joins:
182  
-                    sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition)
183  
-                                    for (alias, (table, join_type, condition)) in joins.items()]))
184  
-
185  
-                # Compose the tables clause into SQL.
186  
-                if tables:
187  
-                    sql.append(", " + ", ".join(tables))
188  
-
189  
-                # Compose the where clause into SQL.
190  
-                if where:
191  
-                    sql.append(where and "WHERE " + " AND ".join(where))
192  
-
193  
-                # ORDER BY clause
194  
-                order_by = []
195  
-                if self._order_by is not None:
196  
-                    ordering_to_use = self._order_by
197  
-                else:
198  
-                    ordering_to_use = opts.ordering
199  
-                for f in handle_legacy_orderlist(ordering_to_use):
200  
-                    if f == '?': # Special case.
201  
-                        order_by.append(DatabaseOperations().random_function_sql())
202  
-                    else:
203  
-                        if f.startswith('-'):
204  
-                            col_name = f[1:]
205  
-                            order = "DESC"
206  
-                        else:
207  
-                            col_name = f
208  
-                            order = "ASC"
209  
-                        if "." in col_name:
210  
-                            table_prefix, col_name = col_name.split('.', 1)
211  
-                            table_prefix = qn(table_prefix) + '.'
212  
-                        else:
213  
-                            # Use the database table as a column prefix if it wasn't given,
214  
-                            # and if the requested column isn't a custom SELECT.
215  
-                            if "." not in col_name and col_name not in (self._select or ()):
216  
-                                table_prefix = qn(opts.db_table) + '.'
217  
-                            else:
218  
-                                table_prefix = ''
219  
-                        order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order))
220  
-                if order_by:
221  
-                    sql.append("ORDER BY " + ", ".join(order_by))
222  
-
223  
-                # Look for column name collisions in the select elements
224  
-                # and fix them with an AS alias.  This allows us to do a
225  
-                # SELECT * later in the paging query.
226  
-                cols = [clause.split('.')[-1] for clause in select]
227  
-                for index, col in enumerate(cols):
228  
-                    if cols.count(col) > 1:
229  
-                        col = '%s%d' % (col.replace('"', ''), index)
230  
-                        cols[index] = col
231  
-                        select[index] = '%s AS %s' % (select[index], col)
232  
-
233  
-                # LIMIT and OFFSET clauses
234  
-                # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
235  
-                select_clause = ",".join(select)
236  
-                distinct = (self._distinct and "DISTINCT " or "")
237  
-
238  
-                if order_by:
239  
-                    order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by))
240  
-                else:
241  
-                    #Oracle's row_number() function always requires an order-by clause.
242  
-                    #So we need to define a default order-by, since none was provided.
243  
-                    order_by_clause = " OVER (ORDER BY %s.%s)" % \
244  
-                        (qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column))
245  
-                # limit_and_offset_clause
246  
-                if self._limit is None:
247  
-                    assert self._offset is None, "'offset' is not allowed without 'limit'"
248  
-
249  
-                if self._offset is not None:
250  
-                    offset = int(self._offset)
251  
-                else:
252  
-                    offset = 0
253  
-                if self._limit is not None:
254  
-                    limit = int(self._limit)
255  
-                else:
256  
-                    limit = None
257  
-
258  
-                limit_and_offset_clause = ''
259  
-                if limit is not None:
260  
-                    limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
261  
-                elif offset:
262  
-                    limit_and_offset_clause = "WHERE rn > %s" % (offset)
263  
-
264  
-                if len(limit_and_offset_clause) > 0:
265  
-                    fmt = \
266  
-    """SELECT * FROM
267  
-      (SELECT %s%s,
268  
-              ROW_NUMBER()%s AS rn
269  
-       %s)
270  
-    %s"""
271  
-                    full_query = fmt % (distinct, select_clause,
272  
-                                        order_by_clause, ' '.join(sql).strip(),
273  
-                                        limit_and_offset_clause)
274  
-                else:
275  
-                    full_query = None
276  
-
277  
-                if get_full_query:
278  
-                    return select, " ".join(sql), params, full_query
279  
-                else:
280  
-                    return select, " ".join(sql), params
281  
-
282  
-            def resolve_columns(self, row, fields=()):
283  
-                from django.db.models.fields import DateField, DateTimeField, \
284  
-                    TimeField, BooleanField, NullBooleanField, DecimalField, Field
285  
-                values = []
286  
-                for value, field in map(None, row, fields):
287  
-                    if isinstance(value, Database.LOB):
288  
-                        value = value.read()
289  
-                    # Oracle stores empty strings as null. We need to undo this in
290  
-                    # order to adhere to the Django convention of using the empty
291  
-                    # string instead of null, but only if the field accepts the
292  
-                    # empty string.
293  
-                    if value is None and isinstance(field, Field) and field.empty_strings_allowed:
294  
-                        value = u''
295  
-                    # Convert 1 or 0 to True or False
296  
-                    elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)):
297  
-                        value = bool(value)
298  
-                    # Convert floats to decimals
299  
-                    elif value is not None and isinstance(field, DecimalField):
300  
-                        value = util.typecast_decimal(field.format_number(value))
301  
-                    # cx_Oracle always returns datetime.datetime objects for
302  
-                    # DATE and TIMESTAMP columns, but Django wants to see a
303  
-                    # python datetime.date, .time, or .datetime.  We use the type
304  
-                    # of the Field to determine which to cast to, but it's not
305  
-                    # always available.
306  
-                    # As a workaround, we cast to date if all the time-related
307  
-                    # values are 0, or to time if the date is 1/1/1900.
308  
-                    # This could be cleaned a bit by adding a method to the Field
309  
-                    # classes to normalize values from the database (the to_python
310  
-                    # method is used for validation and isn't what we want here).
311  
-                    elif isinstance(value, Database.Timestamp):
312  
-                        # In Python 2.3, the cx_Oracle driver returns its own
313  
-                        # Timestamp object that we must convert to a datetime class.
314  
-                        if not isinstance(value, datetime.datetime):
315  
-                            value = datetime.datetime(value.year, value.month, value.day, value.hour,
316  
-                                                      value.minute, value.second, value.fsecond)
317  
-                        if isinstance(field, DateTimeField):
318  
-                            pass  # DateTimeField subclasses DateField so must be checked first.
319  
-                        elif isinstance(field, DateField):
320  
-                            value = value.date()
321  
-                        elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1):
322  
-                            value = value.time()
323  
-                        elif value.hour == value.minute == value.second == value.microsecond == 0:
324  
-                            value = value.date()
325  
-                    values.append(value)
326  
-                return values
327  
-
328  
-        return OracleQuerySet
  102
+    def query_class(self, DefaultQueryClass):
  103
+        return query.query_class(DefaultQueryClass, Database)
329 104
 
330 105
     def quote_name(self, name):
331 106
         # SQL92 requires delimited (quoted) names to be case-sensitive.  When
@@ -339,6 +114,23 @@ def quote_name(self, name):
339 114
     def random_function_sql(self):
340 115
         return "DBMS_RANDOM.RANDOM"
341 116
 
  117
+    def regex_lookup_9(self, lookup_type):
  118
+        raise NotImplementedError("Regexes are not supported in Oracle before version 10g.")
  119
+
  120
+    def regex_lookup_10(self, lookup_type):
  121
+        if lookup_type == 'regex':
  122
+            match_option = "'c'"
  123
+        else:
  124
+            match_option = "'i'"
  125
+        return 'REGEXP_LIKE(%%s, %%s, %s)' % match_option
  126
+
  127
+    def regex_lookup(self, lookup_type):
  128
+        # If regex_lookup is called before it's been initialized, then create
  129
+        # a cursor to initialize it and recur.
  130
+        from django.db import connection
  131
+        connection.cursor()
  132
+        return connection.ops.regex_lookup(lookup_type)
  133
+
342 134
     def sql_flush(self, style, tables, sequences):
343 135
         # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
344 136
         # 'TRUNCATE z;'... style SQL statements
@@ -430,6 +222,14 @@ def _cursor(self, settings):
430 222
                            "NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
431 223
             try:
432 224
                 self.oracle_version = int(self.connection.version.split('.')[0])
  225
+                # There's no way for the DatabaseOperations class to know the
  226
+                # currently active Oracle version, so we do some setups here.
  227
+                # TODO: Multi-db support will need a better solution (a way to
  228
+                # communicate the current version).
  229
+                if self.oracle_version <= 9:
  230
+                    self.ops.regex_lookup = self.ops.regex_lookup_9
  231
+                else:
  232
+                    self.ops.regex_lookup = self.ops.regex_lookup_10
433 233
             except ValueError:
434 234
                 pass
435 235
             try:
151  django/db/backends/oracle/query.py
... ...
@@ -0,0 +1,151 @@
  1
+"""
  2
+Custom Query class for this backend (a derivative of
  3
+django.db.models.sql.query.Query).
  4
+"""
  5
+
  6
+import datetime
  7
+
  8
+from django.db.backends import util
  9
+
  10
+# Cache. Maps default query class to new Oracle query class.
  11
+_classes = {}
  12
+
  13
+def query_class(QueryClass, Database):
  14
+    """
  15
+    Returns a custom djang.db.models.sql.query.Query subclass that is
  16
+    appropraite for Oracle.
  17
+
  18
+    The 'Database' module (cx_Oracle) is passed in here so that all the setup
  19
+    required to import it only needs to be done by the calling module.
  20
+    """
  21
+    global _classes
  22
+    try:
  23
+        return _classes[QueryClass]
  24
+    except KeyError:
  25
+        pass
  26
+
  27
+    class OracleQuery(QueryClass):
  28
+        def resolve_columns(self, row, fields=()):
  29
+            index_start = len(self.extra_select.keys())
  30
+            values = [self.convert_values(v, None) for v in row[:index_start]]
  31
+            for value, field in map(None, row[index_start:], fields):
  32
+                values.append(self.convert_values(value, field))
  33
+            return values
  34
+
  35
+        def convert_values(self, value, field):
  36
+            from django.db.models.fields import DateField, DateTimeField, \
  37
+                 TimeField, BooleanField, NullBooleanField, DecimalField, Field
  38
+            if isinstance(value, Database.LOB):
  39
+                value = value.read()
  40
+            # Oracle stores empty strings as null. We need to undo this in
  41
+            # order to adhere to the Django convention of using the empty
  42
+            # string instead of null, but only if the field accepts the
  43
+            # empty string.
  44
+            if value is None and isinstance(field, Field) and field.empty_strings_allowed:
  45
+                value = u''
  46
+            # Convert 1 or 0 to True or False
  47
+            elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)):
  48
+                value = bool(value)
  49
+            # Convert floats to decimals
  50
+            elif value is not None and isinstance(field, DecimalField):
  51
+                value = util.typecast_decimal(field.format_number(value))
  52
+            # cx_Oracle always returns datetime.datetime objects for
  53
+            # DATE and TIMESTAMP columns, but Django wants to see a
  54
+            # python datetime.date, .time, or .datetime.  We use the type
  55
+            # of the Field to determine which to cast to, but it's not
  56
+            # always available.
  57
+            # As a workaround, we cast to date if all the time-related
  58
+            # values are 0, or to time if the date is 1/1/1900.
  59
+            # This could be cleaned a bit by adding a method to the Field
  60
+            # classes to normalize values from the database (the to_python
  61
+            # method is used for validation and isn't what we want here).
  62
+            elif isinstance(value, Database.Timestamp):
  63
+                # In Python 2.3, the cx_Oracle driver returns its own
  64
+                # Timestamp object that we must convert to a datetime class.
  65
+                if not isinstance(value, datetime.datetime):
  66
+                    value = datetime.datetime(value.year, value.month,
  67
+                            value.day, value.hour, value.minute, value.second,
  68
+                            value.fsecond)
  69
+                if isinstance(field, DateTimeField):
  70
+                    # DateTimeField subclasses DateField so must be checked
  71
+                    # first.
  72
+                    pass
  73
+                elif isinstance(field, DateField):
  74
+                    value = value.date()
  75
+                elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1):
  76
+                    value = value.time()
  77
+                elif value.hour == value.minute == value.second == value.microsecond == 0:
  78
+                    value = value.date()
  79
+            return value
  80
+
  81
+        def as_sql(self, with_limits=True, with_col_aliases=False):
  82
+            """
  83
+            Creates the SQL for this query. Returns the SQL string and list
  84
+            of parameters.  This is overriden from the original Query class
  85
+            to accommodate Oracle's limit/offset SQL.
  86
+
  87
+            If 'with_limits' is False, any limit/offset information is not
  88
+            included in the query.
  89
+            """
  90
+            # The `do_offset` flag indicates whether we need to construct
  91
+            # the SQL needed to use limit/offset w/Oracle.
  92
+            do_offset = with_limits and (self.high_mark or self.low_mark)
  93
+
  94
+            # If no offsets, just return the result of the base class
  95
+            # `as_sql`.
  96
+            if not do_offset:
  97
+                return super(OracleQuery, self).as_sql(with_limits=False,
  98
+                        with_col_aliases=with_col_aliases)
  99
+
  100
+            # `get_columns` needs to be called before `get_ordering` to
  101
+            # populate `_select_alias`.
  102
+            self.pre_sql_setup()
  103
+            out_cols = self.get_columns()
  104
+            ordering = self.get_ordering()
  105
+
  106
+            # Getting the "ORDER BY" SQL for the ROW_NUMBER() result.
  107
+            if ordering:
  108
+                rn_orderby = ', '.join(ordering)
  109
+            else:
  110
+                # Oracle's ROW_NUMBER() function always requires an
  111
+                # order-by clause.  So we need to define a default
  112
+                # order-by, since none was provided.
  113
+                qn = self.quote_name_unless_alias
  114
+                opts = self.model._meta
  115
+                rn_orderby = '%s.%s' % (qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column))
  116
+
  117
+            # Getting the selection SQL and the params, which has the `rn`
  118
+            # extra selection SQL.
  119
+            self.extra_select['rn'] = 'ROW_NUMBER() OVER (ORDER BY %s )' % rn_orderby
  120
+            sql, params= super(OracleQuery, self).as_sql(with_limits=False,
  121
+                    with_col_aliases=True)
  122
+
  123
+            # Constructing the result SQL, using the initial select SQL
  124
+            # obtained above.
  125
+            result = ['SELECT * FROM (%s)' % sql]
  126
+
  127
+            # Place WHERE condition on `rn` for the desired range.
  128
+            result.append('WHERE rn > %d' % self.low_mark)
  129
+            if self.high_mark:
  130
+                result.append('AND rn <= %d' % self.high_mark)
  131
+
  132
+            # Returning the SQL w/params.
  133
+            return ' '.join(result), params
  134
+
  135
+        def set_limits(self, low=None, high=None):
  136
+            super(OracleQuery, self).set_limits(low, high)
  137
+
  138
+            # We need to select the row number for the LIMIT/OFFSET sql.
  139
+            # A placeholder is added to extra_select now, because as_sql is
  140
+            # too late to be modifying extra_select.  However, the actual sql
  141
+            # depends on the ordering, so that is generated in as_sql.
  142
+            self.extra_select['rn'] = '1'
  143
+
  144
+        def clear_limits(self):
  145
+            super(OracleQuery, self).clear_limits()
  146
+            if 'rn' in self.extra_select:
  147
+                del self.extra_select['rn']
  148
+
  149
+    _classes[QueryClass] = OracleQuery
  150
+    return OracleQuery
  151
+
3  django/db/backends/postgresql/operations.py
@@ -44,6 +44,9 @@ def last_insert_id(self, cursor, table_name, pk_name):
44 44
         cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
45 45
         return cursor.fetchone()[0]
46 46
 
  47
+    def no_limit_value(self):
  48
+        return None
  49
+
47 50
     def quote_name(self, name):
48 51
         if name.startswith('"') and name.endswith('"'):
49 52
             return name # Quoting once is enough.
3  django/db/backends/sqlite3/base.py
@@ -63,6 +63,9 @@ def quote_name(self, name):
63 63
             return name # Quoting once is enough.
64 64
         return '"%s"' % name
65 65
 
  66
+    def no_limit_value(self):
  67
+        return -1
  68
+
66 69
     def sql_flush(self, style, tables, sequences):
67 70
         # NB: The generated SQL below is specific to SQLite
68 71
         # Note: The DELETE FROM... SQL generated below works for SQLite databases
381  django/db/models/base.py
... ...
@@ -1,10 +1,16 @@
1  
-import django.db.models.manipulators
2  
-import django.db.models.manager
  1
+import copy
  2
+import types
  3
+import sys
  4
+import os
  5
+from itertools import izip
  6
+
  7
+import django.db.models.manipulators    # Imported to register signal handler.
  8
+import django.db.models.manager         # Ditto.
3 9
 from django.core import validators
4  
-from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
  10
+from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError
5 11
 from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
6  
-from django.db.models.fields.related import OneToOneRel, ManyToOneRel
7  
-from django.db.models.query import delete_objects
  12
+from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField
  13
+from django.db.models.query import delete_objects, Q
8 14
 from django.db.models.options import Options, AdminOptions
9 15
 from django.db import connection, transaction
10 16
 from django.db.models import signals
@@ -14,10 +20,11 @@
14 20
 from django.utils.functional import curry
15 21
 from django.utils.encoding import smart_str, force_unicode, smart_unicode
16 22
 from django.conf import settings
17  
-from itertools import izip
18  
-import types
19  
-import sys
20  
-import os
  23
+
  24
+try:
  25
+    set
  26
+except NameError:
  27
+    from sets import Set as set     # Python 2.3 fallback
21 28
 
22 29
 class ModelBase(type):
23 30
     "Metaclass for all models"
@@ -25,29 +32,45 @@ def __new__(cls, name, bases, attrs):
25 32
         # If this isn't a subclass of Model, don't do anything special.
26 33
         try:
27 34
             parents = [b for b in bases if issubclass(b, Model)]
28  
-            if not parents:
29  
-                return super(ModelBase, cls).__new__(cls, name, bases, attrs)
30 35
         except NameError:
31 36
             # 'Model' isn't defined yet, meaning we're looking at Django's own
32 37
             # Model class, defined below.
  38
+            parents = []
  39
+        if not parents:
33 40
             return super(ModelBase, cls).__new__(cls, name, bases, attrs)
34 41
 
35 42
         # Create the class.
36  
-        new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')})
37  
-        new_class.add_to_class('_meta', Options(attrs.pop('Meta', None)))
38  
-        new_class.add_to_class('DoesNotExist', types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}))
39  
-        new_class.add_to_class('MultipleObjectsReturned',
40  
-            types.ClassType('MultipleObjectsReturned', (MultipleObjectsReturned, ), {}))
41  
-
42  
-        # Build complete list of parents
43  
-        for base in parents:
44  
-            # Things without _meta aren't functional models, so they're
45  
-            # uninteresting parents.
46  
-            if hasattr(base, '_meta'):
47  
-                new_class._meta.parents.append(base)
48  
-                new_class._meta.parents.extend(base._meta.parents)
49  
-
50  
-
  43
+        module = attrs.pop('__module__')
  44
+        new_class = type.__new__(cls, name, bases, {'__module__': module})
  45
+        attr_meta = attrs.pop('Meta', None)
  46
+        abstract = getattr(attr_meta, 'abstract', False)
  47
+        if not attr_meta:
  48
+            meta = getattr(new_class, 'Meta', None)
  49
+        else:
  50
+            meta = attr_meta
  51
+        base_meta = getattr(new_class, '_meta', None)
  52
+
  53
+        new_class.add_to_class('_meta', Options(meta))
  54
+        if not abstract:
  55
+            new_class.add_to_class('DoesNotExist',
  56
+                    subclass_exception('DoesNotExist', ObjectDoesNotExist, module))
  57
+            new_class.add_to_class('MultipleObjectsReturned',
  58
+                    subclass_exception('MultipleObjectsReturned', MultipleObjectsReturned, module))
  59
+            if base_meta and not base_meta.abstract:
  60
+                # Non-abstract child classes inherit some attributes from their
  61
+                # non-abstract parent (unless an ABC comes before it in the
  62
+                # method resolution order).
  63
+                if not hasattr(meta, 'ordering'):
  64
+                    new_class._meta.ordering = base_meta.ordering
  65
+                if not hasattr(meta, 'get_latest_by'):
  66
+                    new_class._meta.get_latest_by = base_meta.get_latest_by
  67
+
  68
+        old_default_mgr = None
  69
+        if getattr(new_class, '_default_manager', None):
  70
+            # We have a parent who set the default manager.
  71
+            if new_class._default_manager.model._meta.abstract:
  72
+                old_default_mgr = new_class._default_manager
  73
+            new_class._default_manager = None
51 74
         if getattr(new_class._meta, 'app_label', None) is None:
52 75
             # Figure out the app_label by looking one level up.
53 76
             # For 'django.contrib.sites.models', this would be 'sites'.
@@ -63,21 +86,50 @@ def __new__(cls, name, bases, attrs):
63 86
         for obj_name, obj in attrs.items():
64 87
             new_class.add_to_class(obj_name, obj)
65 88
 
66  
-        # Add Fields inherited from parents
67  
-        for parent in new_class._meta.parents:
68  
-            for field in parent._meta.fields:
69  
-                # Only add parent fields if they aren't defined for this class.
70  
-                try:
71  
-                    new_class._meta.get_field(field.name)
72  
-                except FieldDoesNotExist:
73  
-                    field.contribute_to_class(new_class, field.name)
74  
-
  89
+        # Do the appropriate setup for any model parents.
  90
+        o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
  91
+                if isinstance(f, OneToOneField)])
  92
+        for base in parents:
  93
+            if not hasattr(base, '_meta'):
  94
+                # Things without _meta aren't functional models, so they're
  95
+                # uninteresting parents.
  96
+                continue
  97
+            if not base._meta.abstract:
  98
+                if base in o2o_map: