Ticket #2076: ticket-2076.6.diff

File ticket-2076.6.diff, 13.2 KB (added by Ramiro Morales <rm0 _at_ gmx.net>, 8 years ago)

Same as ticket-2076.5.diff with corrections in the documentation and testcases comments

  • .5/django/core/management.py

    diff -rN -u old-ticket2076.5/django/core/management.py new-ticket2076.5/django/core/management.py
    old new  
    10331033                    field_name = field_name[1:]
    10341034                if opts.order_with_respect_to and field_name == '_order':
    10351035                    continue
    1036                 if '.' in field_name: continue # Skip ordering in the format 'table.field'.
     1036                if '.' in field_name: # Reject ordering in the format 'table.field'.
     1037                    e.add(opts, '"ordering" has "%s" which uses a format not supported anymore. Use field names and \'__\' instead.' % field_name)
     1038                if '__' in field_name: continue # Skip ordering in the format 'relation__related_model_field'.
    10371039                try:
    10381040                    opts.get_field(field_name, many_to_many=False)
    10391041                except models.FieldDoesNotExist:
  • .5/django/db/models/query.py

    diff -rN -u old-ticket2076.5/django/db/models/query.py new-ticket2076.5/django/db/models/query.py
    old new  
    471471        where.extend(where2)
    472472        params.extend(params2)
    473473
     474        # Resolve order_by dependencies before joins to order by related tables
     475        order_by = []
     476        if self._order_by is not None: # use order_by = () to disable ordering
     477            ordering_to_use = self._order_by
     478        else:
     479            ordering_to_use = opts.ordering
     480
     481        for f in handle_legacy_orderlist(ordering_to_use):
     482            if "." in f: # dot-style field, fall back to old ordering code below
     483                order_by = []
     484                break
     485            elif f == '?': # Special case.
     486                order_by.append(backend.get_random_function_sql())
     487                break
     488            elif f.startswith('-'):
     489                path = f[1:].split(LOOKUP_SEPARATOR)
     490                order = "DESC"
     491            else:
     492                path = f.split(LOOKUP_SEPARATOR)
     493                order = "ASC"
     494
     495            joins3, where3, params3 = lookup_inner(path, 'exact', False, opts, opts.db_table, None)
     496            joins.update(joins3)
     497
     498            # hack to get fieldname to order by. modify lookup_inner to supply this instead.
     499            field = where3[0].replace(' = %s', '')
     500            order_by.append('%s %s' % (field, order))
     501
    474502        # Add additional tables and WHERE clauses based on select_related.
    475503        if self._select_related:
    476504            fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table])
     
    496524            sql.append(where and "WHERE " + " AND ".join(where))
    497525
    498526        # ORDER BY clause
    499         order_by = []
    500527        if self._order_by is not None:
    501528            ordering_to_use = self._order_by
    502529        else:
    503530            ordering_to_use = opts.ordering
     531        if order_by: # dont try to set up ordering again if already done
     532            ordering_to_use = []
    504533        for f in handle_legacy_orderlist(ordering_to_use):
    505             if f == '?': # Special case.
    506                 order_by.append(backend.get_random_function_sql())
     534            if f.startswith('-'):
     535                col_name = f[1:]
     536                order = "DESC"
     537            else:
     538                col_name = f
     539                order = "ASC"
     540            # Use the database table as a column prefix if it wasn't given,
     541            # and if the requested column isn't a custom SELECT.
     542            if col_name not in (self._select or ()):
     543                table_prefix = backend.quote_name(opts.db_table) + '.'
    507544            else:
    508                 if f.startswith('-'):
    509                     col_name = f[1:]
    510                     order = "DESC"
    511                 else:
    512                     col_name = f
    513                     order = "ASC"
    514                 if "." in col_name:
    515                     table_prefix, col_name = col_name.split('.', 1)
    516                     table_prefix = backend.quote_name(table_prefix) + '.'
    517                 else:
    518                     # Use the database table as a column prefix if it wasn't given,
    519                     # and if the requested column isn't a custom SELECT.
    520                     if "." not in col_name and col_name not in (self._select or ()):
    521                         table_prefix = backend.quote_name(opts.db_table) + '.'
    522                     else:
    523                         table_prefix = ''
    524                 order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order))
     545                table_prefix = ''
     546            order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order))
    525547        if order_by:
    526548            sql.append("ORDER BY " + ", ".join(order_by))
    527549
  • .5/docs/db-api.txt

    diff -rN -u old-ticket2076.5/docs/db-api.txt new-ticket2076.5/docs/db-api.txt
    old new  
    420420
    421421    Entry.objects.order_by('?')
    422422
    423 To order by a field in a different table, add the other table's name and a dot,
    424 like so::
     423**New in Django development version:** To order by a field in a related model,
     424use the relationship name and the field name separated by ``__`` (that's a
     425double-underscore) using a notation that resembles the notation used in
     426`Field lookups`_::
    425427
    426     Entry.objects.order_by('blogs_blog.name', 'headline')
     428    Entry.objects.order_by('blog__name', 'headline')
     429
     430This can also span more than one relationships, both many-to-one and
     431many-to-many and both forward and backwards::
     432
     433    Blog.objects.order_by('entry__authors__name')
    427434
    428435There's no way to specify whether ordering should be case sensitive. With
    429436respect to case-sensitivity, Django will order results however your database
  • .5/tests/modeltests/ordering/models.py

    diff -rN -u old-ticket2076.5/tests/modeltests/ordering/models.py new-ticket2076.5/tests/modeltests/ordering/models.py
    old new  
    1111
    1212The ordering attribute is not required. If you leave it off, ordering will be
    1313undefined -- not random, just undefined.
     14
     15This also applies for models that have a relationship to another model, you
     16can use the name of the related field just like you'd any other field to
     17specify ordering of the model instances in the ``Meta.ordering`` attribute or
     18the ``order_by`` queryset method.
     19
     20Another feature is that you can even order by a field of the related model,
     21for this you use a notation similar to the one used in field lookup, that is
     22``relationname__fieldname`` (that's a double-underscore).
    1423"""
    1524
    1625from django.db import models
     
    2433    def __str__(self):
    2534        return self.headline
    2635
     36class ReaderLetter(models.Model):
     37    article = models.ForeignKey(Article, null=True)
     38    reader_name = models.CharField(maxlength=100)
     39    date_sent = models.DateTimeField()
     40    class Meta:
     41        ordering = ('reader_name',)
     42
     43    def __str__(self):
     44        if self.article is None:
     45            return self.reader_name
     46        else:
     47            return "%s, %s" % (self.reader_name, self.article)
     48
     49class EditorResponse(models.Model):
     50    reader_letter = models.ForeignKey(ReaderLetter)
     51    editor_name = models.CharField(maxlength=100)
     52    class Meta:
     53        ordering = ('editor_name',)
     54
     55    def __str__(self):
     56        return "%s, %s" % (self.editor_name, self.reader_letter)
     57
     58class Reporter(models.Model):
     59    name = models.CharField(maxlength=30)
     60    articles = models.ManyToManyField(Article)
     61    class Meta:
     62        ordering = ('articles__headline',)
     63
     64    def __str__(self):
     65        r = "%s" % (self.name)
     66        if self.articles.count():
     67            r += '; articles: %s' % ', '.join([str(a) for a in self.articles.all()])
     68        return r
     69
    2770__test__ = {'API_TESTS':"""
    2871# Create a couple of Articles.
    2972>>> from datetime import datetime
     
    64107# don't know what order the output will be in.
    65108>>> Article.objects.order_by('?')
    66109[...]
     110
     111# Create some reader letters.
     112# First the ones sent as comments about some article.
     113>>> rl1 = ReaderLetter.objects.create(reader_name='Reader D', article=a1, date_sent=(datetime(2005, 8, 5)))
     114>>> rl2 = ReaderLetter.objects.create(reader_name='Reader D', article=a2, date_sent=(datetime(2005, 8, 4)))
     115>>> rl3 = ReaderLetter.objects.create(reader_name='Reader C', article=a4, date_sent=(datetime(2005, 8, 3)))
     116>>> rl4 = ReaderLetter.objects.create(reader_name='Reader B', article=a4, date_sent=(datetime(2005, 8, 2)))
     117>>> rl5 = ReaderLetter.objects.create(reader_name='Reader A', article=None, date_sent=(datetime(2005, 8, 1)))
     118
     119# By default ReaderLetter.objects.all() orders by the name
     120# of the reader that wrote it ascending.
     121>>> ReaderLetter.objects.all()
     122[<ReaderLetter: Reader A>, <ReaderLetter: Reader B, Article 4>, <ReaderLetter: Reader C, Article 4>, <ReaderLetter: Reader D, Article 1>, <ReaderLetter: Reader D, Article 2>]
     123
     124# Composite ordering using the related article headline descending as one of the components
     125>>> ReaderLetter.objects.order_by('reader_name', '-article__headline')
     126[<ReaderLetter: Reader B, Article 4>, <ReaderLetter: Reader C, Article 4>, <ReaderLetter: Reader D, Article 2>, <ReaderLetter: Reader D, Article 1>]
     127
     128>>> er1 = EditorResponse.objects.create(editor_name='Editor A', reader_letter=rl1)
     129>>> er2 = EditorResponse.objects.create(editor_name='Editor B', reader_letter=rl2)
     130>>> er3 = EditorResponse.objects.create(editor_name='Editor B', reader_letter=rl3)
     131
     132>>> EditorResponse.objects.all()
     133[<EditorResponse: Editor A, Reader D, Article 1>, <EditorResponse: Editor B, Reader D, Article 2>, <EditorResponse: Editor B, Reader C, Article 4>]
     134
     135# Order just by the relationship field, this should order by the natural
     136# ordering of the ReaderLetter model (i.e. as dictated by its Meta.ordering),
     137# but right now is ordering by the ReaderLetter PK.
     138>>> EditorResponse.objects.order_by('reader_letter')
     139[<EditorResponse: Editor A, Reader D, Article 1>, <EditorResponse: Editor B, Reader D, Article 2>, <EditorResponse: Editor B, Reader C, Article 4>]
     140
     141# Order by another field of the related ReaderLetter object.
     142>>> EditorResponse.objects.order_by('reader_letter__reader_name')
     143[<EditorResponse: Editor B, Reader C, Article 4>, <EditorResponse: Editor A, Reader D, Article 1>, <EditorResponse: Editor B, Reader D, Article 2>]
     144
     145# The ordering specification can span models through relationships too.
     146# Order by the PK of the article related to the reader letter related
     147# to the editor response, descending.
     148>>> EditorResponse.objects.order_by('-reader_letter__article')
     149[<EditorResponse: Editor B, Reader C, Article 4>, <EditorResponse: Editor B, Reader D, Article 2>, <EditorResponse: Editor A, Reader D, Article 1>]
     150
     151# Order by the article publication date, ascending.
     152>>> EditorResponse.objects.order_by('reader_letter__article__pub_date')
     153[<EditorResponse: Editor A, Reader D, Article 1>, <EditorResponse: Editor B, Reader D, Article 2>, <EditorResponse: Editor B, Reader C, Article 4>]
     154
     155# Composite ordering with one of the components being a specification that
     156# spans a FK relationship
     157>>> EditorResponse.objects.order_by('-editor_name', 'reader_letter__article')
     158[<EditorResponse: Editor B, Reader D, Article 2>, <EditorResponse: Editor B, Reader C, Article 4>, <EditorResponse: Editor A, Reader D, Article 1>]
     159
     160# Test many-to-many relationships
     161>>> r1 = Reporter.objects.create(name='John')
     162>>> r2 = Reporter.objects.create(name='Jane')
     163>>> r3 = Reporter.objects.create(name='Paul')
     164
     165>>> r1.articles = [a1, a2]
     166>>> r2.articles = [a4, a3]
     167>>> r3.articles = [a4, a2]
     168
     169# The natural ordering of the Reporter model as dictated by its
     170# `Meta.ordering` option is by 'article__headline' (i.e. by a field of a
     171# related model)
     172>>> Reporter.objects.all().distinct()
     173[<Reporter: John; articles: Article 2, Article 1>, <Reporter: Paul; articles: Article 4, Article 2>, <Reporter: Jane; articles: Article 4, Article 3>]
     174
     175# Order by a a related model of a many-to-many relationship. Remember the
     176# Article model natural ordering as dictated by its `Meta.ordering`
     177# option is ('-pub_date', 'headline')
     178>>> Reporter.objects.distinct().order_by('articles')
     179[<Reporter: John; articles: Article 2, Article 1>, <Reporter: Paul; articles: Article 4, Article 2>, <Reporter: Jane; articles: Article 4, Article 3>]
     180
     181# Order the reporters by the headlines of the articles they contributed
     182# to, descending
     183>>> Reporter.objects.distinct().order_by('-articles__headline')
     184[<Reporter: Jane; articles: Article 4, Article 3>, <Reporter: Paul; articles: Article 4, Article 2>, <Reporter: John; articles: Article 2, Article 1>]
     185
     186# Note how the ordering can also span backward relationships. Here we order
     187# the reporters by the date the letter in response to the articles she/he
     188# contributed to was sent (if any)
     189>>> Reporter.objects.distinct().order_by('articles__readerletter__date_sent')
     190[<Reporter: Jane; articles: Article 4, Article 3>, <Reporter: Paul; articles: Article 4, Article 2>, <Reporter: John; articles: Article 2, Article 1>]
     191
    67192"""}
Back to Top