Django

Code

Changeset 1434

Show
Ignore:
Timestamp:
11/25/05 15:20:09 (2 years ago)
Author:
adrian
Message:

MERGED NEW-ADMIN BRANCH (except for po/mo files, which will come in a separate commit)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/bin/validate.py

    r1420 r1434  
    1717                "ManyToManyField %s should have 'rel' set to a ManyToMany instance." % f.name 
    1818    # Inline related objects. 
    19     for rel_opts, rel_field in opts.get_inline_related_objects(): 
    20         assert len([f for f in rel_opts.fields if f.core]) > 0, \ 
     19    for related in opts.get_followed_related_objects(): 
     20        assert len([f for f in related.opts.fields if f.core]) > 0, \ 
    2121            "At least one field in %s should have core=True, because it's being edited inline by %s." % \ 
    22             (rel_opts.object_name, opts.object_name) 
     22            (related.opts.object_name, opts.object_name) 
    2323    # All related objects. 
    2424    related_apps_seen = [] 
    25     for rel_opts, rel_field in opts.get_all_related_objects(): 
    26         if rel_opts in related_apps_seen: 
    27             assert rel_field.rel.related_name is not None, \ 
     25    for related in opts.get_all_related_objects(): 
     26        if related.opts in related_apps_seen: 
     27            assert related.field.rel.related_name is not None, \ 
    2828                "Relationship in field %s.%s needs to set 'related_name' because more than one" \ 
    2929                " %s object is referenced in %s." % \ 
    30                 (rel_opts.object_name, rel_field.name, opts.object_name, rel_opts.object_name) 
    31         related_apps_seen.append(rel_opts) 
     30                (related.opts.object_name, related.field.name, opts.object_name, rel_opts.object_name) 
     31        related_apps_seen.append(related.opts) 
    3232    # Etc. 
    3333    if opts.admin is not None: 
  • django/trunk/django/contrib/admin/templates/admin_doc/bookmarklets.html

    r948 r1434  
    11{% extends "admin/base_site" %} 
    22 
    3 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Bookmarklets</div>{% endblock %} 
     3{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; <a href="../">{% trans "Documentation" %}</a> &rsaquo; {% trans "Bookmarklets" %}</div>{% endblock %} 
    44 
    5 {% block title %}Documentation bookmarklets{% endblock %} 
     5{% block title %}{% trans "Documentation bookmarklets" %}{% endblock %} 
    66 
    77{% block content %} 
    88 
     9{% blocktrans %} 
    910<p class="help">To install bookmarklets, drag the link to your bookmarks 
    1011toolbar, or right-click the link and add it to your bookmarks. Now you can 
     
    1314as "internal" (talk to your system administrator if you aren't sure if 
    1415your computer is "internal").</p> 
     16{% endblocktrans %} 
    1517 
    1618<div id="content-main"> 
    17     <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('HEAD',location.href,false);x.send(null);try{view=x.getResponseHeader('x-view');}catch(e){alert('No view found for this page');return;}if(view=="undefined"){alert("No view found for this page");}document.location='{{ admin_url }}/doc/views/'+view+'/';})()">Documentation for this page</a></h3> 
    18     <p>Jumps you from any page to the documentation for the view that generates that page.</p> 
     19    <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('HEAD',location.href,false);x.send(null);try{view=x.getResponseHeader('x-view');}catch(e){alert('No view found for this page');return;}if(view=="undefined"){alert("No view found for this page");}document.location='{{ admin_url }}/doc/views/'+view+'/';})()">{% trans "Documentation for this page" %}</a></h3> 
     20    <p>{% trans "Jumps you from any page to the documentation for the view that generates that page." %}</p> 
    1921 
    20     <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{type=x.getResponseHeader('x-object-type');id=x.getResponseHeader('x-object-id');}catch(e){type='(none)';id='(none)';}d=document;b=d.body;e=d.createElement('div');e.id='xxxhhh';s=e.style;s.position='absolute';s.left='10px';s.top='10px';s.font='10px monospace';s.border='1px black solid';s.padding='4px';s.backgroundColor='#eee';e.appendChild(d.createTextNode('Type: '+type));e.appendChild(d.createElement('br'));e.appendChild(d.createTextNode('ID: '+id));e.appendChild(d.createElement('br'));l=d.createElement('a');l.href='#';l.onclick=function(){b.removeChild(e);};l.appendChild(d.createTextNode('[close]'));l.style.textDecoration='none';e.appendChild(l);b.appendChild(e);})();">Show object ID</a></h3> 
    21     <p>Shows the content-type and unique ID for pages that represent a single object.</p> 
     22    <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{type=x.getResponseHeader('x-object-type');id=x.getResponseHeader('x-object-id');}catch(e){type='(none)';id='(none)';}d=document;b=d.body;e=d.createElement('div');e.id='xxxhhh';s=e.style;s.position='absolute';s.left='10px';s.top='10px';s.font='10px monospace';s.border='1px black solid';s.padding='4px';s.backgroundColor='#eee';e.appendChild(d.createTextNode('Type: '+type));e.appendChild(d.createElement('br'));e.appendChild(d.createTextNode('ID: '+id));e.appendChild(d.createElement('br'));l=d.createElement('a');l.href='#';l.onclick=function(){b.removeChild(e);};l.appendChild(d.createTextNode('[close]'));l.style.textDecoration='none';e.appendChild(l);b.appendChild(e);})();">{% trans "Show object ID" %}</a></h3> 
     23    <p>{% trans "Shows the content-type and unique ID for pages that represent a single object." %}</p> 
    2224 
    23     <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){var x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){var x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{var type=x.getResponseHeader('x-object-type');var id=x.getResponseHeader('x-object-id');}catch(e){return;}document.location='{{ admun_url }}/'+type.split('.').join('/')+'/'+id+'/';})()">Edit this object (current window)</a></h3> 
    24     <p>Jumps to the admin page for pages that represent a single object.</p> 
     25    <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){var x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){var x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{var type=x.getResponseHeader('x-object-type');var id=x.getResponseHeader('x-object-id');}catch(e){return;}document.location='{{ admun_url }}/'+type.split('.').join('/')+'/'+id+'/';})()">{% trans "Edit this object (current window)" %}</a></h3> 
     26    <p>{% trans "Jumps to the admin page for pages that represent a single object." %}</p> 
    2527 
    26     <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){var x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){var x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{var type=x.getResponseHeader('x-object-type');var id=x.getResponseHeader('x-object-id');}catch(e){return;}window.open('{{ admun_url }}/'+type.split('.').join('/')+'/'+id+'/');})()">Edit this object (new window)</a></h3> 
    27     <p>As above, but opens the admin page in a new window.</p> 
     28    <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){var x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){var x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{var type=x.getResponseHeader('x-object-type');var id=x.getResponseHeader('x-object-id');}catch(e){return;}window.open('{{ admun_url }}/'+type.split('.').join('/')+'/'+id+'/');})()">{% trans "Edit this object (new window)" %}</a></h3> 
     29    <p>{% trans "As above, but opens the admin page in a new window." %}</p> 
    2830</div> 
    2931 
  • django/trunk/django/contrib/admin/views/main.py

    r1322 r1434  
    1 # Generic admin views, with admin templates created dynamically at runtime. 
    2  
     1# Generic admin views. 
    32from django.contrib.admin.views.decorators import staff_member_required 
    4 from django.core import formfields, meta 
     3from django.contrib.admin.filterspecs import FilterSpec 
     4from django.core import formfields, meta, template 
    55from django.core.template import loader 
     6from django.core.meta.fields import BoundField, BoundFieldLine, BoundFieldSet 
    67from django.core.exceptions import Http404, ObjectDoesNotExist, PermissionDenied 
    78from django.core.extensions import DjangoContext as Context 
    89from django.core.extensions import get_object_or_404, render_to_response 
     10from django.core.paginator import ObjectPaginator, InvalidPage 
     11from django.conf.settings import ADMIN_MEDIA_PREFIX 
    912from django.models.admin import log 
    1013from django.utils.html import strip_tags 
    1114from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect 
    1215from django.utils.text import capfirst, get_text_list 
    13 from django.conf.settings import ADMIN_MEDIA_PREFIX 
    14 from django.utils.translation import get_date_formats 
     16from django.utils import dateformat 
     17from django.utils.dates import MONTHS 
     18from django.utils.html import escape 
    1519import operator 
     20 
     21# The system will display a "Show all" link only if the total result count 
     22# is less than or equal to this setting. 
     23MAX_SHOW_ALL_ALLOWED = 200 
     24 
     25DEFAULT_RESULTS_PER_PAGE = 100 
     26 
     27ALL_VAR = 'all' 
     28ORDER_VAR = 'o' 
     29ORDER_TYPE_VAR = 'ot' 
     30PAGE_VAR = 'p' 
     31SEARCH_VAR = 'q' 
     32IS_POPUP_VAR = 'pop' 
    1633 
    1734# Text to display within changelist table cells if the value is blank. 
     
    2946    return mod, opts 
    3047 
    31 def get_query_string(original_params, new_params={}, remove=[]): 
    32     """ 
    33     >>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'}) 
    34     '?first_name=adrian&amp;last_name=smith' 
    35     >>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'}, {'first_name': 'john'}) 
    36     '?first_name=john&amp;last_name=smith' 
    37     >>> get_query_string({'test': 'yes'}, {'blah': 'no'}, ['te']) 
    38     '?blah=no' 
    39     """ 
    40     p = original_params.copy() 
    41     for r in remove: 
    42         for k in p.keys(): 
    43             if k.startswith(r): 
     48def index(request): 
     49    return render_to_response('admin/index', {'title': _('Site administration')}, context_instance=Context(request)) 
     50index = staff_member_required(index) 
     51 
     52class IncorrectLookupParameters(Exception): 
     53    pass 
     54 
     55class ChangeList(object): 
     56    def __init__(self, request, app_label, module_name): 
     57        self.get_modules_and_options(app_label, module_name, request) 
     58        self.get_search_parameters(request) 
     59        self.get_ordering() 
     60        self.query = request.GET.get(SEARCH_VAR,'') 
     61        self.get_lookup_params() 
     62        self.get_results(request) 
     63        self.title = (self.is_popup 
     64                      and _('Select %s') % self.opts.verbose_name 
     65                      or _('Select %s to change') % self.opts.verbose_name) 
     66        self.get_filters(request) 
     67        self.pk_attname = self.lookup_opts.pk.attname 
     68 
     69    def get_filters(self, request): 
     70        self.filter_specs = [] 
     71        if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field: 
     72            filter_fields = [self.lookup_opts.get_field(field_name) \ 
     73                              for field_name in self.lookup_opts.admin.list_filter] 
     74            for f in filter_fields: 
     75                spec = FilterSpec.create(f, request, self.params) 
     76                if spec.has_output(): 
     77                    self.filter_specs.append(spec) 
     78        self.has_filters = bool(self.filter_specs) 
     79 
     80    def get_query_string(self, new_params={}, remove=[]): 
     81        p = self.params.copy() 
     82        for r in remove: 
     83            for k in p.keys(): 
     84                if k.startswith(r): 
     85                    del p[k] 
     86        for k, v in new_params.items(): 
     87            if p.has_key(k) and v is None: 
    4488                del p[k] 
    45     for k, v in new_params.items(): 
    46         if p.has_key(k) and v is None: 
    47             del p[k] 
    48         elif v is not None: 
    49             p[k] = v 
    50     return '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20') 
    51  
    52 def index(request): 
    53     return render_to_response('admin/index', {'title': 'Site administration'}, context_instance=Context(request)) 
    54 index = staff_member_required(index) 
    55  
    56 def change_list(request, app_label, module_name): 
    57     from django.core import paginator 
    58     from django.utils import dateformat 
    59     from django.utils.dates import MONTHS 
    60     from django.utils.html import escape 
    61     import datetime 
    62  
    63     # The system will display a "Show all" link only if the total result count 
    64     # is less than or equal to this setting. 
    65     MAX_SHOW_ALL_ALLOWED = 200 
    66  
    67     DEFAULT_RESULTS_PER_PAGE = 100 
    68  
    69     ALL_VAR = 'all' 
    70     ORDER_VAR = 'o' 
    71     ORDER_TYPE_VAR = 'ot' 
    72     PAGE_VAR = 'p' 
    73     SEARCH_VAR = 'q' 
    74     IS_POPUP_VAR = 'pop' 
    75  
    76     mod, opts = _get_mod_opts(app_label, module_name) 
    77     if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): 
    78         raise PermissionDenied 
    79  
    80     lookup_mod, lookup_opts = mod, opts 
    81  
    82     # Get search parameters from the query string. 
    83     try: 
    84         page_num = int(request.GET.get(PAGE_VAR, 0)) 
    85     except ValueError: 
    86         page_num = 0 
    87     show_all = request.GET.has_key(ALL_VAR) 
    88     is_popup = request.GET.has_key(IS_POPUP_VAR) 
    89     params = dict(request.GET.copy()) 
    90     if params.has_key(PAGE_VAR): 
    91         del params[PAGE_VAR] 
    92     # For ordering, first check the "ordering" parameter in the admin options, 
    93     # then check the object's default ordering. If neither of those exist, 
    94     # order descending by ID by default. Finally, look for manually-specified 
    95     # ordering from the query string. 
    96     ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name] 
    97  
    98     # Normalize it to new-style ordering. 
    99     ordering = meta.handle_legacy_orderlist(ordering) 
    100  
    101     if ordering[0].startswith('-'): 
    102         order_field, order_type = ordering[0][1:], 'desc' 
    103     else: 
    104         order_field, order_type = ordering[0], 'asc' 
    105     if params.has_key(ORDER_VAR): 
     89            elif v is not None: 
     90                p[k] = v 
     91        return '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20') 
     92 
     93    def get_modules_and_options(self, app_label, module_name, request): 
     94        self.mod, self.opts = _get_mod_opts(app_label, module_name) 
     95        if not request.user.has_perm(app_label + '.' + self.opts.get_change_permission()): 
     96            raise PermissionDenied 
     97 
     98        self.lookup_mod, self.lookup_opts = self.mod, self.opts 
     99 
     100    def get_search_parameters(self, request): 
     101        # Get search parameters from the query string. 
    106102        try: 
     103            self.req_get = request.GET 
     104            self.page_num = int(request.GET.get(PAGE_VAR, 0)) 
     105        except ValueError: 
     106            self.page_num = 0 
     107        self.show_all = request.GET.has_key(ALL_VAR) 
     108        self.is_popup = request.GET.has_key(IS_POPUP_VAR) 
     109        self.params = dict(request.GET.copy()) 
     110        if self.params.has_key(PAGE_VAR): 
     111            del self.params[PAGE_VAR] 
     112 
     113    def get_results(self, request): 
     114        lookup_mod, lookup_params, show_all, page_num = \ 
     115            self.lookup_mod, self.lookup_params, self.show_all, self.page_num 
     116        # Get the results. 
     117        try: 
     118            paginator = ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE) 
     119        # Naked except! Because we don't have any other way of validating "params". 
     120        # They might be invalid if the keyword arguments are incorrect, or if the 
     121        # values are not in the correct type (which would result in a database 
     122        # error). 
     123        except: 
     124            raise IncorrectLookupParameters() 
     125 
     126        # Get the total number of objects, with no filters applied. 
     127        real_lookup_params = lookup_params.copy() 
     128        del real_lookup_params['order_by'] 
     129        if real_lookup_params: 
     130            full_result_count = lookup_mod.get_count() 
     131        else: 
     132            full_result_count = paginator.hits 
     133        del real_lookup_params 
     134        result_count = paginator.hits 
     135        can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED 
     136        multi_page = result_count > DEFAULT_RESULTS_PER_PAGE 
     137 
     138        # Get the list of objects to display on this page. 
     139        if (show_all and can_show_all) or not multi_page: 
     140            result_list = lookup_mod.get_list(**lookup_params) 
     141        else: 
    107142            try: 
    108                 f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])]) 
    109             except meta.FieldDoesNotExist: 
    110                 pass 
    111             else: 
    112                 if not isinstance(f.rel, meta.ManyToOne) or not f.null: 
    113                     order_field = f.name 
    114         except (IndexError, ValueError): 
    115             pass # Invalid ordering specified. Just use the default. 
    116     if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'): 
    117         order_type = params[ORDER_TYPE_VAR] 
    118     query = request.GET.get(SEARCH_VAR, '') 
    119  
    120     # Prepare the lookup parameters for the API lookup. 
    121     lookup_params = params.copy() 
    122     for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): 
    123         if lookup_params.has_key(i): 
    124             del lookup_params[i] 
    125     # If the order-by field is a field with a relationship, order by the value 
    126     # in the related table. 
    127     lookup_order_field = order_field 
    128     if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOne): 
    129         f = lookup_opts.get_field(order_field) 
    130         rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column 
    131         lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering) 
    132     if lookup_opts.admin.list_select_related: 
    133         lookup_params['select_related'] = True 
    134     else: 
    135         # Use select_related if one of the list_display options is a field with 
    136         # a relationship. 
    137         for field_name in lookup_opts.admin.list_display: 
     143                result_list = paginator.get_page(page_num) 
     144            except InvalidPage: 
     145                result_list = [] 
     146        (self.result_count, self.full_result_count, self.result_list, 
     147            self.can_show_all, self.multi_page, self.paginator) = (result_count, 
     148                  full_result_count, result_list, can_show_all, multi_page, paginator ) 
     149 
     150    def url_for_result(self, result): 
     151        return "%s/" % getattr(result, self.pk_attname) 
     152 
     153    def get_ordering(self): 
     154        lookup_opts, params = self.lookup_opts, self.params 
     155        # For ordering, first check the "ordering" parameter in the admin options, 
     156        # then check the object's default ordering. If neither of those exist, 
     157        # order descending by ID by default. Finally, look for manually-specified 
     158        # ordering from the query string. 
     159        ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name] 
     160 
     161        # Normalize it to new-style ordering. 
     162        ordering = meta.handle_legacy_orderlist(ordering) 
     163 
     164        if ordering[0].startswith('-'): 
     165            order_field, order_type = ordering[0][1:], 'desc' 
     166        else: 
     167            order_field, order_type = ordering[0], 'asc' 
     168        if params.has_key(ORDER_VAR): 
    138169            try: 
    139                 f = lookup_opts.get_field(field_name) 
    140             except meta.FieldDoesNotExist: 
    141                 pass 
    142             else: 
    143                 if isinstance(f.rel, meta.ManyToOne): 
    144                     lookup_params['select_related'] = True 
    145                     break 
    146     lookup_params['order_by'] = ((order_type == 'desc' and '-' or '') + lookup_order_field,) 
    147     if lookup_opts.admin.search_fields and query: 
    148         or_queries = [] 
    149         for bit in query.split(): 
    150             or_query = [] 
    151             for field_name in lookup_opts.admin.search_fields: 
    152                 or_query.append(('%s__icontains' % field_name, bit)) 
    153             or_queries.append(or_query) 
    154         lookup_params['_or'] = or_queries 
    155  
    156     if opts.one_to_one_field: 
    157         lookup_params.update(opts.one_to_one_field.rel.limit_choices_to) 
    158  
    159     # Get the results. 
    160     try: 
    161         p = paginator.ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE) 
    162     # Naked except! Because we don't have any other way of validating "params". 
    163     # They might be invalid if the keyword arguments are incorrect, or if the 
    164     # values are not in the correct type (which would result in a database 
    165     # error). 
    166     except: 
    167         return HttpResponseRedirect(request.path) 
    168  
    169     # Get the total number of objects, with no filters applied. 
    170     real_lookup_params = lookup_params.copy() 
    171     del real_lookup_params['order_by'] 
    172     if real_lookup_params: 
    173         full_result_count = lookup_mod.get_count() 
    174     else: 
    175         full_result_count = p.hits 
    176     del real_lookup_params 
    177     result_count = p.hits 
    178     can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED 
    179     multi_page = result_count > DEFAULT_RESULTS_PER_PAGE 
    180  
    181     # Get the list of objects to display on this page. 
    182     if (show_all and can_show_all) or not multi_page: 
    183         result_list = lookup_mod.get_list(**lookup_params) 
    184     else: 
     170                try: 
     171                    f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])]) 
     172                except meta.FieldDoesNotExist: 
     173                    pass 
     174                else: 
     175                    if not isinstance(f.rel, meta.ManyToOne) or not f.null: 
     176                        order_field = f.name 
     177            except (IndexError, ValueError): 
     178                pass # Invalid ordering specified. Just use the default. 
     179        if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'): 
     180            order_type = params[ORDER_TYPE_VAR] 
     181        self.order_field, self.order_type = order_field, order_type 
     182 
     183    def get_lookup_params(self): 
     184        # Prepare the lookup parameters for the API lookup. 
     185        (params, order_field, lookup_opts, order_type, opts, query) = \ 
     186           (self.params, self.order_field, self.lookup_opts, self.order_type, self.opts, self.query) 
     187 
     188        lookup_params = params.copy() 
     189        for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): 
     190            if lookup_params.has_key(i): 
     191                del lookup_params[i] 
     192        # If the order-by field is a field with a relationship, order by the value 
     193        # in the related table. 
     194        lookup_order_field = order_field 
    185195        try: 
    186             result_list = p.get_page(page_num) 
    187         except paginator.InvalidPage: 
    188             result_list = [] 
    189  
    190     # Calculate filters first, because a CSS class high in the document depends 
    191     # on whether they are available. 
    192     filter_template = [] 
    193     if lookup_opts.admin.list_filter and not opts.one_to_one_field: 
    194         filter_fields = [lookup_opts.get_field(field_name) for field_name in lookup_opts.admin.list_filter] 
    195         for f in filter_fields: 
    196             # Many-to-many or many-to-one filter. 
    197             if f.rel: 
    198                 if isinstance(f, meta.ManyToManyField): 
    199                     lookup_title = f.rel.to.verbose_name 
    200                 else: 
    201                     lookup_title = f.verbose_name 
    202                 lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name) 
    203                 lookup_val = request.GET.get(lookup_kwarg, None) 
    204                 lookup_choices = f.rel.to.get_model_module().get_list() 
    205                 if len(lookup_choices) > 1: 
    206                     filter_template.append('<h3>By %s:</h3>\n<ul>\n' % lookup_title) 
    207                     filter_template.append('<li%s><a href="%s">All</a></li>\n' % \ 
    208                         ((lookup_val is None and ' class="selected"' or ''), 
    209                         get_query_string(params, {}, [lookup_kwarg]))) 
    210                     for val in lookup_choices: 
    211                         pk_val = getattr(val, f.rel.to.pk.attname) 
    212                         filter_template.append('<li%s><a href="%s">%r</a></li>\n' % \ 
    213                             ((lookup_val == str(pk_val) and ' class="selected"' or ''), 
    214                             get_query_string(params, {lookup_kwarg: pk_val}), val)) 
    215                     filter_template.append('</ul>\n\n') 
    216             # Field with choices. 
    217             elif f.choices: 
    218                 lookup_kwarg = '%s__exact' % f.name 
    219                 lookup_val = request.GET.get(lookup_kwarg, None) 
    220                 filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name) 
    221                 filter_template.append('<li%s><a href="%s">All</a></li>\n' % \ 
    222                     ((lookup_val is None and ' class="selected"' or ''), 
    223                     get_query_string(params, {}, [lookup_kwarg]))) 
    224                 for k, v in f.choices: 
    225                     filter_template.append('<li%s><a href="%s">%s</a></li>' % \ 
    226                         ((str(k) == lookup_val) and ' class="selected"' or '', 
    227                         get_query_string(params, {lookup_kwarg: k}), v)) 
    228                 filter_template.append('</ul>\n\n') 
    229             # Date filter. 
    230             elif isinstance(f, meta.DateField): 
    231                 today = datetime.date.today() 
    232                 one_week_ago = today - datetime.timedelta(days=7) 
    233                 field_generic = '%s__' % f.name 
    234                 filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name) 
    235                 date_params = dict([(k, v) for k, v in params.items() if k.startswith(field_generic)]) 
    236                 today_str = isinstance(f, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d') 
    237                 for title, param_dict in ( 
    238                     ('Any date', {}), 
    239                     ('Today', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month), '%s__day' % f.name: str(today.day)}), 
    240                     ('Past 7 days', {'%s__gte' % f.name: one_week_ago.strftime('%Y-%m-%d'), '%s__lte' % f.name: today_str}), 
    241                     ('This month', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month)}), 
    242                     ('This year', {'%s__year' % f.name: str(today.year)}) 
    243                 ): 
    244                     filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \ 
    245                         ((date_params == param_dict) and ' class="selected"' or '', 
    246                         get_query_string(params, param_dict, field_generic), title)) 
    247                 filter_template.append('</ul>\n\n') 
    248             elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField): 
    249                 lookup_kwarg = '%s__exact' % f.name 
    250                 lookup_kwarg2 = '%s__isnull' % f.name 
    251                 lookup_val = request.GET.get(lookup_kwarg, None) 
    252                 lookup_val2 = request.GET.get(lookup_kwarg2, None) 
    253                 filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name) 
    254                 for k, v in (('All', None), ('Yes', '1'), ('No', '0')): 
    255                     filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \ 
    256                         (((lookup_val == v and not lookup_val2) and ' class="selected"' or ''), 
    257                         get_query_string(params, {lookup_kwarg: v}, [lookup_kwarg2]), k)) 
    258                 if isinstance(f, meta.NullBooleanField): 
    259                     filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \ 
    260                         (((lookup_val2 == 'True') and ' class="selected"' or ''), 
    261                         get_query_string(params, {lookup_kwarg2: 'True'}, [lookup_kwarg]), 'Unknown')) 
    262                 filter_template.append('</ul>\n\n') 
    263             else: 
    264                 pass # Invalid argument to "list_filter" 
    265  
    266     raw_template = ['{% extends "admin/base_site" %}\n'] 
    267     raw_template.append('{% block bodyclass %}change-list{% endblock %}\n') 
    268     if not is_popup: 
    269         raw_template.append('{%% block breadcrumbs %%}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; %s</div>{%% endblock %%}\n' % capfirst(opts.verbose_name_plural)) 
    270     raw_template.append('{% block coltype %}flex{% endblock %}') 
    271     raw_template.append('{% block content %}\n') 
    272     raw_template.append('<div id="content-main">\n') 
    273     if request.user.has_perm(app_label + '.' + lookup_opts.get_add_permission()): 
    274         raw_template.append('<ul class="object-tools"><li><a href="add/%s" class="addlink">Add %s</a></li></ul>\n' % ((is_popup and '?_popup=1' or ''), opts.verbose_name)) 
    275     raw_template.append('<div class="module%s" id="changelist">\n' % (filter_template and ' filtered' or '')) 
    276  
    277     # Search form. 
    278     if lookup_opts.admin.search_fields: 
    279         raw_template.append('<div id="toolbar">\n<form id="changelist-search" action="" method="get">\n') 
    280         raw_template.append('<label><img src="%simg/admin/icon_searchbox.png" /></label> ' % ADMIN_MEDIA_PREFIX) 
    281         raw_template.append('<input type="text" size="40" name="%s" value="%s" id="searchbar" /> ' % \ 
    282             (SEARCH_VAR, escape(query))) 
    283         raw_template.append('<input type="submit" value="Go" /> ') 
    284         if result_count != full_result_count and not opts.one_to_one_field: 
    285             raw_template.append('<span class="small quiet">%s result%s (<a href="?">%s total</a>)</span>' % \ 
    286                 (result_count, (result_count != 1 and 's' or ''), full_result_count)) 
    287         for k, v in params.items(): 
    288             if k != SEARCH_VAR: 
    289                 raw_template.append('<input type="hidden" name="%s" value="%s" />' % (escape(k), escape(v))) 
    290         raw_template.append('</form></div>\n') 
    291         raw_template.append('<script type="text/javascript">document.getElementById("searchbar").focus();</script>') 
    292  
    293     # Date-based navigation. 
    294     if lookup_opts.admin.date_hierarchy: 
    295         field_name = lookup_opts.admin.date_hierarchy 
    296  
    297         year_field = '%s__year' % field_name 
    298         month_field = '%s__month' % field_name 
    299         day_field = '%s__day' % field_name 
    300         field_generic = '%s__' % field_name 
    301         year_lookup = params.get(year_field) 
    302         month_lookup = params.get(month_field) 
    303         day_lookup = params.get(day_field) 
    304  
    305         raw_template.append('<div class="xfull">\n<ul class="toplinks">\n') 
    306         if year_lookup and month_lookup and day_lookup: 
    307             raw_template.append('<li class="date-back"><a href="%s">&lsaquo; %s %s </a></li>' % \ 
    308                 (get_query_string(params, {year_field: year_lookup, month_field: month_lookup}, [field_generic]), MONTHS[int(month_lookup)], year_lookup)) 
    309             raw_template.append('<li>%s %s</li>' % (MONTHS[int(month_lookup)], day_lookup)) 
    310         elif year_lookup and month_lookup: 
    311             raw_template.append('<li class="date-back"><a href="%s">&lsaquo; %s</a></li>' % \ 
    312                 (get_query_string(params, {year_field: year_lookup}, [field_generic]), year_lookup)) 
    313             date_lookup_params = lookup_params.copy() 
    314             date_lookup_params.update({year_field: year_lookup, month_field: month_lookup}) 
    315             for day in getattr(lookup_mod, 'get_%s_list' % field_name)('day', **date_lookup_params): 
    316                 raw_template.append('<li><a href="%s">%s</a></li>' % \ 
    317                     (get_query_string(params, {year_field: year_lookup, month_field: month_lookup, day_field: day.day}, [field_generic]), day.strftime('%B %d'))) 
    318         elif year_lookup: 
    319             raw_template.append('<li class="date-back"><a href="%s">&lsaquo; All dates</a></li>' % \ 
    320                 get_query_string(params, {}, [year_field])) 
    321             date_lookup_params = lookup_params.copy() 
    322             date_lookup_params.update({year_field: year_lookup}) 
    323             for month in getattr(lookup_mod, 'get_%s_list' % field_name)('month', **date_lookup_params): 
    324                 raw_template.append('<li><a href="%s">%s %s</a></li>' % \ 
    325                     (get_query_string(params, {year_field: year_lookup, month_field: month.month}, [field_generic]), month.strftime('%B'), month.year)) 
     196            f = lookup_opts.get_field(order_field) 
     197        except meta.FieldDoesNotExist: 
     198            pass 
    326199        else: 
    327             for year in getattr(lookup_mod, 'get_%s_list' % field_name)('year', **lookup_params): 
    328                 raw_template.append('<li><a href="%s">%s</a></li>\n' % \ 
    329                     (get_query_string(params, {year_field: year.year}, [field_generic]), year.year)) 
    330         raw_template.append('</ul><br class="clear" />\n</div>\n') 
    331  
    332     # Filters. 
    333     if filter_template: 
    334         raw_template.append('<div id="changelist-filter">\n<h2>Filter</h2>\n') 
    335         raw_template.extend(filter_template) 
    336         raw_template.append('</div>') 
    337     del filter_template 
    338  
    339     # Result table. 
    340     if result_list: 
    341         # Table headers. 
    342         raw_template.append('<table cellspacing="0">\n<thead>\n<tr>\n') 
    343         for i, field_name in enumerate(lookup_opts.admin.list_display): 
    344             try: 
    345                 f = lookup_opts.get_field(field_name) 
    346             except meta.FieldDoesNotExist: 
    347                 # For non-field list_display values, check for the function 
    348                 # attribute "short_description". If that doesn't exist, fall 
    349                 # back to the method name. And __repr__ is a special-case. 
    350                 if field_name == '__repr__': 
    351                     header = lookup_opts.verbose_name 
    352                 else: 
    353                     func = getattr(mod.Klass, field_name) # Let AttributeErrors propogate. 
    354                     try: 
    355                         header = func.short_description 
    356                     except AttributeError: 
    357                         header = func.__name__.replace('_', ' ') 
    358                 # Non-field list_display values don't get ordering capability. 
    359                 raw_template.append('<th>%s</th>' % capfirst(header)) 
    360             else: 
    361                 if isinstance(f.rel, meta.ManyToOne) and f.null: 
    362                     raw_template.append('<th>%s</th>' % capfirst(f.verbose_name)) 
    363                 else: 
    364                     th_classes = [] 
    365                     new_order_type = 'asc' 
    366                     if field_name == order_field: 
    367                         th_classes.append('sorted %sending' % order_type.lower()) 
    368                         new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type.lower()] 
    369                     raw_template.append('<th%s><a href="%s">%s</a></th>' % \ 
    370                         ((th_classes and ' class="%s"' % ' '.join(th_classes) or ''), 
    371                         get_query_string(params, {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), 
    372                         capfirst(f.verbose_name))) 
    373         raw_template.append('</tr>\n</thead>\n') 
    374         # Result rows. 
    375         pk = lookup_opts.pk.attname 
    376         for i, result in enumerate(result_list): 
    377             raw_template.append('<tr class="row%s">\n' % (i % 2 + 1)) 
    378             for j, field_name in enumerate(lookup_opts.admin.list_display): 
    379                 row_class = '' 
     200            if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOne): 
     201                f = lookup_opts.get_field(order_field) 
     202                rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column 
     203                lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering) 
     204        # Use select_related if one of the list_display options is a field with a 
     205        # relationship. 
     206        if lookup_opts.admin.list_select_related: 
     207            lookup_params['select_related'] = True 
     208        else: 
     209            for field_name in lookup_opts.admin.list_display: 
    380210                try: 
    381211                    f = lookup_opts.get_field(field_name) 
    382212                except meta.FieldDoesNotExist: 
    383                     # For non-field list_display values, the value is a method 
    384                     # name. Execute the method. 
    385                     func = getattr(result, field_name) 
    386                     try: 
    387                         result_repr = str(func()) 
    388                     except ObjectDoesNotExist: 
    389                         result_repr = EMPTY_CHANGELIST_VALUE 
    390                     else: 
    391                         # Strip HTML tags in the resulting text, except if the 
    392                         # function has an "allow_tags" attribute set to True. 
    393                         if not getattr(func, 'allow_tags', False): 
    394                             result_repr = strip_tags(result_repr) 
     213                    pass 
    395214                else: 
    396                     field_val = getattr(result, f.attname) 
    397                     # Foreign-key fields are special: Use the repr of the 
    398                     # related object. 
    399215                    if isinstance(f.rel, meta.ManyToOne): 
    400                         if field_val is not None: 
    401                             result_repr = getattr(result, 'get_%s' % f.name)() 
    402                         else: 
    403                             result_repr = EMPTY_CHANGELIST_VALUE 
    404                     # Dates and times are special: They're formatted in a certain way. 
    405                     elif isinstance(f, meta.DateField) or isinstance(f, meta.TimeField): 
    406                         if field_val: 
    407                             (date_format, datetime_format, time_format) = get_date_formats() 
    408                             if isinstance(f, meta.DateTimeField): 
    409                                 result_repr = capfirst(dateformat.format(field_val, datetime_format)) 
    410                             elif isinstance(f, meta.TimeField): 
    411                                 result_repr = capfirst(dateformat.time_format(field_val, time_format)) 
    412                             else: 
    413                                 result_repr = capfirst(dateformat.format(field_val, date_format)) 
    414                         else: 
    415                             result_repr = EMPTY_CHANGELIST_VALUE 
    416                         row_class = ' class="nowrap"' 
    417                     # Booleans are special: We use images. 
    418                     elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField): 
    419                         BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} 
    420                         result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val) 
    421                     # ImageFields are special: Use a thumbnail. 
    422                     elif isinstance(f, meta.ImageField): 
    423                         from django.parts.media.photos import get_thumbnail_url 
    424                         result_repr = '<img src="%s" alt="%s" title="%s" />' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val) 
    425                     # FloatFields are special: Zero-pad the decimals. 
    426                     elif isinstance(f, meta.FloatField): 
    427                         if field_val is not None: 
    428                             result_repr = ('%%.%sf' % f.decimal_places) % field_val 
    429                         else: 
    430                             result_repr = EMPTY_CHANGELIST_VALUE 
    431                     # Fields with choices are special: Use the representation 
    432                     # of the choice. 
    433                     elif f.choices: 
    434                         result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE) 
    435                     else: 
    436                         result_repr = strip_tags(str(field_val)) 
    437                 # Some browsers don't like empty "<td></td>"s. 
    438                 if result_repr == '': 
    439                     result_repr = '&nbsp;' 
    440                 if j == 0: # First column is a special case 
    441                     result_id = getattr(result, pk) 
    442                     raw_template.append('<th%s><a href="%s/"%s>%s</a></th>' % \ 
    443                         (row_class, result_id, (is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr)) 
    444                 else: 
    445                     raw_template.append('<td%s>%s</td>' % (row_class, result_repr)) 
    446             raw_template.append('</tr>\n') 
    447         del result_list # to free memory 
    448         raw_template.append('</table>\n') 
    449     else: 
    450         raw_template.append('<p>No %s matched your search criteria.</p>' % opts.verbose_name_plural) 
    451  
    452     # Pagination. 
    453     raw_template.append('<p class="paginator">') 
    454     if (show_all and can_show_all) or not multi_page: 
    455         pass 
    456     else: 
    457         raw_template.append('Page &rsaquo; ') 
    458         ON_EACH_SIDE = 3 
    459     &