mirror of
https://github.com/django/django.git
synced 2025-07-05 10:19:20 +00:00
Merged to 1021.
Initial split up of change lists to tags and templates. Needs work. Rationalised related object methods. Various bug fixes. A lot of changes here, so please let me know of any issues. Also I would appreciate examples of models with funky list views, eg searchs, pagination, filtering etc. git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@1023 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
commit
5e5f77f59b
@ -11,6 +11,7 @@ ACTION_MAPPING = {
|
||||
'init': management.init,
|
||||
'inspectdb': management.inspectdb,
|
||||
'install': management.install,
|
||||
'installperms': management.installperms,
|
||||
'runserver': management.runserver,
|
||||
'sql': management.get_sql_create,
|
||||
'sqlall': management.get_sql_all,
|
||||
@ -24,7 +25,7 @@ ACTION_MAPPING = {
|
||||
'validate': management.validate,
|
||||
}
|
||||
|
||||
NO_SQL_TRANSACTION = ('adminindex', 'createcachetable', 'dbcheck', 'install', 'sqlindexes')
|
||||
NO_SQL_TRANSACTION = ('adminindex', 'createcachetable', 'dbcheck', 'install', 'installperms', 'sqlindexes')
|
||||
|
||||
def get_usage():
|
||||
"""
|
||||
|
@ -65,8 +65,8 @@ TEMPLATE_FILE_EXTENSION = '.html'
|
||||
# See the comments in django/core/template/loader.py for interface
|
||||
# documentation.
|
||||
TEMPLATE_LOADERS = (
|
||||
# 'django.core.template.loaders.app_directories.load_template_source',
|
||||
'django.core.template.loaders.filesystem.load_template_source',
|
||||
'django.core.template.loaders.app_directories.load_template_source',
|
||||
# 'django.core.template.loaders.eggs.load_template_source',
|
||||
)
|
||||
|
||||
|
@ -9,7 +9,7 @@ function URLify(s, num_chars) {
|
||||
s = s.replace(r, '');
|
||||
s = s.replace(/[^\w\s-]/g, ''); // remove unneeded chars
|
||||
s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
|
||||
s = s.replace(/\s+/g, '_'); // convert spaces to underscores
|
||||
s = s.replace(/\s+/g, '-'); // convert spaces to hyphens
|
||||
s = s.toLowerCase(); // convert to lowercase
|
||||
return s.substring(0, num_chars);// trim to first num_chars chars
|
||||
}
|
@ -2,28 +2,28 @@
|
||||
{% load admin_modify %}
|
||||
{% load adminmedia %}
|
||||
{% block extrahead %}
|
||||
{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}
|
||||
{% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
|
||||
{% endblock %}
|
||||
{% block coltype %}{{ coltype }}{% endblock %}
|
||||
{% block bodyclass %}{{app_label}}-{{object_name.lower}} change-form{% endblock %}
|
||||
{% block coltype %}{{ bound_manipulator.coltype }}{% endblock %}
|
||||
{% block bodyclass %}{{app_label}}-{{bound_manipulator.object_name.lower}} change-form{% endblock %}
|
||||
{% block breadcrumbs %}{% if not is_popup %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="../../../">Home</a> ›
|
||||
<a href="../">{{verbose_name_plural|capfirst}}</a> ›
|
||||
{% if add %}Add {{verbose_name}}{% else %}{{original|striptags|truncatewords:"18"}}{% endif %}
|
||||
<a href="../">{{bound_manipulator.verbose_name_plural|capfirst}}</a> ›
|
||||
{% if add %}Add {{bound_manipulator.verbose_name}}{% else %}{{bound_manipulator.original|striptags|truncatewords:"18"}}{% endif %}
|
||||
</div>
|
||||
{% endif %}{% endblock %}
|
||||
{% block content %}<div id="content-main">
|
||||
{% if change %}{% if not is_popup %}
|
||||
<ul class="object-tools"><li><a href="history/" class="historylink">History</a></li>
|
||||
{% if has_absolute_url %}<li><a href="/r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">View on site</a></li>{% endif%}
|
||||
{% if has_absolute_url %}<li><a href="/r/{{ bound_manipulator.content_type_id }}/{{ object_id }}/" class="viewsitelink">View on site</a></li>{% endif%}
|
||||
</ul>
|
||||
{% endif %}{% endif %}
|
||||
<form {{ form_enc_attrib }} action='{{ form_url }}' method="post">{% block form_top %}{%endblock%}
|
||||
<form {{ bound_manipulator.form_enc_attrib }} action='{{ form_url }}' method="post">{% block form_top %}{%endblock%}
|
||||
{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}
|
||||
{% if save_on_top %}{% submit_row %}{% endif %}
|
||||
{% if bound_manipulator.save_on_top %}{% submit_row %}{% endif %}
|
||||
{% if form.error_dict %}<p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p>{% endif %}
|
||||
{% for bound_field_set in bound_field_sets %}
|
||||
{% for bound_field_set in bound_manipulator.bound_field_sets %}
|
||||
<fieldset class="module aligned {{ bound_field_set.classes }}">
|
||||
{% if bound_field_set.name %}<h2>{{bound_field_set.name }}</h2>{% endif %}
|
||||
{% for bound_field_line in bound_field_set %}
|
||||
@ -36,7 +36,7 @@
|
||||
{% endfor %}
|
||||
{% block after_field_sets %}{% endblock %}
|
||||
{% if change %}
|
||||
{% if ordered_objects %}
|
||||
{% if bound_manipulator.ordered_objects %}
|
||||
<fieldset class="module"><h2>Ordering</h2>
|
||||
<div class="form-row{% if form.order_.errors %} error{% endif %} ">
|
||||
{% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}
|
||||
@ -45,23 +45,23 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% for related_object in inline_related_objects %}{% edit_inline related_object %}{% endfor %}
|
||||
{% for related_object in bound_manipulator.inline_related_objects %}{% edit_inline related_object %}{% endfor %}
|
||||
{% block after_related_objects%}{%endblock%}
|
||||
{% submit_row %}
|
||||
{% if add %}
|
||||
<script type="text/javascript">document.getElementById("{{first_form_field_id}}").focus();</script>
|
||||
<script type="text/javascript">document.getElementById("{{bound_manipulator.first_form_field_id}}").focus();</script>
|
||||
{% endif %}
|
||||
{% if auto_populated_fields %}
|
||||
{% if bound_manipulator.auto_populated_fields %}
|
||||
<script type="text/javascript">
|
||||
{% auto_populated_field_script auto_populated_fields change %}
|
||||
{% auto_populated_field_script bound_manipulator.auto_populated_fields change %}
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if change %}
|
||||
{% if ordered_objects %}
|
||||
{% if bound_manipulator.ordered_objects %}
|
||||
{% if form.order_objects %}<ul id="orderthese">
|
||||
{% for object in form.order_objects %}
|
||||
<li id="p{% firstof ordered_object_names %}">
|
||||
<span id="handlep{% firstof ordered_object_names %}">{{ object|truncatewords:"5" }}</span>
|
||||
<li id="p{% firstof bound_manipulator.ordered_object_names %}">
|
||||
<span id="handlep{% firstof bound_manipulator.ordered_object_names %}">{{ object|truncatewords:"5" }}</span>
|
||||
</li>
|
||||
{% endfor%}
|
||||
{% endif %}
|
||||
|
19
django/contrib/admin/templates/admin/change_list.html
Normal file
19
django/contrib/admin/templates/admin/change_list.html
Normal file
@ -0,0 +1,19 @@
|
||||
{% load admin_list %}
|
||||
{% extends "admin/base_site" %}
|
||||
{% block bodyclass %}change-list{% endblock %}
|
||||
{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › {{cl.opts.verbose_name_plural|capfirst}} </div>{% endblock %}{% endif %}
|
||||
{% block coltype %}flex{% endblock %}
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
{%if has_add_permission %}
|
||||
<ul class="object-tools"><li><a href="add/{%if is_popup%}?_popup=1{%endif%}" class="addlink">Add {{cl.opts.verbose_name}}</a></li></ul>\n'
|
||||
{% endif %}
|
||||
<div class="module{%if cl.has_filters%} filtered{%endif%}" id="changelist">
|
||||
{% search_form cl%}
|
||||
{% date_hierarchy cl%}
|
||||
{% filters cl %}
|
||||
{% result_list cl%}
|
||||
{% pagination cl%}
|
||||
</div>
|
||||
</div>
|
||||
{%endblock%}
|
5
django/contrib/admin/templates/admin/filters.html
Normal file
5
django/contrib/admin/templates/admin/filters.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% if cl.has_filters %}<div id="changelist-filter">
|
||||
<h2>Filter</h2>
|
||||
{% for spec in cl.filter_specs %}
|
||||
{% output_filter_spec cl spec %}
|
||||
{% endfor %}</div>{% endif %}
|
13
django/contrib/admin/templates/admin/pagination.html
Normal file
13
django/contrib/admin/templates/admin/pagination.html
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
<p class="paginator">
|
||||
{% if pagination_required %}
|
||||
{%for i in page_range %}
|
||||
{% paginator_number cl i %}
|
||||
{%endfor%}
|
||||
{%endif%}
|
||||
{{cl.result_count}} {% ifequal cl.result_count 1 %}{{cl.opts.verbose_name}}{%else%}{{cl.opts.verbose_name_plural}}{%endifequal%}
|
||||
{% if need_show_all_link %} <a href="{% query_string cl override ALL_VAR:'' %}" class="showall">Show all</a>{%endif%}
|
||||
|
||||
</p>
|
||||
|
||||
|
14
django/contrib/admin/templates/admin/search_form.html
Normal file
14
django/contrib/admin/templates/admin/search_form.html
Normal file
@ -0,0 +1,14 @@
|
||||
{%if cl.lookup_opts.admin.search_fields %}
|
||||
<div id="toolbar"><form id="changelist-search" action="" method="get">
|
||||
<label><img src="{% admin_media_prefix %}img/admin/icon_searchbox.png" /></label>
|
||||
<input type="text" size="40" name="{{search_var}}" value="{{cl.query|escape}}" id="searchbar" />
|
||||
<input type="submit" value="Go" />
|
||||
{%if show_result_count %}
|
||||
<span class="small quiet">{{cl.result_count}}s result{{cl.result_count|pluralize}} (<a href="?">{{cl.full_result_count}} total</a>)</span>
|
||||
{%endif%}
|
||||
{% for pair in cl.params.items %}
|
||||
{%ifnotequal pair.0 search_var%}<input type="hidden" name="{{pair.0|escape}}" value="{{pair.1|escape}}"/>{%endifnotequal%}
|
||||
{% endfor %}
|
||||
</form></div>
|
||||
<script type="text/javascript">document.getElementById("searchbar").focus();</script>
|
||||
{%endif%}
|
@ -7,9 +7,9 @@
|
||||
{{message}}
|
||||
</pre>
|
||||
|
||||
{%ifnotequal bottom "0"%}
|
||||
.<br/>.<br/>.<br/>
|
||||
{%endifnotequal%}
|
||||
{%if top%}
|
||||
...
|
||||
{%endif%}
|
||||
|
||||
<pre class="source">
|
||||
|
||||
@ -17,8 +17,8 @@
|
||||
{% else %}{{source_line.0|rjust:"5"}}:{{ source_line.1 }}
|
||||
{% endifequal %}{% endfor %}
|
||||
</pre>
|
||||
{%ifnotequal top total%}
|
||||
.<br/>.<br/>.<br/>
|
||||
{%ifnotequal bottom total%}
|
||||
...
|
||||
{%endifnotequal%}
|
||||
|
||||
</div>
|
||||
|
280
django/contrib/admin/templatetags/admin_list.py
Normal file
280
django/contrib/admin/templatetags/admin_list.py
Normal file
@ -0,0 +1,280 @@
|
||||
from django.core.template.decorators import simple_tag, inclusion_tag
|
||||
|
||||
from django.contrib.admin.views.main import MAX_SHOW_ALL_ALLOWED, DEFAULT_RESULTS_PER_PAGE, ALL_VAR, \
|
||||
ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR , SEARCH_VAR , IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE
|
||||
|
||||
from django.core import meta
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.html import strip_tags, escape
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.conf.settings import ADMIN_MEDIA_PREFIX
|
||||
from django.core import template
|
||||
|
||||
DOT = '.'
|
||||
|
||||
class QueryStringNode(template.Node):
|
||||
def __init__(self, cl_var, override_vars, remove_vars):
|
||||
self.cl_var, self.override_vars, self.remove_vars = cl_var, override_vars, remove_vars
|
||||
|
||||
def render(self, context):
|
||||
def res(var):
|
||||
return template.resolve_variable(var, context)
|
||||
|
||||
cl = res(self.cl_var)
|
||||
overrides = dict([ (res(k), res(v)) for k,v in self.override_vars ])
|
||||
remove = [res(v) for v in self.remove_vars]
|
||||
return cl.get_query_string(overrides, remove)
|
||||
|
||||
def do_query_string(parser, token):
|
||||
bits = token.contents.split()[1:]
|
||||
in_override = False
|
||||
in_remove = False
|
||||
override_vars = []
|
||||
remove_vars = []
|
||||
cl_var = bits.pop(0)
|
||||
|
||||
for word in bits:
|
||||
if in_override:
|
||||
if word == 'remove':
|
||||
in_remove = True
|
||||
in_override = False
|
||||
else:
|
||||
override_vars.append(word.split(':'))
|
||||
elif in_remove:
|
||||
remove_vars.append(word)
|
||||
else:
|
||||
if word == 'override':
|
||||
in_override = True
|
||||
elif word == 'remove':
|
||||
remove = True
|
||||
|
||||
return QueryStringNode(cl_var, override_vars, remove_vars)
|
||||
|
||||
template.register_tag('query_string', do_query_string)
|
||||
|
||||
|
||||
#@simple_tag
|
||||
def paginator_number(cl,i):
|
||||
if i == DOT:
|
||||
return '... '
|
||||
elif i == cl.page_num:
|
||||
return '<span class="this-page">%d</span> ' % (i+1)
|
||||
else:
|
||||
return '<a href="%s"%s>%d</a> ' % (cl.get_query_string( {PAGE_VAR: i}), (i == paginator.pages-1 and ' class="end"' or ''), i+1)
|
||||
paginator_number = simple_tag(paginator_number)
|
||||
|
||||
#@inclusion_tag('admin/pagination')
|
||||
def pagination(cl):
|
||||
paginator, page_num = cl.paginator, cl.page_num
|
||||
|
||||
pagination_required = (not cl.show_all or not cl.can_show_all) and cl.multi_page
|
||||
if not pagination_required:
|
||||
page_range = []
|
||||
else:
|
||||
ON_EACH_SIDE = 3
|
||||
ON_ENDS = 2
|
||||
|
||||
# If there are 10 or fewer pages, display links to every page.
|
||||
# Otherwise, do some fancy
|
||||
if paginator.pages <= 10:
|
||||
page_range = range(paginator.pages)
|
||||
else:
|
||||
# Insert "smart" pagination links, so that there are always ON_ENDS
|
||||
# links at either end of the list of pages, and there are always
|
||||
# ON_EACH_SIDE links at either end of the "current page" link.
|
||||
page_range = []
|
||||
if page_num > (ON_EACH_SIDE + ON_ENDS):
|
||||
page_range.extend(range(0, ON_EACH_SIDE - 1))
|
||||
page_range.append(DOT)
|
||||
page_range.extend(range(page_num - ON_EACH_SIDE, page_num + 1))
|
||||
else:
|
||||
page_range.extend(range(0, page_num + 1))
|
||||
if page_num < (paginator.pages - ON_EACH_SIDE - ON_ENDS - 1):
|
||||
page_range.extend(range(page_num + 1, page_num + ON_EACH_SIDE + 1))
|
||||
page_range.append(DOT)
|
||||
page_range.extend(range(paginator.pages - ON_ENDS, paginator.pages))
|
||||
else:
|
||||
page_range.extend(range(page_num + 1, paginator.pages))
|
||||
|
||||
return {'cl': cl,
|
||||
'pagination_required': pagination_required,
|
||||
'need_show_all_link': cl.can_show_all and not cl.show_all and cl.multi_page,
|
||||
'page_range': page_range,
|
||||
'ALL_VAR': ALL_VAR,
|
||||
'1': 1
|
||||
}
|
||||
pagination = inclusion_tag('admin/pagination')(pagination)
|
||||
|
||||
#@simple_tag
|
||||
def result_list(cl):
|
||||
result_list, lookup_opts, mod, order_field, order_type, params, is_popup, opts = \
|
||||
cl.result_list, cl.lookup_opts, cl.mod, cl.order_field, cl.order_type, cl.params, cl.is_popup, cl.opts
|
||||
|
||||
raw_template = []
|
||||
if result_list:
|
||||
# Table headers.
|
||||
raw_template.append('<table cellspacing="0">\n<thead>\n<tr>\n')
|
||||
for i, field_name in enumerate(lookup_opts.admin.list_display):
|
||||
try:
|
||||
f = lookup_opts.get_field(field_name)
|
||||
except meta.FieldDoesNotExist:
|
||||
# For non-field list_display values, check for the function
|
||||
# attribute "short_description". If that doesn't exist, fall
|
||||
# back to the method name. And __repr__ is a special-case.
|
||||
if field_name == '__repr__':
|
||||
header = lookup_opts.verbose_name
|
||||
else:
|
||||
func = getattr(mod.Klass, field_name) # Let AttributeErrors propogate.
|
||||
try:
|
||||
header = func.short_description
|
||||
except AttributeError:
|
||||
header = func.__name__
|
||||
# Non-field list_display values don't get ordering capability.
|
||||
raw_template.append('<th>%s</th>' % capfirst(header))
|
||||
else:
|
||||
if isinstance(f.rel, meta.ManyToOne) and f.null:
|
||||
raw_template.append('<th>%s</th>' % capfirst(f.verbose_name))
|
||||
else:
|
||||
th_classes = []
|
||||
new_order_type = 'asc'
|
||||
if field_name == order_field:
|
||||
th_classes.append('sorted %sending' % order_type.lower())
|
||||
new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type.lower()]
|
||||
raw_template.append('<th%s><a href="%s">%s</a></th>' % \
|
||||
((th_classes and ' class="%s"' % ' '.join(th_classes) or ''),
|
||||
cl.get_query_string( {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
||||
capfirst(f.verbose_name)))
|
||||
raw_template.append('</tr>\n</thead>\n')
|
||||
# Result rows.
|
||||
pk = lookup_opts.pk.name
|
||||
for i, result in enumerate(result_list):
|
||||
raw_template.append('<tr class="row%s">\n' % (i % 2 + 1))
|
||||
for j, field_name in enumerate(lookup_opts.admin.list_display):
|
||||
row_class = ''
|
||||
try:
|
||||
f = lookup_opts.get_field(field_name)
|
||||
except meta.FieldDoesNotExist:
|
||||
# For non-field list_display values, the value is a method
|
||||
# name. Execute the method.
|
||||
try:
|
||||
result_repr = strip_tags(str(getattr(result, field_name)()))
|
||||
except ObjectDoesNotExist:
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
else:
|
||||
field_val = getattr(result, f.column)
|
||||
# Foreign-key fields are special: Use the repr of the
|
||||
# related object.
|
||||
if isinstance(f.rel, meta.ManyToOne):
|
||||
if field_val is not None:
|
||||
result_repr = getattr(result, 'get_%s' % f.name)()
|
||||
else:
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
# Dates are special: They're formatted in a certain way.
|
||||
elif isinstance(f, meta.DateField):
|
||||
if field_val:
|
||||
if isinstance(f, meta.DateTimeField):
|
||||
result_repr = dateformat.format(field_val, 'N j, Y, P')
|
||||
else:
|
||||
result_repr = dateformat.format(field_val, 'N j, Y')
|
||||
else:
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
row_class = ' class="nowrap"'
|
||||
# Booleans are special: We use images.
|
||||
elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField):
|
||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||
# ImageFields are special: Use a thumbnail.
|
||||
elif isinstance(f, meta.ImageField):
|
||||
from django.parts.media.photos import get_thumbnail_url
|
||||
result_repr = '<img src="%s" alt="%s" title="%s" />' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val)
|
||||
# FloatFields are special: Zero-pad the decimals.
|
||||
elif isinstance(f, meta.FloatField):
|
||||
if field_val is not None:
|
||||
result_repr = ('%%.%sf' % f.decimal_places) % field_val
|
||||
else:
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
# Fields with choices are special: Use the representation
|
||||
# of the choice.
|
||||
elif f.choices:
|
||||
result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE)
|
||||
else:
|
||||
result_repr = strip_tags(str(field_val))
|
||||
# Some browsers don't like empty "<td></td>"s.
|
||||
if result_repr == '':
|
||||
result_repr = ' '
|
||||
if j == 0: # First column is a special case
|
||||
result_id = getattr(result, pk)
|
||||
raw_template.append('<th%s><a href="%s/"%s>%s</a></th>' % \
|
||||
(row_class, result_id, (is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr))
|
||||
else:
|
||||
raw_template.append('<td%s>%s</td>' % (row_class, result_repr))
|
||||
raw_template.append('</tr>\n')
|
||||
del result_list # to free memory
|
||||
raw_template.append('</table>\n')
|
||||
else:
|
||||
raw_template.append('<p>No %s matched your search criteria.</p>' % opts.verbose_name_plural)
|
||||
return ''.join(raw_template)
|
||||
result_list = simple_tag(result_list)
|
||||
|
||||
#@simple_tag
|
||||
def date_hierarchy(cl):
|
||||
lookup_opts, params, lookup_params, lookup_mod = \
|
||||
cl.lookup_opts, cl.params, cl.lookup_params, cl.lookup_mod
|
||||
|
||||
raw_template = []
|
||||
if lookup_opts.admin.date_hierarchy:
|
||||
field_name = lookup_opts.admin.date_hierarchy
|
||||
|
||||
year_field = '%s__year' % field_name
|
||||
month_field = '%s__month' % field_name
|
||||
day_field = '%s__day' % field_name
|
||||
field_generic = '%s__' % field_name
|
||||
year_lookup = params.get(year_field)
|
||||
month_lookup = params.get(month_field)
|
||||
day_lookup = params.get(day_field)
|
||||
|
||||
raw_template.append('<div class="xfull">\n<ul class="toplinks">\n')
|
||||
if year_lookup and month_lookup and day_lookup:
|
||||
raw_template.append('<li class="date-back"><a href="%s">‹ %s %s </a></li>' % \
|
||||
(cl.get_query_string( {year_field: year_lookup, month_field: month_lookup}, [field_generic]), MONTHS[int(month_lookup)], year_lookup))
|
||||
raw_template.append('<li>%s %s</li>' % (MONTHS[int(month_lookup)], day_lookup))
|
||||
elif year_lookup and month_lookup:
|
||||
raw_template.append('<li class="date-back"><a href="%s">‹ %s</a></li>' % \
|
||||
(cl.get_query_string( {year_field: year_lookup}, [field_generic]), year_lookup))
|
||||
date_lookup_params = lookup_params.copy()
|
||||
date_lookup_params.update({year_field: year_lookup, month_field: month_lookup})
|
||||
for day in getattr(lookup_mod, 'get_%s_list' % field_name)('day', **date_lookup_params):
|
||||
raw_template.append('<li><a href="%s">%s</a></li>' % \
|
||||
(cl.get_query_string({year_field: year_lookup, month_field: month_lookup, day_field: day.day}, [field_generic]), day.strftime('%B %d')))
|
||||
elif year_lookup:
|
||||
raw_template.append('<li class="date-back"><a href="%s">‹ All dates</a></li>' % \
|
||||
cl.get_query_string( {}, [year_field]))
|
||||
date_lookup_params = lookup_params.copy()
|
||||
date_lookup_params.update({year_field: year_lookup})
|
||||
for month in getattr(lookup_mod, 'get_%s_list' % field_name)('month', **date_lookup_params):
|
||||
raw_template.append('<li><a href="%s">%s %s</a></li>' % \
|
||||
(cl.get_query_string( {year_field: year_lookup, month_field: month.month}, [field_generic]), month.strftime('%B'), month.year))
|
||||
else:
|
||||
for year in getattr(lookup_mod, 'get_%s_list' % field_name)('year', **lookup_params):
|
||||
raw_template.append('<li><a href="%s">%s</a></li>\n' % \
|
||||
(cl.get_query_string( {year_field: year.year}, [field_generic]), year.year))
|
||||
raw_template.append('</ul><br class="clear" />\n</div>\n')
|
||||
return ''.join(raw_template)
|
||||
date_hierarchy = simple_tag(date_hierarchy)
|
||||
|
||||
#@inclusion_tag('admin/search_form')
|
||||
def search_form(cl):
|
||||
return { 'cl': cl,
|
||||
'show_result_count': cl.result_count != cl.full_result_count and not cl.opts.one_to_one_field,
|
||||
'search_var': SEARCH_VAR }
|
||||
search_form = inclusion_tag('admin/search_form')(search_form)
|
||||
|
||||
#@simple_tag
|
||||
def output_filter_spec(cl, spec):
|
||||
return spec.output(cl)
|
||||
output_filter_spec = simple_tag(output_filter_spec)
|
||||
|
||||
#@inclusion_tag('admin/filters')
|
||||
def filters(cl):
|
||||
return {'cl': cl}
|
||||
filters = inclusion_tag('admin/filters')(filters)
|
@ -288,9 +288,9 @@ DATA_TYPE_MAPPING = {
|
||||
|
||||
def get_readable_field_data_type(field):
|
||||
# ForeignKey is a special case. Use the field type of the relation.
|
||||
if field.__class__.__name__ == 'ForeignKey':
|
||||
if field.get_internal_type() == 'ForeignKey':
|
||||
field = field.rel.get_related_field()
|
||||
return DATA_TYPE_MAPPING[field.__class__.__name__] % field.__dict__
|
||||
return DATA_TYPE_MAPPING[field.get_internal_type()] % field.__dict__
|
||||
|
||||
def extract_views_from_urlpatterns(urlpatterns, base=''):
|
||||
"""
|
||||
|
@ -1,5 +1,4 @@
|
||||
# Generic admin views.
|
||||
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.core import formfields, meta, template
|
||||
from django.core.template import loader
|
||||
@ -12,7 +11,25 @@ from django.utils.html import strip_tags
|
||||
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
|
||||
from django.utils.text import capfirst, get_text_list
|
||||
from django.conf.settings import ADMIN_MEDIA_PREFIX
|
||||
from django.core.paginator import ObjectPaginator, InvalidPage
|
||||
from django.utils import dateformat
|
||||
from django.utils.dates import MONTHS
|
||||
from django.utils.html import escape
|
||||
import operator
|
||||
import datetime
|
||||
|
||||
# The system will display a "Show all" link only if the total result count
|
||||
# is less than or equal to this setting.
|
||||
MAX_SHOW_ALL_ALLOWED = 200
|
||||
|
||||
DEFAULT_RESULTS_PER_PAGE = 100
|
||||
|
||||
ALL_VAR = 'all'
|
||||
ORDER_VAR = 'o'
|
||||
ORDER_TYPE_VAR = 'ot'
|
||||
PAGE_VAR = 'p'
|
||||
SEARCH_VAR = 'q'
|
||||
IS_POPUP_VAR = 'pop'
|
||||
|
||||
# Text to display within changelist table cells if the value is blank.
|
||||
EMPTY_CHANGELIST_VALUE = '(None)'
|
||||
@ -28,16 +45,187 @@ def _get_mod_opts(app_label, module_name):
|
||||
raise Http404 # This object is valid but has no admin interface.
|
||||
return mod, opts
|
||||
|
||||
def get_query_string(original_params, new_params={}, remove=[]):
|
||||
"""
|
||||
>>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'})
|
||||
'?first_name=adrian&last_name=smith'
|
||||
>>> get_query_string({'first_name': 'adrian', 'last_name': 'smith'}, {'first_name': 'john'})
|
||||
'?first_name=john&last_name=smith'
|
||||
>>> get_query_string({'test': 'yes'}, {'blah': 'no'}, ['te'])
|
||||
'?blah=no'
|
||||
"""
|
||||
p = original_params.copy()
|
||||
|
||||
def index(request):
|
||||
return render_to_response('admin/index', {'title': 'Site administration'}, context_instance=Context(request))
|
||||
index = staff_member_required(index)
|
||||
|
||||
class IncorrectLookupParameters(Exception):
|
||||
pass
|
||||
|
||||
class FilterSpec(object):
|
||||
filter_specs = []
|
||||
def __init__(self, f, request, params):
|
||||
self.field = f
|
||||
self.params = params
|
||||
|
||||
def register(cls, test, factory):
|
||||
cls.filter_specs.append( (test, factory) )
|
||||
register = classmethod(register)
|
||||
|
||||
def create(cls, f, request, params):
|
||||
for test, factory in cls.filter_specs:
|
||||
if test(f):
|
||||
return factory(f, request, params)
|
||||
create = classmethod(create)
|
||||
|
||||
def has_output(self):
|
||||
return True
|
||||
|
||||
def output(self, cl):
|
||||
t = []
|
||||
if self.has_output():
|
||||
t.append('<h3>By %s:</h3>\n<ul>\n' % self.title)
|
||||
|
||||
for choice in self.choices:
|
||||
t.append('<li%s><a href="%s">%r</a></li>\n' % \
|
||||
(self.is_selected(choice) and ' class="selected"' or ''),
|
||||
self.get_query_string(choice) ,
|
||||
self.get_display(choice) )
|
||||
t.append('</ul>\n\n')
|
||||
return "".join(t)
|
||||
|
||||
class RelatedFilterSpec(FilterSpec):
|
||||
|
||||
def __init__(self, f, request, params):
|
||||
super(RelatedFilterSpec, self).__init__(f, request, params)
|
||||
if isinstance(f, meta.ManyToManyField):
|
||||
self.lookup_title = f.rel.to.verbose_name
|
||||
else:
|
||||
self.lookup_title = f.verbose_name
|
||||
self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name)
|
||||
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
|
||||
self.lookup_choices = f.rel.to.get_model_module().get_list()
|
||||
|
||||
def has_output(self):
|
||||
return len(self.lookup_choices) > 1
|
||||
|
||||
def output(self, cl):
|
||||
t = []
|
||||
if self.has_output():
|
||||
t.append('<h3>By %s:</h3>\n<ul>\n' % self.lookup_title)
|
||||
t.append('<li%s><a href="%s">All</a></li>\n' % \
|
||||
((self.lookup_val is None and ' class="selected"' or ''),
|
||||
cl.get_query_string({}, [lookup_kwarg])))
|
||||
for val in lookup_choices:
|
||||
pk_val = getattr(val, f.rel.to.pk.column)
|
||||
t.append('<li%s><a href="%s">%r</a></li>\n' % \
|
||||
((self.lookup_val == str(pk_val) and ' class="selected"' or ''),
|
||||
cl.get_query_string( {self.lookup_kwarg: pk_val}), val))
|
||||
t.append('</ul>\n\n')
|
||||
return "".join(t)
|
||||
FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)
|
||||
|
||||
class ChoicesFilterSpec(FilterSpec):
|
||||
|
||||
def __init__(self, f, request, params):
|
||||
super(ChoicesFilterSpec, self).__init__(f, request, params)
|
||||
self.lookup_kwarg = '%s__exact' % f.name
|
||||
self.lookup_val = request.GET.get(lookup_kwarg, None)
|
||||
|
||||
def output(self, cl):
|
||||
t = []
|
||||
t.append('<h3>By %s:</h3><ul>\n' % self.field.verbose_name)
|
||||
t.append('<li%s><a href="%s">All</a></li>\n' % \
|
||||
((self.lookup_val is None and ' class="selected"' or ''),
|
||||
cl.get_query_string( {}, [self.lookup_kwarg])))
|
||||
for k, v in f.choices:
|
||||
t.append('<li%s><a href="%s">%s</a></li>' % \
|
||||
((str(k) == self.lookup_val) and ' class="selected"' or '',
|
||||
cl.get_query_string( {lookup_kwarg: k}), v))
|
||||
t.append('</ul>\n\n')
|
||||
return "".join(t)
|
||||
FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)
|
||||
|
||||
class DateFieldFilterSpec(FilterSpec):
|
||||
|
||||
def __init__(self, f, request, params):
|
||||
super(DateFieldFilterSpec, self).__init__(f, request, params)
|
||||
|
||||
self.field_generic = '%s__' % self.field.name
|
||||
|
||||
self.date_params = dict([(k, v) for k, v in params.items() if k.startswith(field_generic)])
|
||||
|
||||
today = datetime.date.today()
|
||||
one_week_ago = today - datetime.timedelta(days=7)
|
||||
today_str = isinstance(self.f, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
|
||||
|
||||
self.links = (
|
||||
('Any date', {}),
|
||||
('Today', {'%s__year' % self.field.name: str(today.year),
|
||||
'%s__month' % self.field.name: str(today.month),
|
||||
'%s__day' % self.field.name: str(today.day)}),
|
||||
('Past 7 days', {'%s__gte' % self.field.name: one_week_ago.strftime('%Y-%m-%d'),
|
||||
'%s__lte' % f.name: today_str}),
|
||||
('This month', {'%s__year' % self.field.name: str(today.year),
|
||||
'%s__month' % f.name: str(today.month)}),
|
||||
('This year', {'%s__year' % self.field.name: str(today.year)})
|
||||
)
|
||||
|
||||
def output(self, cl):
|
||||
t = []
|
||||
t.append('<h3>By %s:</h3><ul>\n' % self.field.verbose_name)
|
||||
for title, param_dict in self.links:
|
||||
t.append('<li%s><a href="%s">%s</a></li>\n' % \
|
||||
((self.date_params == param_dict) and ' class="selected"' or '',
|
||||
cl.get_query_string( param_dict, self.field_generic), title))
|
||||
t.append('</ul>\n\n')
|
||||
return "".join(t)
|
||||
FilterSpec.register(lambda f: isinstance(f, meta.DateField), DateFieldFilterSpec)
|
||||
|
||||
class BooleanFieldFilterSpec(FilterSpec):
|
||||
|
||||
def __init__(self, f, request, params):
|
||||
super(BooleanFieldFilterSpec, self).__init__(f, request, params)
|
||||
self.lookup_kwarg = '%s__exact' % f.name
|
||||
self.lookup_kwarg2 = '%s__isnull' % f.name
|
||||
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
|
||||
self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None)
|
||||
|
||||
def output(self, cl):
|
||||
t = []
|
||||
t.append('<h3>By %s:</h3><ul>\n' % self.field.verbose_name)
|
||||
for k, v in (('All', None), ('Yes', '1'), ('No', '0')):
|
||||
t.append('<li%s><a href="%s">%s</a></li>\n' % \
|
||||
(((self.lookup_val == v and not self.lookup_val2) and ' class="selected"' or ''),
|
||||
cl.get_query_string( {self.lookup_kwarg: v}, [self.lookup_kwarg2]), k))
|
||||
if isinstance(self.field, meta.NullBooleanField):
|
||||
t.append('<li%s><a href="%s">%s</a></li>\n' % \
|
||||
(((lookup_val2 == 'True') and ' class="selected"' or ''),
|
||||
cl.get_query_string( {self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]), 'Unknown'))
|
||||
t.append('</ul>\n\n')
|
||||
return "".join(t)
|
||||
FilterSpec.register(lambda f: isinstance(f, meta.BooleanField) or
|
||||
isinstance(f, meta.NullBooleanField), BooleanFieldFilterSpec)
|
||||
|
||||
class ChangeList(object):
|
||||
def __init__(self, request, app_label, module_name):
|
||||
self.get_modules_and_options(app_label, module_name, request)
|
||||
self.get_search_parameters(request)
|
||||
self.get_ordering()
|
||||
self.query = request.GET.get(SEARCH_VAR,'')
|
||||
self.get_lookup_params()
|
||||
self.get_results(request)
|
||||
self.title = (self.is_popup
|
||||
and 'Select %s' % self.opts.verbose_name
|
||||
or 'Select %s to change' % self.opts.verbose_name)
|
||||
self.get_filters(request)
|
||||
|
||||
def get_filters(self, request):
|
||||
self.filter_specs = []
|
||||
|
||||
if self.lookup_opts.admin.list_filter and not self.opts.one_to_one_field:
|
||||
filter_fields = [self.lookup_opts.get_field(field_name) \
|
||||
for field_name in self.lookup_opts.admin.list_filter]
|
||||
for f in filter_fields:
|
||||
spec = FilterSpec.create(f, request, self.params)
|
||||
if spec.has_output():
|
||||
self.filter_specs.append(spec)
|
||||
|
||||
self.has_filters = bool(self.filter_specs)
|
||||
|
||||
def get_query_string(self, new_params={}, remove=[]):
|
||||
p = self.params.copy()
|
||||
for r in remove:
|
||||
for k in p.keys():
|
||||
if k.startswith(r):
|
||||
@ -49,53 +237,74 @@ def get_query_string(original_params, new_params={}, remove=[]):
|
||||
p[k] = v
|
||||
return '?' + '&'.join(['%s=%s' % (k, v) for k, v in p.items()]).replace(' ', '%20')
|
||||
|
||||
def index(request):
|
||||
return render_to_response('admin/index', {'title': 'Site administration'}, context_instance=Context(request))
|
||||
index = staff_member_required(index)
|
||||
|
||||
def change_list(request, app_label, module_name):
|
||||
from django.core import paginator
|
||||
from django.utils import dateformat
|
||||
from django.utils.dates import MONTHS
|
||||
from django.utils.html import escape
|
||||
import datetime
|
||||
|
||||
# The system will display a "Show all" link only if the total result count
|
||||
# is less than or equal to this setting.
|
||||
MAX_SHOW_ALL_ALLOWED = 200
|
||||
|
||||
DEFAULT_RESULTS_PER_PAGE = 100
|
||||
|
||||
ALL_VAR = 'all'
|
||||
ORDER_VAR = 'o'
|
||||
ORDER_TYPE_VAR = 'ot'
|
||||
PAGE_VAR = 'p'
|
||||
SEARCH_VAR = 'q'
|
||||
IS_POPUP_VAR = 'pop'
|
||||
|
||||
mod, opts = _get_mod_opts(app_label, module_name)
|
||||
if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
|
||||
def get_modules_and_options(self, app_label, module_name, request):
|
||||
self.mod, self.opts = _get_mod_opts(app_label, module_name)
|
||||
if not request.user.has_perm(app_label + '.' + self.opts.get_change_permission()):
|
||||
raise PermissionDenied
|
||||
|
||||
lookup_mod, lookup_opts = mod, opts
|
||||
|
||||
if opts.one_to_one_field:
|
||||
lookup_mod = opts.one_to_one_field.rel.to.get_model_module()
|
||||
lookup_opts = lookup_mod.Klass._meta
|
||||
if self.opts.one_to_one_field:
|
||||
self.lookup_mod = self.opts.one_to_one_field.rel.to.get_model_module()
|
||||
self.lookup_opts = self.lookup_mod.Klass._meta
|
||||
# If lookup_opts doesn't have admin set, give it the default meta.Admin().
|
||||
if not lookup_opts.admin:
|
||||
lookup_opts.admin = meta.Admin()
|
||||
if not self.lookup_opts.admin:
|
||||
self.lookup_opts.admin = meta.Admin()
|
||||
else:
|
||||
self.lookup_mod, self.lookup_opts = self.mod, self.opts
|
||||
|
||||
|
||||
def get_search_parameters(self, request):
|
||||
# Get search parameters from the query string.
|
||||
try:
|
||||
page_num = int(request.GET.get(PAGE_VAR, 0))
|
||||
self.req_get = request.GET
|
||||
self.page_num = int(request.GET.get(PAGE_VAR, 0))
|
||||
except ValueError:
|
||||
page_num = 0
|
||||
show_all = request.GET.has_key(ALL_VAR)
|
||||
is_popup = request.GET.has_key(IS_POPUP_VAR)
|
||||
params = dict(request.GET.copy())
|
||||
if params.has_key(PAGE_VAR):
|
||||
del params[PAGE_VAR]
|
||||
self.page_num = 0
|
||||
self.show_all = request.GET.has_key(ALL_VAR)
|
||||
self.is_popup = request.GET.has_key(IS_POPUP_VAR)
|
||||
self.params = dict(request.GET.copy())
|
||||
if self.params.has_key(PAGE_VAR):
|
||||
del self.params[PAGE_VAR]
|
||||
|
||||
def get_results(self, request):
|
||||
lookup_mod, lookup_params, show_all, page_num = \
|
||||
self.lookup_mod, self.lookup_params, self.show_all, self.page_num
|
||||
# Get the results.
|
||||
try:
|
||||
paginator = ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE)
|
||||
# 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 (which would result in a database
|
||||
# error).
|
||||
except:
|
||||
raise IncorrectLookupParameters()
|
||||
|
||||
# Get the total number of objects, with no filters applied.
|
||||
real_lookup_params = lookup_params.copy()
|
||||
del real_lookup_params['order_by']
|
||||
if real_lookup_params:
|
||||
full_result_count = lookup_mod.get_count()
|
||||
else:
|
||||
full_result_count = paginator.hits
|
||||
del real_lookup_params
|
||||
result_count = paginator.hits
|
||||
can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
|
||||
multi_page = result_count > DEFAULT_RESULTS_PER_PAGE
|
||||
|
||||
# Get the list of objects to display on this page.
|
||||
if (show_all and can_show_all) or not multi_page:
|
||||
result_list = lookup_mod.get_list(**lookup_params)
|
||||
else:
|
||||
try:
|
||||
result_list = p.get_page(page_num)
|
||||
except InvalidPage:
|
||||
result_list = []
|
||||
(self.result_count, self.full_result_count, self.result_list,
|
||||
self.can_show_all, self.multi_page, self.paginator) = (result_count,
|
||||
full_result_count, result_list, can_show_all, multi_page, 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
|
||||
@ -122,9 +331,13 @@ def change_list(request, app_label, module_name):
|
||||
pass # Invalid ordering specified. Just use the default.
|
||||
if params.has_key(ORDER_TYPE_VAR) and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
|
||||
order_type = params[ORDER_TYPE_VAR]
|
||||
query = request.GET.get(SEARCH_VAR, '')
|
||||
self.order_field, self.order_type = order_field, order_type
|
||||
|
||||
def get_lookup_params(self):
|
||||
# Prepare the lookup parameters for the API lookup.
|
||||
(params, order_field, lookup_opts, order_type, opts, query) = \
|
||||
(self.params, self.order_field, self.lookup_opts, self.order_type, self.opts, self.query)
|
||||
|
||||
lookup_params = params.copy()
|
||||
for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR):
|
||||
if lookup_params.has_key(i):
|
||||
@ -159,364 +372,27 @@ def change_list(request, app_label, module_name):
|
||||
|
||||
if opts.one_to_one_field:
|
||||
lookup_params.update(opts.one_to_one_field.rel.limit_choices_to)
|
||||
self.lookup_params = lookup_params
|
||||
|
||||
# Get the results.
|
||||
|
||||
def change_list(request, app_label, module_name):
|
||||
try:
|
||||
p = paginator.ObjectPaginator(lookup_mod, lookup_params, DEFAULT_RESULTS_PER_PAGE)
|
||||
# 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 (which would result in a database
|
||||
# error).
|
||||
except:
|
||||
cl = ChangeList(request, app_label, module_name)
|
||||
except IncorrectLookupParameters:
|
||||
return HttpResponseRedirect(request.path)
|
||||
|
||||
# Get the total number of objects, with no filters applied.
|
||||
real_lookup_params = lookup_params.copy()
|
||||
del real_lookup_params['order_by']
|
||||
if real_lookup_params:
|
||||
full_result_count = lookup_mod.get_count()
|
||||
else:
|
||||
full_result_count = p.hits
|
||||
del real_lookup_params
|
||||
result_count = p.hits
|
||||
can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED
|
||||
multi_page = result_count > DEFAULT_RESULTS_PER_PAGE
|
||||
|
||||
# Get the list of objects to display on this page.
|
||||
if (show_all and can_show_all) or not multi_page:
|
||||
result_list = lookup_mod.get_list(**lookup_params)
|
||||
else:
|
||||
try:
|
||||
result_list = p.get_page(page_num)
|
||||
except paginator.InvalidPage:
|
||||
result_list = []
|
||||
|
||||
# Calculate filters first, because a CSS class high in the document depends
|
||||
# on whether they are available.
|
||||
filter_template = []
|
||||
if lookup_opts.admin.list_filter and not opts.one_to_one_field:
|
||||
filter_fields = [lookup_opts.get_field(field_name) for field_name in lookup_opts.admin.list_filter]
|
||||
for f in filter_fields:
|
||||
# Many-to-many or many-to-one filter.
|
||||
if f.rel:
|
||||
if isinstance(f, meta.ManyToManyField):
|
||||
lookup_title = f.rel.to.verbose_name
|
||||
else:
|
||||
lookup_title = f.verbose_name
|
||||
lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name)
|
||||
lookup_val = request.GET.get(lookup_kwarg, None)
|
||||
lookup_choices = f.rel.to.get_model_module().get_list()
|
||||
if len(lookup_choices) > 1:
|
||||
filter_template.append('<h3>By %s:</h3>\n<ul>\n' % lookup_title)
|
||||
filter_template.append('<li%s><a href="%s">All</a></li>\n' % \
|
||||
((lookup_val is None and ' class="selected"' or ''),
|
||||
get_query_string(params, {}, [lookup_kwarg])))
|
||||
for val in lookup_choices:
|
||||
pk_val = getattr(val, f.rel.to.pk.column)
|
||||
filter_template.append('<li%s><a href="%s">%r</a></li>\n' % \
|
||||
((lookup_val == str(pk_val) and ' class="selected"' or ''),
|
||||
get_query_string(params, {lookup_kwarg: pk_val}), val))
|
||||
filter_template.append('</ul>\n\n')
|
||||
# Field with choices.
|
||||
elif f.choices:
|
||||
lookup_kwarg = '%s__exact' % f.name
|
||||
lookup_val = request.GET.get(lookup_kwarg, None)
|
||||
filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name)
|
||||
filter_template.append('<li%s><a href="%s">All</a></li>\n' % \
|
||||
((lookup_val is None and ' class="selected"' or ''),
|
||||
get_query_string(params, {}, [lookup_kwarg])))
|
||||
for k, v in f.choices:
|
||||
filter_template.append('<li%s><a href="%s">%s</a></li>' % \
|
||||
((str(k) == lookup_val) and ' class="selected"' or '',
|
||||
get_query_string(params, {lookup_kwarg: k}), v))
|
||||
filter_template.append('</ul>\n\n')
|
||||
# Date filter.
|
||||
elif isinstance(f, meta.DateField):
|
||||
today = datetime.date.today()
|
||||
one_week_ago = today - datetime.timedelta(days=7)
|
||||
field_generic = '%s__' % f.name
|
||||
filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name)
|
||||
date_params = dict([(k, v) for k, v in params.items() if k.startswith(field_generic)])
|
||||
today_str = isinstance(f, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
|
||||
for title, param_dict in (
|
||||
('Any date', {}),
|
||||
('Today', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month), '%s__day' % f.name: str(today.day)}),
|
||||
('Past 7 days', {'%s__gte' % f.name: one_week_ago.strftime('%Y-%m-%d'), '%s__lte' % f.name: today_str}),
|
||||
('This month', {'%s__year' % f.name: str(today.year), '%s__month' % f.name: str(today.month)}),
|
||||
('This year', {'%s__year' % f.name: str(today.year)})
|
||||
):
|
||||
filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \
|
||||
((date_params == param_dict) and ' class="selected"' or '',
|
||||
get_query_string(params, param_dict, field_generic), title))
|
||||
filter_template.append('</ul>\n\n')
|
||||
elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField):
|
||||
lookup_kwarg = '%s__exact' % f.name
|
||||
lookup_kwarg2 = '%s__isnull' % f.name
|
||||
lookup_val = request.GET.get(lookup_kwarg, None)
|
||||
lookup_val2 = request.GET.get(lookup_kwarg2, None)
|
||||
filter_template.append('<h3>By %s:</h3><ul>\n' % f.verbose_name)
|
||||
for k, v in (('All', None), ('Yes', '1'), ('No', '0')):
|
||||
filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \
|
||||
(((lookup_val == v and not lookup_val2) and ' class="selected"' or ''),
|
||||
get_query_string(params, {lookup_kwarg: v}, [lookup_kwarg2]), k))
|
||||
if isinstance(f, meta.NullBooleanField):
|
||||
filter_template.append('<li%s><a href="%s">%s</a></li>\n' % \
|
||||
(((lookup_val2 == 'True') and ' class="selected"' or ''),
|
||||
get_query_string(params, {lookup_kwarg2: 'True'}, [lookup_kwarg]), 'Unknown'))
|
||||
filter_template.append('</ul>\n\n')
|
||||
else:
|
||||
pass # Invalid argument to "list_filter"
|
||||
|
||||
raw_template = ['{% extends "admin/base_site" %}\n']
|
||||
raw_template.append('{% block bodyclass %}change-list{% endblock %}\n')
|
||||
if not is_popup:
|
||||
raw_template.append('{%% block breadcrumbs %%}<div class="breadcrumbs"><a href="../../">Home</a> › %s</div>{%% endblock %%}\n' % capfirst(opts.verbose_name_plural))
|
||||
raw_template.append('{% block coltype %}flex{% endblock %}')
|
||||
raw_template.append('{% block content %}\n')
|
||||
raw_template.append('<div id="content-main">\n')
|
||||
if request.user.has_perm(app_label + '.' + lookup_opts.get_add_permission()):
|
||||
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))
|
||||
raw_template.append('<div class="module%s" id="changelist">\n' % (filter_template and ' filtered' or ''))
|
||||
|
||||
# Search form.
|
||||
if lookup_opts.admin.search_fields:
|
||||
raw_template.append('<div id="toolbar">\n<form id="changelist-search" action="" method="get">\n')
|
||||
raw_template.append('<label><img src="%simg/admin/icon_searchbox.png" /></label> ' % ADMIN_MEDIA_PREFIX)
|
||||
raw_template.append('<input type="text" size="40" name="%s" value="%s" id="searchbar" /> ' % \
|
||||
(SEARCH_VAR, escape(query)))
|
||||
raw_template.append('<input type="submit" value="Go" /> ')
|
||||
if result_count != full_result_count and not opts.one_to_one_field:
|
||||
raw_template.append('<span class="small quiet">%s result%s (<a href="?">%s total</a>)</span>' % \
|
||||
(result_count, (result_count != 1 and 's' or ''), full_result_count))
|
||||
for k, v in params.items():
|
||||
if k != SEARCH_VAR:
|
||||
raw_template.append('<input type="hidden" name="%s" value="%s" />' % (escape(k), escape(v)))
|
||||
raw_template.append('</form></div>\n')
|
||||
raw_template.append('<script type="text/javascript">document.getElementById("searchbar").focus();</script>')
|
||||
|
||||
# Date-based navigation.
|
||||
if lookup_opts.admin.date_hierarchy:
|
||||
field_name = lookup_opts.admin.date_hierarchy
|
||||
|
||||
year_field = '%s__year' % field_name
|
||||
month_field = '%s__month' % field_name
|
||||
day_field = '%s__day' % field_name
|
||||
field_generic = '%s__' % field_name
|
||||
year_lookup = params.get(year_field)
|
||||
month_lookup = params.get(month_field)
|
||||
day_lookup = params.get(day_field)
|
||||
|
||||
raw_template.append('<div class="xfull">\n<ul class="toplinks">\n')
|
||||
if year_lookup and month_lookup and day_lookup:
|
||||
raw_template.append('<li class="date-back"><a href="%s">‹ %s %s </a></li>' % \
|
||||
(get_query_string(params, {year_field: year_lookup, month_field: month_lookup}, [field_generic]), MONTHS[int(month_lookup)], year_lookup))
|
||||
raw_template.append('<li>%s %s</li>' % (MONTHS[int(month_lookup)], day_lookup))
|
||||
elif year_lookup and month_lookup:
|
||||
raw_template.append('<li class="date-back"><a href="%s">‹ %s</a></li>' % \
|
||||
(get_query_string(params, {year_field: year_lookup}, [field_generic]), year_lookup))
|
||||
date_lookup_params = lookup_params.copy()
|
||||
date_lookup_params.update({year_field: year_lookup, month_field: month_lookup})
|
||||
for day in getattr(lookup_mod, 'get_%s_list' % field_name)('day', **date_lookup_params):
|
||||
raw_template.append('<li><a href="%s">%s</a></li>' % \
|
||||
(get_query_string(params, {year_field: year_lookup, month_field: month_lookup, day_field: day.day}, [field_generic]), day.strftime('%B %d')))
|
||||
elif year_lookup:
|
||||
raw_template.append('<li class="date-back"><a href="%s">‹ All dates</a></li>' % \
|
||||
get_query_string(params, {}, [year_field]))
|
||||
date_lookup_params = lookup_params.copy()
|
||||
date_lookup_params.update({year_field: year_lookup})
|
||||
for month in getattr(lookup_mod, 'get_%s_list' % field_name)('month', **date_lookup_params):
|
||||
raw_template.append('<li><a href="%s">%s %s</a></li>' % \
|
||||
(get_query_string(params, {year_field: year_lookup, month_field: month.month}, [field_generic]), month.strftime('%B'), month.year))
|
||||
else:
|
||||
for year in getattr(lookup_mod, 'get_%s_list' % field_name)('year', **lookup_params):
|
||||
raw_template.append('<li><a href="%s">%s</a></li>\n' % \
|
||||
(get_query_string(params, {year_field: year.year}, [field_generic]), year.year))
|
||||
raw_template.append('</ul><br class="clear" />\n</div>\n')
|
||||
|
||||
# Filters.
|
||||
if filter_template:
|
||||
raw_template.append('<div id="changelist-filter">\n<h2>Filter</h2>\n')
|
||||
raw_template.extend(filter_template)
|
||||
raw_template.append('</div>')
|
||||
del filter_template
|
||||
|
||||
# Result table.
|
||||
if result_list:
|
||||
# Table headers.
|
||||
raw_template.append('<table cellspacing="0">\n<thead>\n<tr>\n')
|
||||
for i, field_name in enumerate(lookup_opts.admin.list_display):
|
||||
try:
|
||||
f = lookup_opts.get_field(field_name)
|
||||
except meta.FieldDoesNotExist:
|
||||
# For non-field list_display values, check for the function
|
||||
# attribute "short_description". If that doesn't exist, fall
|
||||
# back to the method name. And __repr__ is a special-case.
|
||||
if field_name == '__repr__':
|
||||
header = lookup_opts.verbose_name
|
||||
else:
|
||||
func = getattr(mod.Klass, field_name) # Let AttributeErrors propogate.
|
||||
try:
|
||||
header = func.short_description
|
||||
except AttributeError:
|
||||
header = func.__name__
|
||||
# Non-field list_display values don't get ordering capability.
|
||||
raw_template.append('<th>%s</th>' % capfirst(header))
|
||||
else:
|
||||
if isinstance(f.rel, meta.ManyToOne) and f.null:
|
||||
raw_template.append('<th>%s</th>' % capfirst(f.verbose_name))
|
||||
else:
|
||||
th_classes = []
|
||||
new_order_type = 'asc'
|
||||
if field_name == order_field:
|
||||
th_classes.append('sorted %sending' % order_type.lower())
|
||||
new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type.lower()]
|
||||
raw_template.append('<th%s><a href="%s">%s</a></th>' % \
|
||||
((th_classes and ' class="%s"' % ' '.join(th_classes) or ''),
|
||||
get_query_string(params, {ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
|
||||
capfirst(f.verbose_name)))
|
||||
raw_template.append('</tr>\n</thead>\n')
|
||||
# Result rows.
|
||||
pk = lookup_opts.pk.name
|
||||
for i, result in enumerate(result_list):
|
||||
raw_template.append('<tr class="row%s">\n' % (i % 2 + 1))
|
||||
for j, field_name in enumerate(lookup_opts.admin.list_display):
|
||||
row_class = ''
|
||||
try:
|
||||
f = lookup_opts.get_field(field_name)
|
||||
except meta.FieldDoesNotExist:
|
||||
# For non-field list_display values, the value is a method
|
||||
# name. Execute the method.
|
||||
try:
|
||||
result_repr = strip_tags(str(getattr(result, field_name)()))
|
||||
except ObjectDoesNotExist:
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
else:
|
||||
field_val = getattr(result, f.column)
|
||||
# Foreign-key fields are special: Use the repr of the
|
||||
# related object.
|
||||
if isinstance(f.rel, meta.ManyToOne):
|
||||
if field_val is not None:
|
||||
result_repr = getattr(result, 'get_%s' % f.name)()
|
||||
else:
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
# Dates are special: They're formatted in a certain way.
|
||||
elif isinstance(f, meta.DateField):
|
||||
if field_val:
|
||||
if isinstance(f, meta.DateTimeField):
|
||||
result_repr = dateformat.format(field_val, 'N j, Y, P')
|
||||
else:
|
||||
result_repr = dateformat.format(field_val, 'N j, Y')
|
||||
else:
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
row_class = ' class="nowrap"'
|
||||
# Booleans are special: We use images.
|
||||
elif isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField):
|
||||
BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'}
|
||||
result_repr = '<img src="%simg/admin/icon-%s.gif" alt="%s" />' % (ADMIN_MEDIA_PREFIX, BOOLEAN_MAPPING[field_val], field_val)
|
||||
# ImageFields are special: Use a thumbnail.
|
||||
elif isinstance(f, meta.ImageField):
|
||||
from django.parts.media.photos import get_thumbnail_url
|
||||
result_repr = '<img src="%s" alt="%s" title="%s" />' % (get_thumbnail_url(getattr(result, 'get_%s_url' % f.name)(), '120'), field_val, field_val)
|
||||
# FloatFields are special: Zero-pad the decimals.
|
||||
elif isinstance(f, meta.FloatField):
|
||||
if field_val is not None:
|
||||
result_repr = ('%%.%sf' % f.decimal_places) % field_val
|
||||
else:
|
||||
result_repr = EMPTY_CHANGELIST_VALUE
|
||||
# Fields with choices are special: Use the representation
|
||||
# of the choice.
|
||||
elif f.choices:
|
||||
result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE)
|
||||
else:
|
||||
result_repr = strip_tags(str(field_val))
|
||||
# Some browsers don't like empty "<td></td>"s.
|
||||
if result_repr == '':
|
||||
result_repr = ' '
|
||||
if j == 0: # First column is a special case
|
||||
result_id = getattr(result, pk)
|
||||
raw_template.append('<th%s><a href="%s/"%s>%s</a></th>' % \
|
||||
(row_class, result_id, (is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr))
|
||||
else:
|
||||
raw_template.append('<td%s>%s</td>' % (row_class, result_repr))
|
||||
raw_template.append('</tr>\n')
|
||||
del result_list # to free memory
|
||||
raw_template.append('</table>\n')
|
||||
else:
|
||||
raw_template.append('<p>No %s matched your search criteria.</p>' % opts.verbose_name_plural)
|
||||
|
||||
# Pagination.
|
||||
raw_template.append('<p class="paginator">')
|
||||
if (show_all and can_show_all) or not multi_page:
|
||||
pass
|
||||
else:
|
||||
raw_template.append('Page › ')
|
||||
ON_EACH_SIDE = 3
|
||||
ON_ENDS = 2
|
||||
DOT = '.'
|
||||
# If there are 10 or fewer pages, display links to every page.
|
||||
# Otherwise, do some fancy
|
||||
if p.pages <= 10:
|
||||
page_range = range(p.pages)
|
||||
else:
|
||||
# Insert "smart" pagination links, so that there are always ON_ENDS
|
||||
# links at either end of the list of pages, and there are always
|
||||
# ON_EACH_SIDE links at either end of the "current page" link.
|
||||
page_range = []
|
||||
if page_num > (ON_EACH_SIDE + ON_ENDS):
|
||||
page_range.extend(range(0, ON_EACH_SIDE - 1))
|
||||
page_range.append(DOT)
|
||||
page_range.extend(range(page_num - ON_EACH_SIDE, page_num + 1))
|
||||
else:
|
||||
page_range.extend(range(0, page_num + 1))
|
||||
if page_num < (p.pages - ON_EACH_SIDE - ON_ENDS - 1):
|
||||
page_range.extend(range(page_num + 1, page_num + ON_EACH_SIDE + 1))
|
||||
page_range.append(DOT)
|
||||
page_range.extend(range(p.pages - ON_ENDS, p.pages))
|
||||
else:
|
||||
page_range.extend(range(page_num + 1, p.pages))
|
||||
for i in page_range:
|
||||
if i == DOT:
|
||||
raw_template.append('... ')
|
||||
elif i == page_num:
|
||||
raw_template.append('<span class="this-page">%d</span> ' % (i+1))
|
||||
else:
|
||||
raw_template.append('<a href="%s"%s>%d</a> ' % \
|
||||
(get_query_string(params, {PAGE_VAR: i}), (i == p.pages-1 and ' class="end"' or ''), i+1))
|
||||
raw_template.append('%s %s' % (result_count, result_count == 1 and opts.verbose_name or opts.verbose_name_plural))
|
||||
if can_show_all and not show_all and multi_page:
|
||||
raw_template.append(' <a href="%s" class="showall">Show all</a>' % \
|
||||
get_query_string(params, {ALL_VAR: ''}))
|
||||
raw_template.append('</p>')
|
||||
|
||||
raw_template.append('</div>\n</div>')
|
||||
raw_template.append('{% endblock %}\n')
|
||||
t = loader.get_template_from_string(''.join(raw_template))
|
||||
c = Context(request, {
|
||||
'title': (is_popup and 'Select %s' % opts.verbose_name or 'Select %s to change' % opts.verbose_name),
|
||||
'is_popup': is_popup,
|
||||
'title': cl.title,
|
||||
'is_popup': cl.is_popup,
|
||||
'cl' : cl
|
||||
})
|
||||
return HttpResponse(t.render(c))
|
||||
return render_to_response('admin/change_list',
|
||||
context_instance = c)
|
||||
change_list = staff_member_required(change_list)
|
||||
|
||||
|
||||
use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOne, meta.ManyToMany)) and field.rel.raw_id_admin
|
||||
|
||||
def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects):
|
||||
t = ['<div class="submit-row">']
|
||||
if change or show_delete:
|
||||
t.append('{%% if perms.%s.%s %%}{%% if not is_popup %%}<p class="float-left"><a href="delete/" class="deletelink">Delete</a></p>{%% endif %%}{%% endif %%}' % \
|
||||
(app_label, opts.get_delete_permission()))
|
||||
if change and opts.admin.save_as:
|
||||
t.append('{%% if not is_popup %%}<input type="submit" value="Save as new" name="_saveasnew" %s/>{%% endif %%}' % \
|
||||
(ordered_objects and change and 'onclick="submitOrderForm();"' or ''))
|
||||
if not opts.admin.save_as or add:
|
||||
t.append('{%% if not is_popup %%}<input type="submit" value="Save and add another" name="_addanother" %s/>{%% endif %%}' % \
|
||||
(ordered_objects and change and 'onclick="submitOrderForm();"' or ''))
|
||||
t.append('{%% if not is_popup %%}<input type="submit" value="Save and continue editing" name="_continue" %s/>{%% endif %%}' % \
|
||||
(ordered_objects and change and 'onclick="submitOrderForm();"' or ''))
|
||||
t.append('<input type="submit" value="Save" class="default" %s/>' % \
|
||||
(ordered_objects and change and 'onclick="submitOrderForm();"' or ''))
|
||||
t.append('</div>\n')
|
||||
return t
|
||||
|
||||
def get_javascript_imports(opts,auto_populated_fields, ordered_objects, field_sets):
|
||||
# Put in any necessary JavaScript imports.
|
||||
@ -534,8 +410,9 @@ def get_javascript_imports(opts,auto_populated_fields, ordered_objects, field_se
|
||||
if not seen_collapse and 'collapse' in field_set.classes:
|
||||
seen_collapse = True
|
||||
js.append('js/admin/CollapsedFieldsets.js' )
|
||||
try:
|
||||
|
||||
for field_line in field_set:
|
||||
try:
|
||||
for f in field_line:
|
||||
if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface:
|
||||
js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
|
||||
@ -544,7 +421,6 @@ def get_javascript_imports(opts,auto_populated_fields, ordered_objects, field_se
|
||||
break
|
||||
return js
|
||||
|
||||
|
||||
class AdminBoundField(BoundField):
|
||||
def __init__(self, field, field_mapping, original):
|
||||
super(AdminBoundField, self).__init__(field,field_mapping,original)
|
||||
@ -607,47 +483,47 @@ class AdminBoundFieldSet(BoundFieldSet):
|
||||
super(AdminBoundFieldSet, self).__init__(field_set, field_mapping, original, AdminBoundFieldLine)
|
||||
|
||||
|
||||
def render_change_form(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''):
|
||||
ordered_objects = opts.get_ordered_objects()[:]
|
||||
auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
|
||||
coltype = ordered_objects and 'colMS' or 'colM'
|
||||
|
||||
has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url')
|
||||
form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or ''
|
||||
form = context['form']
|
||||
original = context['original']
|
||||
class AdminBoundManipulator(object):
|
||||
def __init__(self, opts, manipulator, field_mapping):
|
||||
self.inline_related_objects = opts.get_followed_related_objects()
|
||||
|
||||
field_sets = opts.admin.get_field_sets(opts)
|
||||
bound_field_sets = [field_set.bind(form, original, AdminBoundFieldSet)
|
||||
self.original = hasattr(manipulator, 'original_object') and manipulator.original_object or None
|
||||
self.bound_field_sets = [field_set.bind(field_mapping, self.original, AdminBoundFieldSet)
|
||||
for field_set in field_sets]
|
||||
|
||||
javascript_imports = get_javascript_imports(opts, auto_populated_fields, ordered_objects, field_sets);
|
||||
first_form_field = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0];
|
||||
inline_related_objects = opts.get_followed_related_objects()
|
||||
ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects])
|
||||
|
||||
self.ordered_objects = opts.get_ordered_objects()[:]
|
||||
self.auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
|
||||
self.javascript_imports = get_javascript_imports(opts, self.auto_populated_fields, self.ordered_objects, field_sets);
|
||||
|
||||
self.coltype = self.ordered_objects and 'colMS' or 'colM'
|
||||
self.has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url')
|
||||
self.form_enc_attrib = opts.has_field_type(meta.FileField) and \
|
||||
'enctype="multipart/form-data" ' or ''
|
||||
|
||||
|
||||
|
||||
self.first_form_field_id = self.bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0].get_id();
|
||||
self.ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in self.ordered_objects])
|
||||
|
||||
self.save_on_top = opts.admin.save_on_top
|
||||
self.save_as = opts.admin.save_as
|
||||
|
||||
self.content_type_id = opts.get_content_type_id()
|
||||
self.verbose_name_plural = opts.verbose_name_plural
|
||||
self.verbose_name = opts.verbose_name
|
||||
self.object_name = opts.object_name
|
||||
|
||||
def render_change_form(opts, manipulator, app_label, context, add=False, change=False, show_delete=False, form_url=''):
|
||||
|
||||
extra_context = {
|
||||
'add': add,
|
||||
'change': change,
|
||||
'first_form_field_id': first_form_field.get_id(),
|
||||
'ordered_objects' : ordered_objects,
|
||||
'ordered_object_names' : ordered_object_names,
|
||||
'auto_populated_fields' : auto_populated_fields,
|
||||
'javascript_imports' : javascript_imports,
|
||||
'coltype' : coltype,
|
||||
'has_absolute_url': has_absolute_url,
|
||||
'form_enc_attrib': form_enc_attrib,
|
||||
'bound_manipulator' : AdminBoundManipulator(opts, manipulator, context['form']),
|
||||
'has_delete_permission' : context['perms'][app_label][opts.get_delete_permission()],
|
||||
'form_url' : form_url,
|
||||
'bound_field_sets' : bound_field_sets,
|
||||
'inline_related_objects': inline_related_objects,
|
||||
'content_type_id' : opts.get_content_type_id(),
|
||||
'save_on_top' : opts.admin.save_on_top,
|
||||
'verbose_name_plural': opts.verbose_name_plural,
|
||||
'verbose_name': opts.verbose_name,
|
||||
'save_as': opts.admin.save_as,
|
||||
'app_label': app_label,
|
||||
'object_name': opts.object_name,
|
||||
'has_delete_permission' : context['perms'][app_label][opts.get_delete_permission()]
|
||||
}
|
||||
|
||||
context.update(extra_context)
|
||||
@ -677,7 +553,7 @@ def add_stage(request, app_label, module_name, show_delete=False, form_url='', p
|
||||
new_object = manipulator.save(new_data)
|
||||
log_add_message(request.user, opts,manipulator,new_object)
|
||||
msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object)
|
||||
|
||||
pk_value = getattr(new_object,opts.pk.column)
|
||||
# Here, we distinguish between different save types by checking for
|
||||
# the presence of keys in request.POST.
|
||||
if request.POST.has_key("_continue"):
|
||||
@ -713,7 +589,7 @@ def add_stage(request, app_label, module_name, show_delete=False, form_url='', p
|
||||
if object_id_override is not None:
|
||||
c['object_id'] = object_id_override
|
||||
|
||||
return render_change_form(opts, app_label, c, add=True)
|
||||
return render_change_form(opts, manipulator, app_label, c, add=True)
|
||||
add_stage = staff_member_required(add_stage)
|
||||
|
||||
def log_change_message(user, opts,manipulator,new_object):
|
||||
@ -754,6 +630,7 @@ def change_stage(request, app_label, module_name, object_id):
|
||||
new_object = manipulator.save(new_data)
|
||||
log_change_message(request.user,opts,manipulator,new_object)
|
||||
msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object)
|
||||
pk_value = getattr(new_object,opts.pk.column)
|
||||
if request.POST.has_key("_continue"):
|
||||
request.user.add_message("%s You may edit it again below." % msg)
|
||||
if request.REQUEST.has_key('_popup'):
|
||||
@ -779,7 +656,7 @@ def change_stage(request, app_label, module_name, object_id):
|
||||
|
||||
id_order_list = []
|
||||
for rel_obj in opts.get_ordered_objects():
|
||||
id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())())
|
||||
id_order_list.extend(getattr(manipulator.original_object, 'get_%s_order' % rel_obj.object_name.lower())())
|
||||
if id_order_list:
|
||||
new_data['order_'] = ','.join(map(str, id_order_list))
|
||||
errors = {}
|
||||
@ -794,7 +671,7 @@ def change_stage(request, app_label, module_name, object_id):
|
||||
wrt = related.opts.order_with_respect_to
|
||||
if wrt and wrt.rel and wrt.rel.to == opts:
|
||||
func = getattr(manipulator.original_object, 'get_%s_list' %
|
||||
opts.get_rel_object_method_name(rel_opts, rel_field))
|
||||
related.get_method_name_part())
|
||||
orig_list = func()
|
||||
form.order_objects.extend(orig_list)
|
||||
|
||||
@ -806,7 +683,7 @@ def change_stage(request, app_label, module_name, object_id):
|
||||
'is_popup' : request.REQUEST.has_key('_popup')
|
||||
})
|
||||
|
||||
return render_change_form(opts, app_label, c, change=True)
|
||||
return render_change_form(opts,manipulator, app_label, c, change=True)
|
||||
|
||||
|
||||
|
||||
@ -825,8 +702,8 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
|
||||
for related in opts.get_all_related_objects():
|
||||
if related.opts in objects_seen:
|
||||
continue
|
||||
objects_seen.append(rel_opts)
|
||||
rel_opts_name = opts.get_rel_object_method_name(related.opts, related.field)
|
||||
objects_seen.append(related.opts)
|
||||
rel_opts_name = related.get_method_name_part()
|
||||
if isinstance(related.field.rel, meta.OneToOne):
|
||||
try:
|
||||
sub_obj = getattr(obj, 'get_%s' % rel_opts_name)()
|
||||
@ -868,29 +745,29 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
|
||||
p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
|
||||
if not user.has_perm(p):
|
||||
perms_needed.add(rel_opts.verbose_name)
|
||||
for rel_opts, rel_field in opts.get_all_related_many_to_many_objects():
|
||||
if rel_opts in objects_seen:
|
||||
for related in opts.get_all_related_many_to_many_objects():
|
||||
if related.opts in objects_seen:
|
||||
continue
|
||||
objects_seen.append(rel_opts)
|
||||
rel_opts_name = opts.get_rel_object_method_name(rel_opts, rel_field)
|
||||
objects_seen.append(related.opts)
|
||||
rel_opts_name = related.get_method_name_part()
|
||||
has_related_objs = False
|
||||
for sub_obj in getattr(obj, 'get_%s_list' % rel_opts_name)():
|
||||
has_related_objs = True
|
||||
if rel_field.rel.edit_inline or not rel_opts.admin:
|
||||
if related.field.rel.edit_inline or not related.opts.admin:
|
||||
# Don't display link to edit, because it either has no
|
||||
# admin or is edited inline.
|
||||
nh(deleted_objects, current_depth, ['One or more %s in %s: %s' % \
|
||||
(rel_field.name, rel_opts.verbose_name, strip_tags(repr(sub_obj))), []])
|
||||
(related.field.name, related.opts.verbose_name, strip_tags(repr(sub_obj))), []])
|
||||
else:
|
||||
# Display a link to the admin page.
|
||||
nh(deleted_objects, current_depth, ['One or more %s in %s: <a href="../../../../%s/%s/%s/">%s</a>' % \
|
||||
(rel_field.name, rel_opts.verbose_name, rel_opts.app_label, rel_opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []])
|
||||
(related.field.name, related.opts.verbose_name, related.opts.app_label, related.opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []])
|
||||
# If there were related objects, and the user doesn't have
|
||||
# permission to change them, add the missing perm to perms_needed.
|
||||
if rel_opts.admin and has_related_objs:
|
||||
p = '%s.%s' % (rel_opts.app_label, rel_opts.get_change_permission())
|
||||
if related.opts.admin and has_related_objs:
|
||||
p = '%s.%s' % (related.opts.app_label, related.opts.get_change_permission())
|
||||
if not user.has_perm(p):
|
||||
perms_needed.add(rel_opts.verbose_name)
|
||||
perms_needed.add(related.opts.verbose_name)
|
||||
|
||||
def delete_stage(request, app_label, module_name, object_id):
|
||||
import sets
|
||||
|
@ -13,7 +13,9 @@ settings.CACHE_BACKEND and use that to create and load a cache object.
|
||||
The CACHE_BACKEND setting is a quasi-URI; examples are:
|
||||
|
||||
memcached://127.0.0.1:11211/ A memcached backend; the server is running
|
||||
on localhost port 11211.
|
||||
on localhost port 11211. You can use
|
||||
multiple memcached servers by separating
|
||||
them with semicolons.
|
||||
|
||||
db://tablename/ A database backend in a table named
|
||||
"tablename". This table should be created
|
||||
@ -134,7 +136,7 @@ else:
|
||||
"Memcached cache backend."
|
||||
def __init__(self, server, params):
|
||||
_Cache.__init__(self, params)
|
||||
self._cache = memcache.Client([server])
|
||||
self._cache = memcache.Client(server.split(';'))
|
||||
|
||||
def get(self, key, default=None):
|
||||
val = self._cache.get(key)
|
||||
|
@ -315,6 +315,7 @@ class FormField:
|
||||
####################
|
||||
|
||||
class TextField(FormField):
|
||||
input_type = "text"
|
||||
def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[]):
|
||||
self.field_name = field_name
|
||||
self.length, self.maxlength = length, maxlength
|
||||
@ -878,8 +879,10 @@ class CommaSeparatedIntegerField(TextField):
|
||||
except validators.ValidationError, e:
|
||||
raise validators.CriticalValidationError, e.messages
|
||||
|
||||
class RawIdAdminField(CommaSeparatedIntegerField):
|
||||
def html2python(data):
|
||||
return data.split(',');
|
||||
html2python = classmethod(html2python)
|
||||
|
||||
class XMLLargeTextField(LargeTextField):
|
||||
"""
|
||||
|
@ -48,6 +48,9 @@ class BaseHandler:
|
||||
from django.core.mail import mail_admins
|
||||
from django.conf.settings import DEBUG, INTERNAL_IPS, ROOT_URLCONF
|
||||
|
||||
# Reset query list per request.
|
||||
db.db.queries = []
|
||||
|
||||
# Apply request middleware
|
||||
for middleware_method in self._request_middleware:
|
||||
response = middleware_method(request)
|
||||
|
@ -345,6 +345,27 @@ The full error: %s\n""" % \
|
||||
install.help_doc = "Executes ``sqlall`` for the given model module name(s) in the current database."
|
||||
install.args = APP_ARGS
|
||||
|
||||
def installperms(mod):
|
||||
"Installs any permissions for the given model, if needed."
|
||||
from django.models.auth import permissions
|
||||
from django.models.core import packages
|
||||
num_added = 0
|
||||
package = packages.get_object(pk=mod._MODELS[0]._meta.app_label)
|
||||
for klass in mod._MODELS:
|
||||
opts = klass._meta
|
||||
for codename, name in _get_all_permissions(opts):
|
||||
try:
|
||||
permissions.get_object(name__exact=name, codename__exact=codename, package__label__exact=package.label)
|
||||
except permissions.PermissionDoesNotExist:
|
||||
p = permissions.Permission(name=name, package=package, codename=codename)
|
||||
p.save()
|
||||
print "Added permission '%r'." % p
|
||||
num_added += 1
|
||||
if not num_added:
|
||||
print "No permissions were added, because all necessary permissions were already installed."
|
||||
installperms.help_doc = "Installs any permissions for the given model module name(s), if needed."
|
||||
installperms.args = APP_ARGS
|
||||
|
||||
def _start_helper(app_or_project, name, directory, other_name=''):
|
||||
other = {'project': 'app', 'app': 'project'}[app_or_project]
|
||||
if not _is_valid_dir_name(name):
|
||||
|
@ -190,7 +190,7 @@ class RelatedObject(object):
|
||||
def get_list(self, parent_instance = None):
|
||||
"Get the list of this type of object from an instance of the parent class"
|
||||
if parent_instance != None:
|
||||
func_name = 'get_%s_list' % self.parent_opts.get_rel_object_method_name(self.opts, self.field)
|
||||
func_name = 'get_%s_list' % self.get_method_name_part()
|
||||
func = getattr(parent_instance, func_name)
|
||||
list = func()
|
||||
|
||||
@ -239,8 +239,9 @@ class RelatedObject(object):
|
||||
return "<RelatedObject: %s related to %s>" % ( self.name, self.field.name)
|
||||
|
||||
def get_manipulator_fields(self, opts, manipulator, change, follow):
|
||||
#TODO: remove core fields stuff
|
||||
if change:
|
||||
meth_name = 'get_%s_count' % self.parent_opts.get_rel_object_method_name(self.opts, self.field)
|
||||
meth_name = 'get_%s_count' % self.get_method_name_part()
|
||||
count = getattr(manipulator.original_object, meth_name)()
|
||||
count += self.field.rel.num_extra_on_change
|
||||
if self.field.rel.min_num_in_admin:
|
||||
@ -263,6 +264,9 @@ class RelatedObject(object):
|
||||
def bind(self, field_mapping, original, bound_related_object_class=BoundRelatedObject):
|
||||
return bound_related_object_class(self, field_mapping, original)
|
||||
|
||||
def get_method_name_part(self):
|
||||
return self.parent_opts.get_rel_object_method_name(self.opts, self.field)
|
||||
|
||||
class Options:
|
||||
def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='',
|
||||
fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False,
|
||||
@ -461,7 +465,7 @@ class Options:
|
||||
try:
|
||||
for f in klass._meta.many_to_many:
|
||||
if f.rel and self == f.rel.to:
|
||||
rel_objs.append((klass._meta, f))
|
||||
rel_objs.append(RelatedObject(self, klass._meta, f))
|
||||
raise StopIteration
|
||||
except StopIteration:
|
||||
continue
|
||||
@ -856,10 +860,9 @@ class ModelBase(type):
|
||||
old_app._MODELS[i] = new_class
|
||||
# Replace all relationships to the old class with
|
||||
# relationships to the new one.
|
||||
for related in model._meta.get_all_related_objects():
|
||||
for related in model._meta.get_all_related_objects() + \
|
||||
model._meta.get_all_related_many_to_many_objects():
|
||||
related.field.rel.to = opts
|
||||
for rel_opts, rel_field in model._meta.get_all_related_many_to_many_objects():
|
||||
rel_field.rel.to = opts
|
||||
break
|
||||
|
||||
return new_class
|
||||
@ -958,7 +961,7 @@ def method_delete(opts, self):
|
||||
self._pre_delete()
|
||||
cursor = db.db.cursor()
|
||||
for related in opts.get_all_related_objects():
|
||||
rel_opts_name = opts.get_rel_object_method_name(related.opts, related.field)
|
||||
rel_opts_name = related.get_method_name_part()
|
||||
if isinstance(related.field.rel, OneToOne):
|
||||
try:
|
||||
sub_obj = getattr(self, 'get_%s' % rel_opts_name)()
|
||||
@ -969,8 +972,8 @@ def method_delete(opts, self):
|
||||
else:
|
||||
for sub_obj in getattr(self, 'get_%s_list' % rel_opts_name)():
|
||||
sub_obj.delete()
|
||||
for rel_opts, rel_field in opts.get_all_related_many_to_many_objects():
|
||||
cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (rel_field.get_m2m_db_table(rel_opts),
|
||||
for related in opts.get_all_related_many_to_many_objects():
|
||||
cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (related.field.get_m2m_db_table(related.opts),
|
||||
self._meta.object_name.lower()), [getattr(self, opts.pk.column)])
|
||||
for f in opts.many_to_many:
|
||||
cursor.execute("DELETE FROM %s WHERE %s_id=%%s" % (f.get_m2m_db_table(opts), self._meta.object_name.lower()),
|
||||
@ -1671,7 +1674,7 @@ def manipulator_save(opts, klass, add, change, self, new_data):
|
||||
if change:
|
||||
if rel_new_data[related.opts.pk.name][0]:
|
||||
try:
|
||||
old_rel_obj = getattr(self.original_object, 'get_%s' % opts.get_rel_object_method_name(related.opts, related.field))(**{'%s__exact' % related.opts.pk.name: rel_new_data[related.opts.pk.name][0]})
|
||||
old_rel_obj = getattr(self.original_object, 'get_%s' % related.get_method_name_part() )(**{'%s__exact' % related.opts.pk.name: rel_new_data[related.opts.pk.name][0]})
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
|
@ -706,7 +706,7 @@ class ManyToManyField(Field):
|
||||
|
||||
def get_manipulator_field_objs(self):
|
||||
if self.rel.raw_id_admin:
|
||||
return [formfields.CommaSeparatedIntegerField]
|
||||
return [formfields.RawIdAdminField]
|
||||
else:
|
||||
choices = self.get_choices_default()
|
||||
return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
||||
@ -741,7 +741,7 @@ class ManyToManyField(Field):
|
||||
instance_ids = [getattr(instance, self.rel.to.pk.column) for instance in get_list_func()]
|
||||
if self.rel.raw_id_admin:
|
||||
new_data[self.name] = ",".join([str(id) for id in instance_ids])
|
||||
elif not self.rel.edit_inline:
|
||||
else:
|
||||
new_data[self.name] = instance_ids
|
||||
else:
|
||||
# In required many-to-many fields with only one available choice,
|
||||
|
@ -395,7 +395,7 @@ class DebugParser(Parser):
|
||||
(command, (origin,line)) = self.command_stack.pop()
|
||||
msg = "Unclosed tag '%s' starting at %s, line %d. Looking for one of: %s " % \
|
||||
(command, origin, line, ', '.join(parse_until) )
|
||||
raise self.error( token.source, msg)
|
||||
raise self.error( (origin,line), msg)
|
||||
|
||||
def compile_function_error(self, token, e):
|
||||
if not hasattr(e, 'source'):
|
||||
|
@ -24,15 +24,15 @@ def fix_ampersands(value, _):
|
||||
|
||||
def floatformat(text, _):
|
||||
"""
|
||||
Displays a floating point number as 34.2 (with one decimal place) - but
|
||||
Displays a floating point number as 34.2 (with one decimal place) -- but
|
||||
only if there's a point to be displayed
|
||||
"""
|
||||
from math import modf
|
||||
if not text:
|
||||
return ''
|
||||
if modf(float(text))[0] < 0.1:
|
||||
return text
|
||||
return "%.1f" % float(text)
|
||||
f = float(text)
|
||||
m = f - int(f)
|
||||
if m:
|
||||
return '%.1f' % f
|
||||
else:
|
||||
return '%d' % int(f)
|
||||
|
||||
def linenumbers(value, _):
|
||||
"Displays text with line numbers"
|
||||
@ -175,7 +175,7 @@ def removetags(value, tags):
|
||||
"Removes a space separated list of [X]HTML tags from the output"
|
||||
tags = [re.escape(tag) for tag in tags.split()]
|
||||
tags_re = '(%s)' % '|'.join(tags)
|
||||
starttag_re = re.compile('<%s(>|(\s+[^>]*>))' % tags_re)
|
||||
starttag_re = re.compile(r'<%s(/?>|(\s+[^>]*>))' % tags_re)
|
||||
endtag_re = re.compile('</%s>' % tags_re)
|
||||
value = starttag_re.sub('', value)
|
||||
value = endtag_re.sub('', value)
|
||||
|
@ -142,6 +142,7 @@ class IfEqualNode(Node):
|
||||
def render(self, context):
|
||||
val1 = resolve_variable(self.var1, context)
|
||||
val2 = resolve_variable(self.var2, context)
|
||||
|
||||
if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
|
||||
return self.nodelist_true.render(context)
|
||||
return self.nodelist_false.render(context)
|
||||
|
@ -14,7 +14,6 @@ class TemplateDebugMiddleware(object):
|
||||
top = max(0, line - context_lines)
|
||||
bottom = min(total, line + 1 + context_lines)
|
||||
|
||||
|
||||
return render_to_response('template_debug', {
|
||||
'message' : exception.args[0],
|
||||
'source_lines' : source_lines[top:bottom],
|
||||
|
@ -25,7 +25,7 @@ for mod in modules:
|
||||
# label prepended, and the add_BLAH() method will not be
|
||||
# generated.
|
||||
rel_mod = related.opts.get_model_module()
|
||||
rel_obj_name = klass._meta.get_rel_object_method_name(related.opts, related.field)
|
||||
rel_obj_name = related.get_method_name_part()
|
||||
if isinstance(related.field.rel, meta.OneToOne):
|
||||
# Add "get_thingie" methods for one-to-one related objects.
|
||||
# EXAMPLE: Place.get_restaurants_restaurant()
|
||||
@ -62,18 +62,18 @@ for mod in modules:
|
||||
del rel_obj_name, rel_mod, related # clean up
|
||||
|
||||
# Do the same for all related many-to-many objects.
|
||||
for rel_opts, rel_field in klass._meta.get_all_related_many_to_many_objects():
|
||||
rel_mod = rel_opts.get_model_module()
|
||||
rel_obj_name = klass._meta.get_rel_object_method_name(rel_opts, rel_field)
|
||||
setattr(klass, 'get_%s' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_object', klass._meta, rel_mod, rel_field))
|
||||
setattr(klass, 'get_%s_count' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_count', klass._meta, rel_mod, rel_field))
|
||||
setattr(klass, 'get_%s_list' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_list', klass._meta, rel_mod, rel_field))
|
||||
if rel_opts.app_label == klass._meta.app_label:
|
||||
func = curry(meta.method_set_related_many_to_many, rel_opts, rel_field)
|
||||
for related in klass._meta.get_all_related_many_to_many_objects():
|
||||
rel_mod = related.opts.get_model_module()
|
||||
rel_obj_name = related.get_method_name_part()
|
||||
setattr(klass, 'get_%s' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_object', klass._meta, rel_mod, related.field))
|
||||
setattr(klass, 'get_%s_count' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_count', klass._meta, rel_mod, related.field))
|
||||
setattr(klass, 'get_%s_list' % rel_obj_name, curry(meta.method_get_related_many_to_many, 'get_list', klass._meta, rel_mod, related.field))
|
||||
if related.opts.app_label == klass._meta.app_label:
|
||||
func = curry(meta.method_set_related_many_to_many, related.opts, related.field)
|
||||
func.alters_data = True
|
||||
setattr(klass, 'set_%s' % rel_opts.module_name, func)
|
||||
setattr(klass, 'set_%s' % related.opts.module_name, func)
|
||||
del func
|
||||
del rel_obj_name, rel_mod, rel_opts, rel_field # clean up
|
||||
del rel_obj_name, rel_mod, related # clean up
|
||||
|
||||
# Add "set_thingie_order" and "get_thingie_order" methods for objects
|
||||
# that are ordered with respect to this.
|
||||
|
@ -37,6 +37,8 @@ def reloader_thread():
|
||||
mtimes = {}
|
||||
while RUN_RELOADER:
|
||||
for filename in filter(lambda v: v, map(lambda m: getattr(m, "__file__", None), sys.modules.values())) + reloadFiles:
|
||||
if not os.path.exists(filename):
|
||||
continue # File might be in an egg, so it can't be reloaded.
|
||||
if filename.endswith(".pyc"):
|
||||
filename = filename[:-1]
|
||||
mtime = os.stat(filename).st_mtime
|
||||
|
@ -21,6 +21,45 @@ import datetime, md5, re
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
|
||||
cc_delim_re = re.compile(r'\s*,\s*')
|
||||
def patch_cache_control(response, **kwargs):
|
||||
"""
|
||||
This function patches the Cache-Control header by adding all
|
||||
keyword arguments to it. The transformation is as follows:
|
||||
|
||||
- all keyword parameter names are turned to lowercase and
|
||||
all _ will be translated to -
|
||||
- if the value of a parameter is True (exatly True, not just a
|
||||
true value), only the parameter name is added to the header
|
||||
- all other parameters are added with their value, after applying
|
||||
str to it.
|
||||
"""
|
||||
|
||||
def dictitem(s):
|
||||
t = s.split('=',1)
|
||||
if len(t) > 1:
|
||||
return (t[0].lower().replace('-', '_'), t[1])
|
||||
else:
|
||||
return (t[0].lower().replace('-', '_'), True)
|
||||
|
||||
def dictvalue(t):
|
||||
if t[1] == True:
|
||||
return t[0]
|
||||
else:
|
||||
return t[0] + '=' + str(t[1])
|
||||
|
||||
if response.has_header('Cache-Control'):
|
||||
print response['Cache-Control']
|
||||
cc = cc_delim_re.split(response['Cache-Control'])
|
||||
print cc
|
||||
cc = dict([dictitem(el) for el in cc])
|
||||
else:
|
||||
cc = {}
|
||||
for (k,v) in kwargs.items():
|
||||
cc[k.replace('_', '-')] = v
|
||||
cc = ', '.join([dictvalue(el) for el in cc.items()])
|
||||
response['Cache-Control'] = cc
|
||||
|
||||
vary_delim_re = re.compile(r',\s*')
|
||||
|
||||
def patch_response_headers(response, cache_timeout=None):
|
||||
@ -43,8 +82,7 @@ def patch_response_headers(response, cache_timeout=None):
|
||||
response['Last-Modified'] = now.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
if not response.has_header('Expires'):
|
||||
response['Expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
if not response.has_header('Cache-Control'):
|
||||
response['Cache-Control'] = 'max-age=%d' % cache_timeout
|
||||
patch_cache_control(response, max_age=cache_timeout)
|
||||
|
||||
def patch_vary_headers(response, newheaders):
|
||||
"""
|
||||
|
@ -4,7 +4,6 @@ def user_passes_test(test_func):
|
||||
redirecting to the log-in page if necessary. The test should be a callable
|
||||
that takes the user object and returns True if the user passes.
|
||||
"""
|
||||
|
||||
def _dec(view_func):
|
||||
def _checklogin(request, *args, **kwargs):
|
||||
from django.views.auth.login import redirect_to_login
|
||||
@ -14,7 +13,6 @@ def user_passes_test(test_func):
|
||||
return _checklogin
|
||||
return _dec
|
||||
|
||||
|
||||
login_required = user_passes_test(lambda u: not u.is_anonymous())
|
||||
login_required.__doc__ = (
|
||||
"""
|
||||
@ -22,4 +20,3 @@ login_required.__doc__ = (
|
||||
to the log-in page if necessary.
|
||||
"""
|
||||
)
|
||||
|
||||
|
@ -10,8 +10,24 @@ example, as that is unique across a Django project.
|
||||
Additionally, all headers from the response's Vary header will be taken into
|
||||
account on caching -- just like the middleware does.
|
||||
"""
|
||||
import re
|
||||
|
||||
from django.utils.decorators import decorator_from_middleware
|
||||
from django.utils.cache import patch_cache_control
|
||||
from django.middleware.cache import CacheMiddleware
|
||||
|
||||
cache_page = decorator_from_middleware(CacheMiddleware)
|
||||
|
||||
def cache_control(**kwargs):
|
||||
|
||||
def _cache_controller(viewfunc):
|
||||
|
||||
def _cache_controlled(request, *args, **kw):
|
||||
response = viewfunc(request, *args, **kw)
|
||||
patch_cache_control(response, **kwargs)
|
||||
return response
|
||||
|
||||
return _cache_controlled
|
||||
|
||||
return _cache_controller
|
||||
|
||||
|
@ -1,9 +1,35 @@
|
||||
"""
|
||||
Decorator for views that supports conditional get on ETag and Last-Modified
|
||||
headers.
|
||||
Decorators for views based on HTTP headers.
|
||||
"""
|
||||
|
||||
from django.utils.decorators import decorator_from_middleware
|
||||
from django.middleware.http import ConditionalGetMiddleware
|
||||
from django.utils.httpwrappers import HttpResponseForbidden
|
||||
|
||||
conditional_page = decorator_from_middleware(ConditionalGetMiddleware)
|
||||
|
||||
def require_http_methods(request_method_list):
|
||||
"""
|
||||
Decorator to make a view only accept particular request methods. Usage::
|
||||
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def my_view(request):
|
||||
# I can assume now that only GET or POST requests make it this far
|
||||
# ...
|
||||
|
||||
Note that request methods ARE case sensitive.
|
||||
"""
|
||||
def decorator(func):
|
||||
def inner(request, *args, **kwargs):
|
||||
method = request.META.get("REQUEST_METHOD", None)
|
||||
if method not in request_method_list:
|
||||
raise HttpResponseForbidden("REQUEST_METHOD '%s' not allowed" % method)
|
||||
return func(request, *args, **kwargs)
|
||||
return inner
|
||||
return decorator
|
||||
|
||||
require_GET = require_http_methods(["GET"])
|
||||
require_GET.__doc__ = "Decorator to require that a view only accept the GET method."
|
||||
|
||||
require_POST = require_http_methods(["POST"])
|
||||
require_POST.__doc__ = "Decorator to require that a view only accept the POST method."
|
@ -1,10 +1,11 @@
|
||||
from django.core.exceptions import Http404, ObjectDoesNotExist
|
||||
from django.core.template import Context, loader
|
||||
from django.models.core import sites
|
||||
from django.models.core import sites, contenttypes
|
||||
from django.utils import httpwrappers
|
||||
|
||||
def shortcut(request, content_type_id, object_id):
|
||||
from django.models.core import contenttypes
|
||||
"""Redirect to an object's page based on a content-type ID and an object ID"""
|
||||
# Look up the object, making sure it's got a get_absolute_url() function.
|
||||
try:
|
||||
content_type = contenttypes.get_object(pk=content_type_id)
|
||||
obj = content_type.get_object_for_this_type(pk=object_id)
|
||||
@ -14,25 +15,37 @@ def shortcut(request, content_type_id, object_id):
|
||||
absurl = obj.get_absolute_url()
|
||||
except AttributeError:
|
||||
raise Http404, "%s objects don't have get_absolute_url() methods" % content_type.name
|
||||
|
||||
# Try to figure out the object's domain so we can do a cross-site redirect
|
||||
# if necessary
|
||||
|
||||
# If the object actually defines a domain, we're done.
|
||||
if absurl.startswith('http://'):
|
||||
return httpwrappers.HttpResponseRedirect(absurl)
|
||||
|
||||
object_domain = None
|
||||
|
||||
# Next, look for an many-to-many relationship to sites
|
||||
if hasattr(obj, 'get_site_list'):
|
||||
site_list = obj.get_site_list()
|
||||
if site_list:
|
||||
object_domain = site_list[0].domain
|
||||
|
||||
# Next, look for a many-to-one relationship to sites
|
||||
elif hasattr(obj, 'get_site'):
|
||||
try:
|
||||
object_domain = obj.get_site().domain
|
||||
except sites.SiteDoesNotExist:
|
||||
pass
|
||||
|
||||
# Then, fall back to the current site (if possible)
|
||||
else:
|
||||
try:
|
||||
object_domain = sites.get_current().domain
|
||||
except sites.SiteDoesNotExist:
|
||||
pass
|
||||
if not object_domain:
|
||||
return httpwrappers.HttpResponseRedirect(absurl)
|
||||
return httpwrappers.HttpResponseRedirect('http://%s%s' % (object_domain, absurl))
|
||||
# Finally, give up and use a URL without the domain name
|
||||
return httpwrappers.HttpResponseRedirect(obj.get_absolute_url())
|
||||
return httpwrappers.HttpResponseRedirect('http://%s%s' % (object_domain, obj.get_absolute_url()))
|
||||
|
||||
def page_not_found(request):
|
||||
"""
|
||||
|
@ -29,7 +29,9 @@ Examples:
|
||||
CACHE_BACKEND Explanation
|
||||
============================== ===========================================
|
||||
memcached://127.0.0.1:11211/ A memcached backend; the server is running
|
||||
on localhost port 11211.
|
||||
on localhost port 11211. You can use
|
||||
multiple memcached servers by separating
|
||||
them with semicolons.
|
||||
|
||||
db://tablename/ A database backend in a table named
|
||||
"tablename". This table should be created
|
||||
@ -270,6 +272,40 @@ and a list/tuple of header names as its second argument.
|
||||
|
||||
.. _`HTTP Vary headers`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
|
||||
|
||||
Controlling cache: Using Vary headers
|
||||
=====================================
|
||||
|
||||
Another problem with caching is the privacy of data, and the question where data can
|
||||
be stored in a cascade of caches. A user usually faces two kinds of caches: his own
|
||||
browser cache (a private cache) and his providers cache (a public cache). A public cache
|
||||
is used by multiple users and controlled by someone else. This poses problems with private
|
||||
(in the sense of sensitive) data - you don't want your social security number or your
|
||||
banking account numbers stored in some public cache. So web applications need a way
|
||||
to tell the caches what data is private and what is public.
|
||||
|
||||
Other aspects are the definition how long a page should be cached at max, or wether the
|
||||
cache should allways check for newer versions and only deliver the cache content when
|
||||
there were no changes (some caches might deliver cached content even if the server page
|
||||
changed - just because the cache copy isn't yet expired).
|
||||
|
||||
So there are a multitude of options you can control for your pages. This is where the
|
||||
Cache-Control header (more infos in `HTTP Cache-Control headers`_) comes in. The usage
|
||||
is quite simple::
|
||||
|
||||
@cache_control(private=True, must_revalidate=True, max_age=3600)
|
||||
def my_view(request):
|
||||
...
|
||||
|
||||
This would define the view as private, to be revalidated on every access and cache
|
||||
copies will only be stored for 3600 seconds at max.
|
||||
|
||||
The caching middleware already set's this header up with a max-age of the CACHE_MIDDLEWARE_SETTINGS
|
||||
setting. And the cache_page decorator does the same. The cache_control decorator correctly merges
|
||||
different values into one big header, though. But you should take into account that middlewares
|
||||
might overwrite some of your headers or set their own defaults if you don't give that header yourself.
|
||||
|
||||
.. _`HTTP Cache-Control headers`: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
||||
|
||||
Other optimizations
|
||||
===================
|
||||
|
||||
|
@ -750,11 +750,12 @@ Here's a list of all possible ``META`` options. No options are required. Adding
|
||||
``permissions``
|
||||
Extra permissions to enter into the permissions table when creating this
|
||||
object. Add, delete and change permissions are automatically created for
|
||||
each object. This option specifies extra permissions::
|
||||
each object that has ``admin`` set. This example specifies an extra
|
||||
permission, ``can_deliver_pizzas``::
|
||||
|
||||
permissions = (("can_deliver_pizzas", "Can deliver pizzas"),)
|
||||
|
||||
This is a list of 2-tuples of
|
||||
This is a list or tuple of 2-tuples in the format
|
||||
``(permission_code, human_readable_permission_name)``.
|
||||
|
||||
``unique_together``
|
||||
|
20
tests/othertests/defaultfilters.py
Normal file
20
tests/othertests/defaultfilters.py
Normal file
@ -0,0 +1,20 @@
|
||||
"""
|
||||
>>> floatformat(7.7, None)
|
||||
'7.7'
|
||||
>>> floatformat(7.0, None)
|
||||
'7'
|
||||
>>> floatformat(0.7, None)
|
||||
'0.7'
|
||||
>>> floatformat(0.07, None)
|
||||
'0.1'
|
||||
>>> floatformat(0.007, None)
|
||||
'0.0'
|
||||
>>> floatformat(0.0, None)
|
||||
'0'
|
||||
"""
|
||||
|
||||
from django.core.template.defaultfilters import *
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
Loading…
x
Reference in New Issue
Block a user