Commit 9c52d56f authored by Malcolm Tredinnick's avatar Malcolm Tredinnick
Browse files

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
parent c91a30f0
Loading
Loading
Loading
Loading
+7 −11
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, Per
from django.core.paginator import QuerySetPaginator, InvalidPage
from django.shortcuts import get_object_or_404, render_to_response
from django.db import models
from django.db.models.query import handle_legacy_orderlist, QuerySet
from django.db.models.query import QuerySet
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.utils.html import escape
from django.utils.text import capfirst, get_text_list
@@ -627,7 +627,7 @@ class ChangeList(object):
        # Perform a slight optimization: Check to see whether any filters were
        # given. If not, use paginator.hits to calculate the number of objects,
        # because we've already done paginator.hits and the value is cached.
        if isinstance(self.query_set._filters, models.Q) and not self.query_set._filters.kwargs:
        if not self.query_set.query.where:
            full_result_count = result_count
        else:
            full_result_count = self.manager.count()
@@ -653,15 +653,12 @@ class ChangeList(object):

    def get_ordering(self):
        lookup_opts, params = self.lookup_opts, self.params
        # For ordering, first check the "ordering" parameter in the admin options,
        # then check the object's default ordering. If neither of those exist,
        # order descending by ID by default. Finally, look for manually-specified
        # ordering from the query string.
        # For ordering, first check the "ordering" parameter in the admin
        # options, then check the object's default ordering. If neither of
        # those exist, order descending by ID by default. Finally, look for
        # manually-specified ordering from the query string.
        ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]

        # Normalize it to new-style ordering.
        ordering = handle_legacy_orderlist(ordering)

        if ordering[0].startswith('-'):
            order_field, order_type = ordering[0][1:], 'desc'
        else:
@@ -753,8 +750,7 @@ class ChangeList(object):
            for bit in self.query.split():
                or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields]
                other_qs = QuerySet(self.model)
                if qs._select_related:
                    other_qs = other_qs.select_related()
                other_qs.dup_select_related(qs)
                other_qs = other_qs.filter(reduce(operator.or_, or_queries))
                qs = qs & other_qs

+5 −0
Original line number Diff line number Diff line
@@ -154,6 +154,11 @@ class GenericRelation(RelatedField, Field):
    def get_internal_type(self):
        return "ManyToManyField"

    def db_type(self):
        # Since we're simulating a ManyToManyField, in effect, best return the
        # same db_type as well.
        return None

