Django

Code

Ticket #2076: ticket-2076.6.diff

File ticket-2076.6.diff, 13.2 kB (added by Ramiro Morales <rm0 _at_ gmx.net>, 1 year ago)

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

  • old-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: 
  • old-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 
  • old-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 
  • old-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"""}