diff -r 000000000000 -r 0d40e90630ef web/lib/django/contrib/admin/views/main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/lib/django/contrib/admin/views/main.py Wed Jan 20 00:34:04 2010 +0100 @@ -0,0 +1,243 @@ +from django.contrib.admin.filterspecs import FilterSpec +from django.contrib.admin.options import IncorrectLookupParameters +from django.contrib.admin.util import quote +from django.core.paginator import Paginator, InvalidPage +from django.db import models +from django.db.models.query import QuerySet +from django.utils.encoding import force_unicode, smart_str +from django.utils.translation import ugettext +from django.utils.http import urlencode +import operator + +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback + +# The system will display a "Show all" link on the change list only if the +# total result count is less than or equal to this setting. +MAX_SHOW_ALL_ALLOWED = 200 + +# Changelist settings +ALL_VAR = 'all' +ORDER_VAR = 'o' +ORDER_TYPE_VAR = 'ot' +PAGE_VAR = 'p' +SEARCH_VAR = 'q' +TO_FIELD_VAR = 't' +IS_POPUP_VAR = 'pop' +ERROR_FLAG = 'e' + +# Text to display within change-list table cells if the value is blank. +EMPTY_CHANGELIST_VALUE = '(None)' + +class ChangeList(object): + def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, model_admin): + self.model = model + self.opts = model._meta + self.lookup_opts = self.opts + self.root_query_set = model_admin.queryset(request) + self.list_display = list_display + self.list_display_links = list_display_links + self.list_filter = list_filter + self.date_hierarchy = date_hierarchy + self.search_fields = search_fields + self.list_select_related = list_select_related + self.list_per_page = list_per_page + self.list_editable = list_editable + self.model_admin = model_admin + + # Get search parameters from the query string. + try: + self.page_num = int(request.GET.get(PAGE_VAR, 0)) + except ValueError: + self.page_num = 0 + self.show_all = ALL_VAR in request.GET + self.is_popup = IS_POPUP_VAR in request.GET + self.to_field = request.GET.get(TO_FIELD_VAR) + self.params = dict(request.GET.items()) + if PAGE_VAR in self.params: + del self.params[PAGE_VAR] + if TO_FIELD_VAR in self.params: + del self.params[TO_FIELD_VAR] + if ERROR_FLAG in self.params: + del self.params[ERROR_FLAG] + + self.order_field, self.order_type = self.get_ordering() + self.query = request.GET.get(SEARCH_VAR, '') + self.query_set = self.get_query_set() + self.get_results(request) + self.title = (self.is_popup and ugettext('Select %s') % force_unicode(self.opts.verbose_name) or ugettext('Select %s to change') % force_unicode(self.opts.verbose_name)) + self.filter_specs, self.has_filters = self.get_filters(request) + self.pk_attname = self.lookup_opts.pk.attname + + def get_filters(self, request): + filter_specs = [] + if self.list_filter: + filter_fields = [self.lookup_opts.get_field(field_name) for field_name in self.list_filter] + for f in filter_fields: + spec = FilterSpec.create(f, request, self.params, self.model, self.model_admin) + if spec and spec.has_output(): + filter_specs.append(spec) + return filter_specs, bool(filter_specs) + + def get_query_string(self, new_params=None, remove=None): + if new_params is None: new_params = {} + if remove is None: remove = [] + p = self.params.copy() + for r in remove: + for k in p.keys(): + if k.startswith(r): + del p[k] + for k, v in new_params.items(): + if v is None: + if k in p: + del p[k] + else: + p[k] = v + return '?%s' % urlencode(p) + + def get_results(self, request): + paginator = Paginator(self.query_set, self.list_per_page) + # Get the number of objects, with admin filters applied. + result_count = paginator.count + + # Get the total number of objects, with no admin filters applied. + # 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 not self.query_set.query.where: + full_result_count = result_count + else: + full_result_count = self.root_query_set.count() + + can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED + multi_page = result_count > self.list_per_page + + # Get the list of objects to display on this page. + if (self.show_all and can_show_all) or not multi_page: + result_list = self.query_set._clone() + else: + try: + result_list = paginator.page(self.page_num+1).object_list + except InvalidPage: + result_list = () + + self.result_count = result_count + self.full_result_count = full_result_count + self.result_list = result_list + self.can_show_all = can_show_all + self.multi_page = multi_page + self.paginator = paginator + + 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. + ordering = self.model_admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name] + + if ordering[0].startswith('-'): + order_field, order_type = ordering[0][1:], 'desc' + else: + order_field, order_type = ordering[0], 'asc' + if ORDER_VAR in params: + try: + field_name = self.list_display[int(params[ORDER_VAR])] + try: + f = lookup_opts.get_field(field_name) + except models.FieldDoesNotExist: + # See whether field_name is a name of a non-field + # that allows sorting. + try: + if callable(field_name): + attr = field_name + elif hasattr(self.model_admin, field_name): + attr = getattr(self.model_admin, field_name) + else: + attr = getattr(self.model, field_name) + order_field = attr.admin_order_field + except AttributeError: + pass + else: + order_field = f.name + except (IndexError, ValueError): + pass # Invalid ordering specified. Just use the default. + if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'): + order_type = params[ORDER_TYPE_VAR] + return order_field, order_type + + def get_query_set(self): + qs = self.root_query_set + lookup_params = self.params.copy() # a dictionary of the query string + for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): + if i in lookup_params: + del lookup_params[i] + for key, value in lookup_params.items(): + if not isinstance(key, str): + # 'key' will be used as a keyword argument later, so Python + # requires it to be a string. + del lookup_params[key] + lookup_params[smart_str(key)] = value + + # if key ends with __in, split parameter into separate values + if key.endswith('__in'): + lookup_params[key] = value.split(',') + + # Apply lookup parameters from the query string. + try: + qs = qs.filter(**lookup_params) + # Naked except! Because we don't have any other way of validating "params". + # They might be invalid if the keyword arguments are incorrect, or if the + # values are not in the correct type, so we might get FieldError, ValueError, + # ValicationError, or ? from a custom field that raises yet something else + # when handed impossible data. + except: + raise IncorrectLookupParameters + + # Use select_related() if one of the list_display options is a field + # with a relationship and the provided queryset doesn't already have + # select_related defined. + if not qs.query.select_related: + if self.list_select_related: + qs = qs.select_related() + else: + for field_name in self.list_display: + try: + f = self.lookup_opts.get_field(field_name) + except models.FieldDoesNotExist: + pass + else: + if isinstance(f.rel, models.ManyToOneRel): + qs = qs.select_related() + break + + # Set ordering. + if self.order_field: + qs = qs.order_by('%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field)) + + # Apply keyword searches. + def construct_search(field_name): + if field_name.startswith('^'): + return "%s__istartswith" % field_name[1:] + elif field_name.startswith('='): + return "%s__iexact" % field_name[1:] + elif field_name.startswith('@'): + return "%s__search" % field_name[1:] + else: + return "%s__icontains" % field_name + + if self.search_fields and self.query: + for bit in self.query.split(): + or_queries = [models.Q(**{construct_search(str(field_name)): bit}) for field_name in self.search_fields] + qs = qs.filter(reduce(operator.or_, or_queries)) + for field_name in self.search_fields: + if '__' in field_name: + qs = qs.distinct() + break + + return qs + + def url_for_result(self, result): + return "%s/" % quote(getattr(result, self.pk_attname))