class ReverseGenericRelatedObjectsDescriptor(object):
    """
    This class provides the functionality that makes the related-object
+5 −0
Original line number Diff line number Diff line
@@ -27,3 +27,8 @@ class MiddlewareNotUsed(Exception):
class ImproperlyConfigured(Exception):
    "Django is somehow improperly configured"
    pass

class FieldError(Exception):
    """Some kind of problem with a model field."""
    pass
+9 −15
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ def django_table_list(only_existing=False):
    for app in models.get_apps():
        for model in models.get_models(app):
            tables.append(model._meta.db_table)
            tables.extend([f.m2m_db_table() for f in model._meta.many_to_many])
            tables.extend([f.m2m_db_table() for f in model._meta.local_many_to_many])
    if only_existing:
        existing = table_list()
        tables = [t for t in tables if t in existing]
@@ -54,12 +54,12 @@ def sequence_list():

    for app in apps:
        for model in models.get_models(app):
            for f in model._meta.fields:
            for f in model._meta.local_fields:
                if isinstance(f, models.AutoField):
                    sequence_list.append({'table': model._meta.db_table, 'column': f.column})
                    break # Only one AutoField is allowed per model, so don't bother continuing.

            for f in model._meta.many_to_many:
            for f in model._meta.local_many_to_many:
                sequence_list.append({'table': f.m2m_db_table(), 'column': None})

    return sequence_list
@@ -149,7 +149,7 @@ def sql_delete(app, style):
        if cursor and table_name_converter(model._meta.db_table) in table_names:
            # The table exists, so it needs to be dropped
            opts = model._meta
            for f in opts.fields:
            for f in opts.local_fields:
                if f.rel and f.rel.to not in to_delete:
                    references_to_delete.setdefault(f.rel.to, []).append( (model, f) )

@@ -181,7 +181,7 @@ def sql_delete(app, style):
    # Output DROP TABLE statements for many-to-many tables.
    for model in app_models:
        opts = model._meta
        for f in opts.many_to_many:
        for f in opts.local_many_to_many:
            if isinstance(f.rel, generic.GenericRel):
                continue
            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()):
    pending_references = {}
    qn = connection.ops.quote_name
    inline_references = connection.features.inline_fk_references
    for f in opts.fields:
    for f in opts.local_fields:
        col_type = f.db_type()
        tablespace = f.db_tablespace or opts.db_tablespace
        if col_type is None:
@@ -294,14 +294,8 @@ def sql_model_create(model, style, known_models=set()):
            style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
            style.SQL_KEYWORD('NULL'))
    for field_constraints in opts.unique_together:
        constraint_output = [style.SQL_KEYWORD('UNIQUE')]
        constraint_output.append('(%s)' % \
        table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
            ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
        if opts.db_tablespace and connection.features.supports_tablespaces \
               and connection.features.autoindexes_primary_keys:
            constraint_output.append(connection.ops.tablespace_sql(
                opts.db_tablespace, inline=True))
        table_output.append(' '.join(constraint_output))

    full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
    for i, line in enumerate(table_output): # Combine and add commas.
@@ -359,7 +353,7 @@ def many_to_many_sql_for_model(model, style):
    final_output = []
    qn = connection.ops.quote_name
    inline_references = connection.features.inline_fk_references
    for f in opts.many_to_many:
    for f in opts.local_many_to_many:
        if not isinstance(f.rel, generic.GenericRel):
            tablespace = f.db_tablespace or opts.db_tablespace
            if tablespace and connection.features.supports_tablespaces and connection.features.autoindexes_primary_keys:
@@ -466,7 +460,7 @@ def sql_indexes_for_model(model, style):
    output = []

    qn = connection.ops.quote_name
    for f in model._meta.fields:
    for f in model._meta.local_fields:
        if f.db_index and not ((f.primary_key or f.unique) and connection.features.autoindexes_primary_keys):
            unique = f.unique and 'UNIQUE ' or ''
            tablespace = f.db_tablespace or model._meta.db_tablespace
+18 −12
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ def get_validation_errors(outfile, app=None):
        opts = cls._meta

        # Do field-specific validation.
        for f in opts.fields:
        for f in opts.local_fields:
            if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
                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)
            if f.name.endswith('_'):
@@ -69,8 +69,8 @@ def get_validation_errors(outfile, app=None):
                if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.max_length > 255:
                    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]])))

            # Check to see if the related field will clash with any
            # existing fields, m2m fields, m2m related objects or related objects
            # Check to see if the related field will clash with any existing
            # fields, m2m fields, m2m related objects or related objects
            if f.rel:
                if f.rel.to not in models.get_models():
                    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):
                        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))
                    if r.name == rel_query_name:
                        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))
                for r in rel_opts.many_to_many:
                for r in rel_opts.local_many_to_many:
                    if r.name == rel_name:
                        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))
                    if r.name == rel_query_name:
@@ -104,9 +104,10 @@ def get_validation_errors(outfile, app=None):
                        if r.get_accessor_name() == rel_query_name:
                            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))

        for i, f in enumerate(opts.many_to_many):
        for i, f in enumerate(opts.local_many_to_many):
            # Check to see if the related m2m field will clash with any
            # existing fields, m2m fields, m2m related objects or related objects
            # existing fields, m2m fields, m2m related objects or related
            # objects
            if f.rel.to not in models.get_models():
                e.add(opts, "'%s' has m2m relation with model %s, which has not been installed" % (f.name, f.rel.to))
                # 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):
            rel_opts = f.rel.to._meta
            rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
            rel_query_name = f.related_query_name()
            # If rel_name is none, there is no reverse accessor.
            # (This only occurs for symmetrical m2m relations to self).
            # If this is the case, there are no clashes to check for this field, as
            # there are no reverse descriptors for this field.
            # If rel_name is none, there is no reverse accessor (this only
            # occurs for symmetrical m2m relations to self). If this is the
            # case, there are no clashes to check for this field, as there are
            # no reverse descriptors for this field.
            if rel_name is not None:
                for r in rel_opts.fields:
                    if r.name == rel_name:
                        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))
                    if r.name == rel_query_name:
                        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))
                for r in rel_opts.many_to_many:
                for r in rel_opts.local_many_to_many:
                    if r.name == rel_name:
                        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))
                    if r.name == rel_query_name:
@@ -200,7 +201,10 @@ def get_validation_errors(outfile, app=None):
                    field_name = field_name[1:]
                if opts.order_with_respect_to and field_name == '_order':
                    continue
                if '.' in field_name: continue # Skip ordering in the format 'table.field'.
                # Skip ordering in the format field1__field2 (FIXME: checking
                # this format would be nice, but it's a little fiddly).
                if '_' in field_name:
                    continue
                try:
                    opts.get_field(field_name, many_to_many=False)
                except models.FieldDoesNotExist:
@@ -228,5 +232,7 @@ def get_validation_errors(outfile, app=None):
                else:
                    if isinstance(f.rel, models.ManyToManyRel):
                        e.add(opts, '"unique_together" refers to %s. ManyToManyFields are not supported in unique_together.' % f.name)
                    if f not in opts.local_fields:
                        e.add(opts, '"unique_together" refers to %s. This is not in the same model as the unique_together statement.' % f.name)

    return len(e.errors)
Loading