Skip to content

Commit 9dda4ab

Browse files
MERGED NEW-ADMIN BRANCH (except for po/mo files, which will come in a separate commit)
git-svn-id: http://code.djangoproject.com/svn/django/trunk@1434 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 4fe5c9b commit 9dda4ab

30 files changed

+2062
-1125
lines changed

django/bin/validate.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ def validate_class(klass):
1616
assert isinstance(f.rel, meta.ManyToMany), \
1717
"ManyToManyField %s should have 'rel' set to a ManyToMany instance." % f.name
1818
# Inline related objects.
19-
for rel_opts, rel_field in opts.get_inline_related_objects():
20-
assert len([f for f in rel_opts.fields if f.core]) > 0, \
19+
for related in opts.get_followed_related_objects():
20+
assert len([f for f in related.opts.fields if f.core]) > 0, \
2121
"At least one field in %s should have core=True, because it's being edited inline by %s." % \
22-
(rel_opts.object_name, opts.object_name)
22+
(related.opts.object_name, opts.object_name)
2323
# All related objects.
2424
related_apps_seen = []
25-
for rel_opts, rel_field in opts.get_all_related_objects():
26-
if rel_opts in related_apps_seen:
27-
assert rel_field.rel.related_name is not None, \
25+
for related in opts.get_all_related_objects():
26+
if related.opts in related_apps_seen:
27+
assert related.field.rel.related_name is not None, \
2828
"Relationship in field %s.%s needs to set 'related_name' because more than one" \
2929
" %s object is referenced in %s." % \
30-
(rel_opts.object_name, rel_field.name, opts.object_name, rel_opts.object_name)
31-
related_apps_seen.append(rel_opts)
30+
(related.opts.object_name, related.field.name, opts.object_name, rel_opts.object_name)
31+
related_apps_seen.append(related.opts)
3232
# Etc.
3333
if opts.admin is not None:
3434
assert opts.admin.ordering or opts.ordering, \
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"""
2+
FilterSpec encapsulates the logic for displaying filters in the Django admin.
3+
Filters are specified in models with the "list_filter" option.
4+
5+
Each filter subclass knows how to display a filter for a field that passes a
6+
certain test -- e.g. being a DateField or ForeignKey.
7+
"""
8+
9+
from django.core import meta
10+
import datetime
11+
12+
class FilterSpec(object):
13+
filter_specs = []
14+
def __init__(self, f, request, params):
15+
self.field = f
16+
self.params = params
17+
18+
def register(cls, test, factory):
19+
cls.filter_specs.append( (test, factory) )
20+
register = classmethod(register)
21+
22+
def create(cls, f, request, params):
23+
for test, factory in cls.filter_specs:
24+
if test(f):
25+
return factory(f, request, params)
26+
create = classmethod(create)
27+
28+
def has_output(self):
29+
return True
30+
31+
def choices(self, cl):
32+
raise NotImplementedError()
33+
34+
def title(self):
35+
return self.field.verbose_name
36+
37+
def output(self, cl):
38+
t = []
39+
if self.has_output():
40+
t.append(_('<h3>By %s:</h3>\n<ul>\n') % self.title())
41+
42+
for choice in self.choices(cl):
43+
t.append('<li%s><a href="%s">%s</a></li>\n' % \
44+
((choice['selected'] and ' class="selected"' or ''),
45+
choice['query_string'] ,
46+
choice['display']))
47+
t.append('</ul>\n\n')
48+
return "".join(t)
49+
50+
class RelatedFilterSpec(FilterSpec):
51+
def __init__(self, f, request, params):
52+
super(RelatedFilterSpec, self).__init__(f, request, params)
53+
if isinstance(f, meta.ManyToManyField):
54+
self.lookup_title = f.rel.to.verbose_name
55+
else:
56+
self.lookup_title = f.verbose_name
57+
self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to.pk.name)
58+
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
59+
self.lookup_choices = f.rel.to.get_model_module().get_list()
60+
61+
def has_output(self):
62+
return len(self.lookup_choices) > 1
63+
64+
def title(self):
65+
return self.lookup_title
66+
67+
def choices(self, cl):
68+
yield {'selected': self.lookup_val is None,
69+
'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
70+
'display': _('All')}
71+
for val in self.lookup_choices:
72+
pk_val = getattr(val, self.field.rel.to.pk.attname)
73+
yield {'selected': self.lookup_val == str(pk_val),
74+
'query_string': cl.get_query_string( {self.lookup_kwarg: pk_val}),
75+
'display': val}
76+
77+
FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)
78+
79+
class ChoicesFilterSpec(FilterSpec):
80+
def __init__(self, f, request, params):
81+
super(ChoicesFilterSpec, self).__init__(f, request, params)
82+
self.lookup_kwarg = '%s__exact' % f.name
83+
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
84+
85+
def choices(self, cl):
86+
yield {'selected': self.lookup_val is None,
87+
'query_string': cl.get_query_string( {}, [self.lookup_kwarg]),
88+
'display': _('All')}
89+
for k, v in self.field.choices:
90+
yield {'selected': str(k) == self.lookup_val,
91+
'query_string': cl.get_query_string( {self.lookup_kwarg: k}),
92+
'display': v}
93+
94+
FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)
95+
96+
class DateFieldFilterSpec(FilterSpec):
97+
def __init__(self, f, request, params):
98+
super(DateFieldFilterSpec, self).__init__(f, request, params)
99+
100+
self.field_generic = '%s__' % self.field.name
101+
102+
self.date_params = dict([(k, v) for k, v in params.items() if k.startswith(self.field_generic)])
103+
104+
today = datetime.date.today()
105+
one_week_ago = today - datetime.timedelta(days=7)
106+
today_str = isinstance(self.field, meta.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
107+
108+
self.links = (
109+
(_('Any date'), {}),
110+
(_('Today'), {'%s__year' % self.field.name: str(today.year),
111+
'%s__month' % self.field.name: str(today.month),
112+
'%s__day' % self.field.name: str(today.day)}),
113+
(_('Past 7 days'), {'%s__gte' % self.field.name: one_week_ago.strftime('%Y-%m-%d'),
114+
'%s__lte' % f.name: today_str}),
115+
(_('This month'), {'%s__year' % self.field.name: str(today.year),
116+
'%s__month' % f.name: str(today.month)}),
117+
(_('This year'), {'%s__year' % self.field.name: str(today.year)})
118+
)
119+
120+
def title(self):
121+
return self.field.verbose_name
122+
123+
def choices(self, cl):
124+
for title, param_dict in self.links:
125+
yield {'selected': self.date_params == param_dict,
126+
'query_string': cl.get_query_string( param_dict, self.field_generic),
127+
'display': title}
128+
129+
FilterSpec.register(lambda f: isinstance(f, meta.DateField), DateFieldFilterSpec)
130+
131+
class BooleanFieldFilterSpec(FilterSpec):
132+
def __init__(self, f, request, params):
133+
super(BooleanFieldFilterSpec, self).__init__(f, request, params)
134+
self.lookup_kwarg = '%s__exact' % f.name
135+
self.lookup_kwarg2 = '%s__isnull' % f.name
136+
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
137+
self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None)
138+
139+
def title(self):
140+
return self.field.verbose_name
141+
142+
def choices(self, cl):
143+
for k, v in ((_('All'), None), (_('Yes'), '1'), (_('No'), '0')):
144+
yield {'selected': self.lookup_val == v and not self.lookup_val2,
145+
'query_string': cl.get_query_string( {self.lookup_kwarg: v}, [self.lookup_kwarg2]),
146+
'display': k}
147+
if isinstance(self.field, meta.NullBooleanField):
148+
yield {'selected': self.lookup_val2 == 'True',
149+
'query_string': cl.get_query_string( {self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]),
150+
'display': _('Unknown')}
151+
152+
FilterSpec.register(lambda f: isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField), BooleanFieldFilterSpec)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{% extends "admin/base_site" %}
2+
{% load i18n %}
3+
{% load admin_modify %}
4+
{% load adminmedia %}
5+
{% block extrahead %}
6+
{% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
7+
{% endblock %}
8+
{% block coltype %}{{ bound_manipulator.coltype }}{% endblock %}
9+
{% block bodyclass %}{{ app_label }}-{{ bound_manipulator.object_name.lower }} change-form{% endblock %}
10+
{% block breadcrumbs %}{% if not is_popup %}
11+
<div class="breadcrumbs">
12+
<a href="../../../">{% trans "Home" %}</a> &rsaquo;
13+
<a href="../">{{ bound_manipulator.verbose_name_plural|capfirst }}</a> &rsaquo;
14+
{% if add %}{% trans "Add" %} {{ bound_manipulator.verbose_name }}{% else %}{{ bound_manipulator.original|striptags|truncatewords:"18" }}{% endif %}
15+
</div>
16+
{% endif %}{% endblock %}
17+
{% block content %}<div id="content-main">
18+
{% if change %}{% if not is_popup %}
19+
<ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
20+
{% if bound_manipulator.has_absolute_url %}<li><a href="/r/{{ bound_manipulator.content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
21+
</ul>
22+
{% endif %}{% endif %}
23+
<form {{ bound_manipulator.form_enc_attrib }} action='{{ form_url }}' method="post">{% block form_top %}{% endblock %}
24+
{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}
25+
{% if bound_manipulator.save_on_top %}{% submit_row bound_manipulator %}{% endif %}
26+
{% if form.error_dict %}
27+
<p class="errornote">
28+
{% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
29+
</p>
30+
{% endif %}
31+
{% for bound_field_set in bound_manipulator.bound_field_sets %}
32+
<fieldset class="module aligned {{ bound_field_set.classes }}">
33+
{% if bound_field_set.name %}<h2>{{ bound_field_set.name }}</h2>{% endif %}
34+
{% for bound_field_line in bound_field_set %}
35+
{% admin_field_line bound_field_line %}
36+
{% for bound_field in bound_field_line %}
37+
{% filter_interface_script_maybe bound_field %}
38+
{% endfor %}
39+
{% endfor %}
40+
</fieldset>
41+
{% endfor %}
42+
{% block after_field_sets %}{% endblock %}
43+
{% if change %}
44+
{% if bound_manipulator.ordered_objects %}
45+
<fieldset class="module"><h2>{% trans "Ordering" %}</h2>
46+
<div class="form-row{% if form.order_.errors %} error{% endif %} ">
47+
{% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}
48+
<p><label for="id_order_">{% trans "Order:" %}</label> {{ form.order_ }}</p>
49+
</div></fieldset>
50+
{% endif %}
51+
{% endif %}
52+
{% for related_object in bound_manipulator.inline_related_objects %}{% edit_inline related_object %}{% endfor %}
53+
{% block after_related_objects %}{% endblock %}
54+
{% submit_row bound_manipulator %}
55+
{% if add %}
56+
<script type="text/javascript">document.getElementById("{{ bound_manipulator.first_form_field_id }}").focus();</script>
57+
{% endif %}
58+
{% if bound_manipulator.auto_populated_fields %}
59+
<script type="text/javascript">
60+
{% auto_populated_field_script bound_manipulator.auto_populated_fields change %}
61+
</script>
62+
{% endif %}
63+
{% if change %}
64+
{% if bound_manipulator.ordered_objects %}
65+
{% if form.order_objects %}<ul id="orderthese">
66+
{% for object in form.order_objects %}
67+
<li id="p{% object_pk bound_manipulator object %}">
68+
<span id="handlep{% object_pk bound_manipulator object %}">{{ object|truncatewords:"5" }}</span>
69+
</li>
70+
{% endfor %}
71+
</ul>{% endif %}
72+
{% endif %}
73+
{% endif %}
74+
</form></div>
75+
{% endblock %}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{% load admin_list %}
2+
{% load i18n %}
3+
{% extends "admin/base_site" %}
4+
{% block bodyclass %}change-list{% endblock %}
5+
{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; {{ cl.opts.verbose_name_plural|capfirst }} </div>{% endblock %}{% endif %}
6+
{% block coltype %}flex{% endblock %}
7+
{% block content %}
8+
<div id="content-main">
9+
{% if has_add_permission %}
10+
<ul class="object-tools"><li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
11+
{% endif %}
12+
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
13+
{% search_form cl %}
14+
{% date_hierarchy cl %}
15+
{% filters cl %}
16+
{% result_list cl %}
17+
{% pagination cl %}
18+
</div>
19+
</div>
20+
{% endblock %}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<table cellspacing="0">
2+
<thead>
3+
<tr>
4+
{% for header in result_headers %}<th{{ header.class_attrib }}>
5+
{% if header.sortable %}<a href="{{ header.url }}">{% endif %}
6+
{{ header.text|capfirst }}
7+
{% if header.sortable %}</a>{% endif %}</th>{% endfor %}
8+
</tr>
9+
</thead>
10+
{% for result in results %}
11+
<tr class="{% cycle row1,row2 %}">{% for item in result %}{{ item }}{% endfor %}</tr>
12+
{% endfor %}
13+
</table>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{% if show %}
2+
<div class="xfull">
3+
<ul class="toplinks">
4+
{% if back %}<li class="date-back"><a href="{{ back.link }}">&lsaquo; {{ back.title }}</a></li>{% endif %}
5+
{% for choice in choices %}
6+
<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title }}{% if choice.link %}</a>{% endif %}</li>
7+
{% endfor %}
8+
</ul><br class="clear" />
9+
</div>
10+
{% endif %}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<fieldset class="module aligned">
2+
{% for fcw in bound_related_object.form_field_collection_wrappers %}
3+
<h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2>
4+
{% if bound_related_object.show_url %}{% if fcw.obj.original %}
5+
<p><a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a></p>
6+
{% endif %}{% endif %}
7+
{% for bound_field in fcw.bound_fields %}
8+
{% if bound_field.hidden %}
9+
{% field_widget bound_field %}
10+
{% else %}
11+
{% admin_field_line bound_field %}
12+
{% endif %}
13+
{% endfor %}
14+
{% endfor %}
15+
</fieldset>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<fieldset class="module">
2+
<h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
3+
<thead><tr>
4+
{% for fw in bound_related_object.field_wrapper_list %}
5+
{% if fw.needs_header %}
6+
<th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th>
7+
{% endif %}
8+
{% endfor %}
9+
{% for fcw in bound_related_object.form_field_collection_wrappers %}
10+
{% if change %}{% if original_row_needed %}
11+
{% if fcw.obj.original %}
12+
<tr class="row-label {% cycle row1,row2 %}"><td colspan="{{ num_headers }}"><strong>{{ fcw.obj.original }}</strong></tr>
13+
{% endif %}
14+
{% endif %}{% endif %}
15+
{% if fcw.obj.errors %}
16+
<tr class="errorlist"><td colspan="{{ num_headers }}">
17+
{{ fcw.obj.html_combined_error_list }}
18+
</tr>
19+
{% endif %}
20+
<tr class="{% cycle row1,row2 %}">
21+
{% for bound_field in fcw.bound_fields %}
22+
{% if not bound_field.hidden %}
23+
<td {{ bound_field.cell_class_attribute }}>
24+
{% field_widget bound_field %}
25+
</td>
26+
{% endif %}
27+
{% endfor %}
28+
{% if bound_related_object.show_url %}<td>
29+
{% if fcw.obj.original %}<a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a>{% endif %}
30+
</td>{% endif %}
31+
</tr>
32+
33+
{% endfor %} </table>
34+
35+
{% for fcw in bound_related_object.form_field_collection_wrappers %}
36+
{% for bound_field in fcw.bound_fields %}
37+
{% if bound_field.hidden %}
38+
{% field_widget bound_field %}
39+
{% endif %}
40+
{% endfor %}
41+
{% endfor %}
42+
</fieldset>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<div class="{{ class_names }}" >
2+
{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
3+
{% for bound_field in bound_fields %}
4+
{% if bound_field.has_label_first %}
5+
{% field_label bound_field %}
6+
{% endif %}
7+
{% field_widget bound_field %}
8+
{% if not bound_field.has_label_first %}
9+
{% field_label bound_field %}
10+
{% endif %}
11+
{% if change %}
12+
{% if bound_field.field.primary_key %}
13+
{{ bound_field.original_value }}
14+
{% endif %}
15+
{% if bound_field.raw_id_admin %}
16+
{% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14" }}</strong>{% endif %}
17+
{% endif %}
18+
{% endif %}
19+
{% if bound_field.field.help_text %}<p class="help">{{ bound_field.field.help_text }}</p>{% endif %}
20+
{% endfor %}
21+
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<h3>{% blocktrans %} By {{ title }} {% endblocktrans %}</h3>
2+
<ul>
3+
{% for choice in choices %}
4+
<li{% if choice.selected %} class="selected"{% endif %}>
5+
<a href="{{ choice.query_string }}">{{ choice.display }}</a></li>
6+
{% endfor %}
7+
</ul>

0 commit comments

Comments
 (0)