=== modified file 'django/contrib/admin/views/main.py'
|
|
|
|
| 8 | 8 | from django.core.paginator import ObjectPaginator, 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 handle_legacy_orderlist, QuerySet, LOOKUP_SEPARATOR |
| 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 |
| … |
… |
|
| 543 | 543 | "admin/object_history.html"], extra_context, context_instance=template.RequestContext(request)) |
| 544 | 544 | history = staff_member_required(never_cache(history)) |
| 545 | 545 | |
| | 546 | class FollowForeignKey(object): |
| | 547 | """ |
| | 548 | A class to represent a ForeignKey that spans multiple foreign keys |
| | 549 | over multiple model classes. |
| | 550 | """ |
| | 551 | def __init__(self, field, lookups): |
| | 552 | # Traverse the relationships to reach the final model class. |
| | 553 | for field_name in lookups[1:]: |
| | 554 | model = field.rel.to |
| | 555 | new_field = model._meta.get_field(field_name) |
| | 556 | if not new_field.rel: break |
| | 557 | field = new_field |
| | 558 | self.verbose_name = field.verbose_name |
| | 559 | self.rel = field.rel |
| | 560 | # Rejoin the lookups using the separator so that the |
| | 561 | # RelatedFilterSpec uses the correct lookup_kwarg. |
| | 562 | self.name = LOOKUP_SEPARATOR.join(lookups) |
| | 563 | |
| 546 | 564 | class ChangeList(object): |
| 547 | 565 | def __init__(self, request, model): |
| 548 | 566 | self.model = model |
| … |
… |
|
| 574 | 592 | def get_filters(self, request): |
| 575 | 593 | filter_specs = [] |
| 576 | 594 | if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field: |
| 577 | | filter_fields = [self.lookup_opts.get_field(field_name) \ |
| 578 | | for field_name in self.lookup_opts.admin.list_filter] |
| 579 | | for f in filter_fields: |
| | 595 | for field_name in self.lookup_opts.admin.list_filter: |
| | 596 | field_lookups = field_name.split(LOOKUP_SEPARATOR) |
| | 597 | f = self.lookup_opts.get_field(field_lookups[0]) |
| | 598 | # If the field name spans multiple models, wrap the field in a |
| | 599 | # FollowForeignKey. |
| | 600 | if len(field_lookups) > 1 and f.rel: |
| | 601 | f = FollowForeignKey(f, field_lookups) |
| 580 | 602 | spec = FilterSpec.create(f, request, self.params, self.model) |
| 581 | 603 | if spec and spec.has_output(): |
| 582 | 604 | filter_specs.append(spec) |
=== modified file 'django/core/management/validation.py'
|
|
|
|
| 21 | 21 | from django.db import models, connection |
| 22 | 22 | from django.db.models.loading import get_app_errors |
| 23 | 23 | from django.db.models.fields.related import RelatedObject |
| | 24 | from django.db.models.query import LOOKUP_SEPARATOR |
| 24 | 25 | |
| 25 | 26 | e = ModelErrorCollection(outfile) |
| 26 | 27 | |
| … |
… |
|
| 170 | 171 | e.add(opts, '"admin.list_filter", if given, must be set to a list or tuple.') |
| 171 | 172 | else: |
| 172 | 173 | for fn in opts.admin.list_filter: |
| | 174 | field_lookups = fn.split(LOOKUP_SEPARATOR) |
| 173 | 175 | try: |
| 174 | | f = opts.get_field(fn) |
| | 176 | f = opts.get_field(field_lookups[0]) |
| | 177 | if len(field_lookups) > 1: |
| | 178 | if not f.rel: |
| | 179 | e.add(opts, '"admin.list_filter" refers to %r which containts a non-related field.' % fn) |
| | 180 | for field_name in field_lookups[1:]: |
| | 181 | model = f.rel.to |
| | 182 | f = model._meta.get_field(field_name) |
| | 183 | if not f.rel: |
| | 184 | e.add(opts, '"admin.list_filter" refers to %r which containts a non-related field.' % fn) |
| 175 | 185 | except models.FieldDoesNotExist: |
| 176 | 186 | e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn) |
| 177 | 187 | # date_hierarchy |