Django

Code

root/django/branches/0.90-bugfixes/django/contrib/admin/views/main.py

Revision 1208, 62.1 kB (checked in by hugo, 3 years ago)

fixes #109 - added translation possibilities for date and time formats (and updated translation files for the new message IDs)

Line 
1 # Generic admin views, with admin templates created dynamically at runtime.
2
3 from django.contrib.admin.views.decorators import staff_member_required
4 from django.core import formfields, meta
5 from django.core.template import loader
6 from django.core.exceptions import Http404, ObjectDoesNotExist, PermissionDenied
7 from django.core.extensions import DjangoContext as Context
8 from django.core.extensions import get_object_or_404, render_to_response
9 from django.models.admin import log
10 from django.utils.html import strip_tags
11 from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
12 from 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
15 import operator
16
17 # Text to display within changelist table cells if the value is blank.
18 EMPTY_CHANGELIST_VALUE = '(None)'
19
20 def _get_mod_opts(app_label, module_name):
21     "Helper function that returns a tuple of (module, opts), raising Http404 if necessary."
22     try:
23         mod = meta.get_module(app_label, module_name)
24     except ImportError:
25         raise Http404 # Invalid app or module name. Maybe it's not in INSTALLED_APPS.
26     opts = mod.Klass._meta
27     if not opts.admin:
28         raise Http404 # This object is valid but has no admin interface.
29     return mod, opts
30
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&last_name=smith'
35     >>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'}, {'first_name': 'john'})
36     '?first_name=john&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):
44                 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 '?' + '&'.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     if opts.one_to_one_field:
83         lookup_mod = opts.one_to_one_field.rel.to.get_model_module()
84         lookup_opts = lookup_mod.Klass._meta
85         # If lookup_opts doesn't have admin set, give it the default meta.Admin().
86         if not lookup_opts.admin:
87             lookup_opts.admin = meta.Admin()
88
89     # Get search parameters from the query string.
90     try:
91         page_num = int(request.GET.get(PAGE_VAR, 0))
92     except ValueError:
93         page_num = 0
94     show_all = request.GET.has_key(ALL_VAR)
95     is_popup = request.GET.has_key(IS_POPUP_VAR)
96     params = dict(request.GET.copy())
97     if params.has_key(PAGE_VAR):
98         del params[PAGE_VAR]
99     # For ordering, first check the "ordering" parameter in the admin options,
100     # then check the object's default ordering. If neither of those exist,
101     # order descending by ID by default. Finally, look for manually-specified
102     # ordering from the query string.
103     ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
104
105     # Normalize it to new-style ordering.
106     ordering = meta.handle_legacy_orderlist(ordering)
107
108     if ordering[0].startswith('-'):
109         order_field, order_type = ordering[0][1:], 'desc'
110     else:
111         order_field, order_type = ordering[0], 'asc'
112     if params.has_key(ORDER_VAR):
113         try:
114             try:
115                 f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])])
116             except meta.FieldDoesNotExist:
117                 pass
118             else:
119                 if not isinstance(f.rel, meta.ManyToOne) or not f.null:
120                     order_field = f.name
121         except (IndexError, ValueError):
122             pass # Invalid ordering specified. Just use the default.
123     if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
124         order_type = params[ORDER_TYPE_VAR]
125     query = request.GET.get(SEARCH_VAR, '')
126
127     # Prepare the lookup parameters for the API lookup.
128     lookup_params = params.copy()
129     for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR):
130         if lookup_params.has_key(i):
131             del lookup_params[i]
132     # If the order-by field is a field with a relationship, order by the value
133     # in the related table.
134     lookup_order_field = order_field
135     if isinstance(lookup_opts.get_field(order_field).rel, meta.ManyToOne):
136         f = lookup_opts.get_field(order_field)
137         rel_ordering = f.rel.to.ordering and f.rel.to.ordering[0] or f.rel.to.pk.column
138         lookup_order_field = '%s.%s' % (f.rel.to.db_table, rel_ordering)
139     if lookup_opts.admin.list_select_related:
140         lookup_params['select_related'] = True
141     else:
142         # Use select_related if one of the list_display options is a field with
143         # a relationship.
144         for field_name in lookup_opts.admin.list_display:
145             try:
146                 f = lookup_opts.get_field(field_name)
147             except meta.FieldDoesNotExist:
148                 pass
149             else:
150                 if isinstance(f.rel, meta.ManyToOne):
151                     lookup_params['select_related'] = True
152                     break
153     lookup_params['order_by'] = ((order_type == 'desc' and '-' or '') + lookup_order_field,)
154     if lookup_opts.admin.search_fields and query:
155         or_queries = []
156         for bit in query.split():
157             or_query = []
158             for field_name in lookup_opts.admin.search_fields:
159                 or_query.append(('%s__icontains' % field_name, bit))
160             or_queries.append(or_query)
161         lookup_params['_or'] = or_queries
162
163     if opts.one_to_one_field:
164         lookup_params.update(opts.one_to_one_field.rel.limit_choices_to)
165
166     # Get the results.
167     try:
168         p = paginator.ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE)
169     # Naked except! Because we don't have any other way of validating "params".
170     # They might be invalid if the keyword arguments are incorrect, or if the
171     # values are not in the correct type (which would result in a database
172     # error).
173     except:
174         return HttpResponseRedirect(request.path)
175
176     # Get the total number of objects, with no filters applied.
177     real_lookup_params = lookup_params.copy()
178     del real_lookup_params['order_by']
179     if real_lookup_params:
180         full_result_count = lookup_mod.get_count()
181     else:
182         full_result_count = p.hits
183     del real_lookup_params
184     result_count = p.hits
185     can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
186     multi_page = result_count > DEFAULT_RESULTS_PER_PAGE
187
188     # Get the list of objects to display on this page.
189     if (show_all and can_show_all) or not multi_page:
190         result_list = lookup_mod.get_list(**lookup_params)
191     else:
192         try:
193             result_list = p.get_page(page_num)
194         except paginator.InvalidPage:
195             result_list = []
196
197     # Calculate filters first, because a CSS class high in the document depends
198     # on whether they are available.
199     filter_template = []
200     if lookup_opts.admin.list_filter and not opts.one_to_one_field:
201         filter_fields = [lookup_opts.get_field(field_name) for field_name in lookup_opts.admin.list_filter]
202         for f in filter_fields:
203             # Many-to-many or many-to-one filter.
204             if f.rel:
205                 if isinstance(f, meta.ManyToManyField):
206                     lookup_title = f.rel.to.verbose_name
207                 else:
208                     lookup_title = f.verbose_name
209                 lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name)
210                 lookup_val = request.GET.get(lookup_kwarg, None)
211                 lookup_choices = f.rel.to.get_model_module().get_list()
212                 if len(lookup_choices) > 1:
213                     filter_template.append('<h3>By %s:</h3>\n<ul>\n' % lookup_title)
214                     filter_template.append('<li%s><a href="%s">All</a></li>\n' % \
215                         ((lookup_val is None and ' class="selected"' or ''),
216                         get_query_string(params, {}, [lookup_kwarg])))
217                     for val in lookup_choices:
218                         pk_val = getattr(val, f.rel.to.pk.attname)
219                         filter_template.append('<li%s><a href="%s">%r</a></li>\n' % \
220                             ((lookup_val == str(pk_val) and ' class="selected"' or ''),
221                             get_query_string(params, {lookup_kwarg: pk_val}), val))
222                     filter_template.append('</ul>\n\n')
223             # Field with choices.
224             elif f.choices:
225                 lookup_kwarg = '%s__exact' % f.name
226                 lookup_val = request.GET.get(lookup_kwarg, None)
227                 filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name)
228                 filter_template.append('<li%s><a href="%s">All</a></li>\n' % \
229                     ((lookup_val is None and ' class="selected"' or ''),
230                     get_query_string(params, {}, [lookup_kwarg])))
231                 for k, v in f.choices:
232                     filter_template.append('<li%s><a href="%s">%s</a></li>' % \
233                         ((str(k) == lookup_val) and ' class="selected"' or '',
234                         get_query_string(params, {lookup_kwarg: k}), v))
235                 filter_template.append('</ul>\n\n')
236             # Date filter.
237             elif isinstance(f, meta.DateField):
238                 today = datetime.date.today()
239                 one_week_ago = today - datetime.timedelta(days=7)
240                 field_generic = '%s__' % f.name
241                 filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name)
242                 date_params = dict([(k, v) for k, v in params.items() if k.startswith(field_generic)])
243                 today_str = isinstance(f, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
244                 for title, param_dict in (
245                     ('Any date', {}),
246                     ('Today', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month), '%s__day' % f.name: str(today.day)}),
247                     ('Past 7 days', {'%s__gte' % f.name: one_week_ago.strftime('%Y-%m-%d'), '%s__lte' % f.name: today_str}),
248                     ('This month', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month)}),
249                     ('This year', {'%s__year' % f.name: str(today.year)})
250                 ):
251                     filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \
252                         ((date_params == param_dict) and ' class="selected"' or '',
253                         get_query_string(params, param_dict, field_generic), title))
254                 filter_template.append('</ul>\n\n')
255             elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField):
256                 lookup_kwarg = '%s__exact' % f.name
257                 lookup_kwarg2 = '%s__isnull' % f.name
258                 lookup_val = request.GET.get(lookup_kwarg, None)
259                 lookup_val2 = request.GET.get(lookup_kwarg2, None)
260                 filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name)
261                 for k, v in (('All', None), ('Yes', '1'), ('No', '0')):
262                     filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \
263                         (((lookup_val == v and not lookup_val2) and ' class="selected"' or ''),
264                         get_query_string(params, {lookup_kwarg: v}, [lookup_kwarg2]), k))
265                 if isinstance(f, meta.NullBooleanField):
266                     filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \
267                         (((lookup_val2 == 'True') and ' class="selected"' or ''),
268                         get_query_string(params, {lookup_kwarg2: 'True'}, [lookup_kwarg]), 'Unknown'))
269                 filter_template.append('</ul>\n\n')
270             else:
271                 pass # Invalid argument to "list_filter"
272
273     raw_template = ['{% extends "admin/base_site" %}\n']
274     raw_template.append('{% block bodyclass %}change-list{% endblock %}\n')
275     if not is_popup:
276         raw_template.append('{%% block breadcrumbs %%}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; %s</div>{%% endblock %%}\n' % capfirst(opts.verbose_name_plural))
277     raw_template.append('{% block coltype %}flex{% endblock %}')
278     raw_template.append('{% block content %}\n')
279     raw_template.append('<div id="content-main">\n')
280     if request.user.has_perm(app_label + '.' + lookup_opts.get_add_permission()):
281         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))
282     raw_template.append('<div class="module%s" id="changelist">\n' % (filter_template and ' filtered' or ''))
283
284     # Search form.
285     if lookup_opts.admin.search_fields:
286         raw_template.append('<div id="toolbar">\n<form id="changelist-search" action="" method="get">\n')
287         raw_template.append('<label><img src="%simg/admin/icon_searchbox.png" /></label> ' % ADMIN_MEDIA_PREFIX)
288         raw_template.append('<input type="text" size="40" name="%s" value="%s" id="searchbar" /> ' % \
289             (SEARCH_VAR, escape(query)))
290         raw_template.append('<input type="submit" value="Go" /> ')
291         if result_count != full_result_count and not opts.one_to_one_field:
292             raw_template.append('<span class="small quiet">%s result%s (<a href="?">%s total</a>)</span>' % \
293                 (result_count, (result_count != 1 and 's' or ''), full_result_count))
294         for k, v in params.items():
295             if k != SEARCH_VAR:
296                 raw_template.append('<input type="hidden" name="%s" value="%s" />' % (escape(k), escape(v)))
297         raw_template.append('</form></div>\n')
298         raw_template.append('<script type="text/javascript">document.getElementById("searchbar").focus();</script>')
299
300     # Date-based navigation.
301     if lookup_opts.admin.date_hierarchy:
302         field_name = lookup_opts.admin.date_hierarchy
303
304         year_field = '%s__year' % field_name
305         month_field = '%s__month' % field_name
306         day_field = '%s__day' % field_name
307         field_generic = '%s__' % field_name
308         year_lookup = params.get(year_field)
309         month_lookup = params.get(month_field)
310         day_lookup = params.get(day_field)
311
312         raw_template.append('<div class="xfull">\n<ul class="toplinks">\n')
313         if year_lookup and month_lookup and day_lookup:
314             raw_template.append('<li class="date-back"><a href="%s">&lsaquo; %s %s </a></li>' % \
315                 (get_query_string(params, {year_field: year_lookup, month_field: month_lookup}, [field_generic]), MONTHS[int(month_lookup)], year_lookup))
316             raw_template.append('<li>%s %s</li>' % (MONTHS[int(month_lookup)], day_lookup))
317         elif year_lookup and month_lookup:
318             raw_template.append('<li class="date-back"><a href="%s">&lsaquo; %s</a></li>' % \
319                 (get_query_string(params, {year_field: year_lookup}, [field_generic]), year_lookup))
320             date_lookup_params = lookup_params.copy()
321             date_lookup_params.update({year_field: year_lookup, month_field: month_lookup})
322             for day in getattr(lookup_mod, 'get_%s_list' % field_name)('day', **date_lookup_params):
323                 raw_template.append('<li><a href="%s">%s</a></li>' % \
324                     (get_query_string(params, {year_field: year_lookup, month_field: month_lookup, day_field: day.day}, [field_generic]), day.strftime('%B %d')))
325         elif year_lookup:
326             raw_template.append('<li class="date-back"><a href="%s">&lsaquo; All dates</a></li>' % \
327                 get_query_string(params, {}, [year_field]))
328             date_lookup_params = lookup_params.copy()
329             date_lookup_params.update({year_field: year_lookup})
330             for month in getattr(lookup_mod, 'get_%s_list' % field_name)('month', **date_lookup_params):
331                 raw_template.append('<li><a href="%s">%s %s</a></li>' % \
332                     (get_query_string(params, {year_field: year_lookup, month_field: month.month}, [field_generic]), month.strftime('%B'), month.year))
333         else:
334             for year in getattr(lookup_mod, 'get_%s_list' % field_name)('year', **lookup_params):
335                 raw_template.append('<li><a href="%s">%s</a></li>\n' % \
336                     (get_query_string(params, {year_field: year.year}, [field_generic]), year.year))
337         raw_template.append('</ul><br class="clear" />\n</div>\n')
338
339     # Filters.
340     if filter_template:
341         raw_template.append('<div id="changelist-filter">\n<h2>Filter</h2>\n')
342         raw_template.extend(filter_template)
343         raw_template.append('</div>')
344     del filter_template
345
346     # Result table.
347     if result_list:
348         # Table headers.
349         raw_template.append('<table cellspacing="0">\n<thead>\n<tr>\n')
350         for i, field_name in enumerate(lookup_opts.admin.list_display):
351             try:
352                 f = lookup_opts.get_field(field_name)
353             except meta.FieldDoesNotExist:
354                 # For non-field list_display values, check for the function
355                 # attribute "short_description". If that doesn't exist, fall
356                 # back to the method name. And __repr__ is a special-case.
357                 if field_name == '__repr__':
358                     header = lookup_opts.verbose_name
359                 else:
360                     func = getattr(mod.Klass, field_name) # Let AttributeErrors propogate.
361                     try:
362                         header = func.short_description
363                     except AttributeError:
364                         header = func.__name__
365                 # Non-field list_display values don't get ordering capability.
366                 raw_template.append('<th>%s</th>' % capfirst(header))
367             else:
368                 if isinstance(f.rel, meta.ManyToOne) and f.null:
369                     raw_template.append('<th>%s</th>' % capfirst(f.verbose_name))
370                 else:
371                     th_classes = []
372                     new_order_type = 'asc'
373                     if field_name == order_field:
374                         th_classes.append('sorted %sending' % order_type.lower())
375                         new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type.lower()]
376                     raw_template.append('<th%s><a href="%s">%s</a></th>' % \
377                         ((th_classes and ' class="%s"' % ' '.join(th_classes) or ''),
378                         get_query_string(params, {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
379                         capfirst(f.verbose_name)))
380         raw_template.append('</tr>\n</thead>\n')
381         # Result rows.
382         pk = lookup_opts.pk.attname
383         for i, result in enumerate(result_list):
384             raw_template.append('<tr class="row%s">\n' % (i % 2 + 1))
385             for j, field_name in enumerate(lookup_opts.admin.list_display):
386                 row_class = ''
387                 try:
388                     f = lookup_opts.</