Permalink
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
  • Loading branch information...
1 parent 4fe5c9b commit 9dda4abee1225db7a7b195b84c915fdd141a7260 @adrianholovaty adrianholovaty committed Nov 25, 2005
Showing with 2,062 additions and 1,125 deletions.
  1. +8 −8 django/bin/validate.py
  2. +152 −0 django/contrib/admin/filterspecs.py
  3. +75 −0 django/contrib/admin/templates/admin/change_form.html
  4. +20 −0 django/contrib/admin/templates/admin/change_list.html
  5. +13 −0 django/contrib/admin/templates/admin/change_list_results.html
  6. +10 −0 django/contrib/admin/templates/admin/date_hierarchy.html
  7. +15 −0 django/contrib/admin/templates/admin/edit_inline_stacked.html
  8. +42 −0 django/contrib/admin/templates/admin/edit_inline_tabular.html
  9. +21 −0 django/contrib/admin/templates/admin/field_line.html
  10. +7 −0 django/contrib/admin/templates/admin/filter.html
  11. +5 −0 django/contrib/admin/templates/admin/filters.html
  12. +9 −0 django/contrib/admin/templates/admin/pagination.html
  13. +14 −0 django/contrib/admin/templates/admin/search_form.html
  14. +8 −0 django/contrib/admin/templates/admin/submit_line.html
  15. +12 −10 django/contrib/admin/templates/admin_doc/bookmarklets.html
  16. +4 −0 django/contrib/admin/templates/widget/date_time.html
  17. +1 −0 django/contrib/admin/templates/widget/default.html
  18. +4 −0 django/contrib/admin/templates/widget/file.html
  19. +7 −0 django/contrib/admin/templates/widget/foreign.html
  20. +1 −0 django/contrib/admin/templates/widget/many_to_many.html
  21. +278 −0 django/contrib/admin/templatetags/admin_list.py
  22. +258 −0 django/contrib/admin/templatetags/admin_modify.py
  23. +431 −876 django/contrib/admin/views/main.py
  24. +130 −24 django/core/formfields.py
  25. +3 −3 django/core/management.py
  26. +307 −145 django/core/meta/__init__.py
  27. +188 −25 django/core/meta/fields.py
  28. +29 −26 django/models/__init__.py
  29. +9 −8 django/views/generic/create_update.py
  30. +1 −0 setup.py
@@ -16,19 +16,19 @@ def validate_class(klass):
assert isinstance(f.rel, meta.ManyToMany), \
"ManyToManyField %s should have 'rel' set to a ManyToMany instance." % f.name
# Inline related objects.
- for rel_opts, rel_field in opts.get_inline_related_objects():
- assert len([f for f in rel_opts.fields if f.core]) > 0, \
+ for related in opts.get_followed_related_objects():
+ assert len([f for f in related.opts.fields if f.core]) > 0, \
"At least one field in %s should have core=True, because it's being edited inline by %s." % \
- (rel_opts.object_name, opts.object_name)
+ (related.opts.object_name, opts.object_name)
# All related objects.
related_apps_seen = []
- for rel_opts, rel_field in opts.get_all_related_objects():
- if rel_opts in related_apps_seen:
- assert rel_field.rel.related_name is not None, \
+ for related in opts.get_all_related_objects():
+ if related.opts in related_apps_seen:
+ assert related.field.rel.related_name is not None, \
"Relationship in field %s.%s needs to set 'related_name' because more than one" \
" %s object is referenced in %s." % \
- (rel_opts.object_name, rel_field.name, opts.object_name, rel_opts.object_name)
- related_apps_seen.append(rel_opts)
+ (related.opts.object_name, related.field.name, opts.object_name, rel_opts.object_name)
+ related_apps_seen.append(related.opts)
# Etc.
if opts.admin is not None:
assert opts.admin.ordering or opts.ordering, \
@@ -0,0 +1,152 @@
+"""
+FilterSpec encapsulates the logic for displaying filters in the Django admin.
+Filters are specified in models with the "list_filter" option.
+
+Each filter subclass knows how to display a filter for a field that passes a
+certain test -- e.g. being a DateField or ForeignKey.
+"""
+
+from django.core import meta
+import datetime
+
+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 choices(self, cl):
+ raise NotImplementedError()
+
+ def title(self):
+ return self.field.verbose_name
+
+ 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(cl):
+ t.append('<li%s><a href="%s">%s</a></li>\n' % \
+ ((choice['selected'] and ' class="selected"' or ''),
+ choice['query_string'] ,
+ choice['display']))
+ 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 title(self):
+ return self.lookup_title
+
+ def choices(self, cl):
+ yield {'selected': self.lookup_val is None,
+ 'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
+ 'display': _('All')}
+ for val in self.lookup_choices:
+ pk_val = getattr(val, self.field.rel.to.pk.attname)
+ yield {'selected': self.lookup_val == str(pk_val),
+ 'query_string': cl.get_query_string( {self.lookup_kwarg: pk_val}),
+ 'display': val}
+
+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(self.lookup_kwarg, None)
+
+ def choices(self, cl):
+ yield {'selected': self.lookup_val is None,
+ 'query_string': cl.get_query_string( {}, [self.lookup_kwarg]),
+ 'display': _('All')}
+ for k, v in self.field.choices:
+ yield {'selected': str(k) == self.lookup_val,
+ 'query_string': cl.get_query_string( {self.lookup_kwarg: k}),
+ 'display': v}
+
+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(self.field_generic)])
+
+ today = datetime.date.today()
+ one_week_ago = today - datetime.timedelta(days=7)
+ today_str = isinstance(self.field, 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 title(self):
+ return self.field.verbose_name
+
+ def choices(self, cl):
+ for title, param_dict in self.links:
+ yield {'selected': self.date_params == param_dict,
+ 'query_string': cl.get_query_string( param_dict, self.field_generic),
+ 'display': title}
+
+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 title(self):
+ return self.field.verbose_name
+
+ def choices(self, cl):
+ for k, v in ((_('All'), None), (_('Yes'), '1'), (_('No'), '0')):
+ yield {'selected': self.lookup_val == v and not self.lookup_val2,
+ 'query_string': cl.get_query_string( {self.lookup_kwarg: v}, [self.lookup_kwarg2]),
+ 'display': k}
+ if isinstance(self.field, meta.NullBooleanField):
+ yield {'selected': self.lookup_val2 == 'True',
+ 'query_string': cl.get_query_string( {self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]),
+ 'display': _('Unknown')}
+
+FilterSpec.register(lambda f: isinstance(f, meta.BooleanField) or isinstance(f, meta.NullBooleanField), BooleanFieldFilterSpec)
@@ -0,0 +1,75 @@
+{% extends "admin/base_site" %}
+{% load i18n %}
+{% load admin_modify %}
+{% load adminmedia %}
+{% block extrahead %}
+{% for js in bound_manipulator.javascript_imports %}{% include_admin_script js %}{% endfor %}
+{% 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="../../../">{% trans "Home" %}</a> &rsaquo;
+ <a href="../">{{ bound_manipulator.verbose_name_plural|capfirst }}</a> &rsaquo;
+ {% if add %}{% trans "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">{% trans "History" %}</a></li>
+ {% 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%}
+ </ul>
+{% endif %}{% endif %}
+<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 bound_manipulator.save_on_top %}{% submit_row bound_manipulator %}{% endif %}
+{% if form.error_dict %}
+ <p class="errornote">
+ {% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
+ </p>
+{% endif %}
+{% 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 %}
+ {% admin_field_line bound_field_line %}
+ {% for bound_field in bound_field_line %}
+ {% filter_interface_script_maybe bound_field %}
+ {% endfor %}
+ {% endfor %}
+ </fieldset>
+{% endfor %}
+{% block after_field_sets %}{% endblock %}
+{% if change %}
+ {% if bound_manipulator.ordered_objects %}
+ <fieldset class="module"><h2>{% trans "Ordering" %}</h2>
+ <div class="form-row{% if form.order_.errors %} error{% endif %} ">
+ {% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}
+ <p><label for="id_order_">{% trans "Order:" %}</label> {{ form.order_ }}</p>
+ </div></fieldset>
+ {% endif %}
+{% endif %}
+{% for related_object in bound_manipulator.inline_related_objects %}{% edit_inline related_object %}{% endfor %}
+{% block after_related_objects %}{% endblock %}
+{% submit_row bound_manipulator %}
+{% if add %}
+ <script type="text/javascript">document.getElementById("{{ bound_manipulator.first_form_field_id }}").focus();</script>
+{% endif %}
+{% if bound_manipulator.auto_populated_fields %}
+ <script type="text/javascript">
+ {% auto_populated_field_script bound_manipulator.auto_populated_fields change %}
+ </script>
+{% endif %}
+{% if change %}
+ {% if bound_manipulator.ordered_objects %}
+ {% if form.order_objects %}<ul id="orderthese">
+ {% for object in form.order_objects %}
+ <li id="p{% object_pk bound_manipulator object %}">
+ <span id="handlep{% object_pk bound_manipulator object %}">{{ object|truncatewords:"5" }}</span>
+ </li>
+ {% endfor %}
+ </ul>{% endif %}
+ {% endif %}
+{% endif %}
+</form></div>
+{% endblock %}
@@ -0,0 +1,20 @@
+{% load admin_list %}
+{% load i18n %}
+{% extends "admin/base_site" %}
+{% block bodyclass %}change-list{% endblock %}
+{% if not is_popup %}{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; {{ 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">{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}</a></li></ul>
+{% 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 %}
@@ -0,0 +1,13 @@
+<table cellspacing="0">
+<thead>
+<tr>
+{% for header in result_headers %}<th{{ header.class_attrib }}>
+{% if header.sortable %}<a href="{{ header.url }}">{% endif %}
+{{ header.text|capfirst }}
+{% if header.sortable %}</a>{% endif %}</th>{% endfor %}
+</tr>
+</thead>
+{% for result in results %}
+<tr class="{% cycle row1,row2 %}">{% for item in result %}{{ item }}{% endfor %}</tr>
+{% endfor %}
+</table>
@@ -0,0 +1,10 @@
+{% if show %}
+<div class="xfull">
+<ul class="toplinks">
+{% if back %}<li class="date-back"><a href="{{ back.link }}">&lsaquo; {{ back.title }}</a></li>{% endif %}
+{% for choice in choices %}
+<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title }}{% if choice.link %}</a>{% endif %}</li>
+{% endfor %}
+</ul><br class="clear" />
+</div>
+{% endif %}
@@ -0,0 +1,15 @@
+<fieldset class="module aligned">
+ {% for fcw in bound_related_object.form_field_collection_wrappers %}
+ <h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }}&nbsp;#{{ forloop.counter }}</h2>
+ {% if bound_related_object.show_url %}{% if fcw.obj.original %}
+ <p><a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a></p>
+ {% endif %}{% endif %}
+ {% for bound_field in fcw.bound_fields %}
+ {% if bound_field.hidden %}
+ {% field_widget bound_field %}
+ {% else %}
+ {% admin_field_line bound_field %}
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+</fieldset>
@@ -0,0 +1,42 @@
+<fieldset class="module">
+ <h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
+ <thead><tr>
+ {% for fw in bound_related_object.field_wrapper_list %}
+ {% if fw.needs_header %}
+ <th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th>
+ {% endif %}
+ {% endfor %}
+ {% for fcw in bound_related_object.form_field_collection_wrappers %}
+ {% if change %}{% if original_row_needed %}
+ {% if fcw.obj.original %}
+ <tr class="row-label {% cycle row1,row2 %}"><td colspan="{{ num_headers }}"><strong>{{ fcw.obj.original }}</strong></tr>
+ {% endif %}
+ {% endif %}{% endif %}
+ {% if fcw.obj.errors %}
+ <tr class="errorlist"><td colspan="{{ num_headers }}">
+ {{ fcw.obj.html_combined_error_list }}
+ </tr>
+ {% endif %}
+ <tr class="{% cycle row1,row2 %}">
+ {% for bound_field in fcw.bound_fields %}
+ {% if not bound_field.hidden %}
+ <td {{ bound_field.cell_class_attribute }}>
+ {% field_widget bound_field %}
+ </td>
+ {% endif %}
+ {% endfor %}
+ {% if bound_related_object.show_url %}<td>
+ {% if fcw.obj.original %}<a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a>{% endif %}
+ </td>{% endif %}
+ </tr>
+
+ {% endfor %} </table>
+
+ {% for fcw in bound_related_object.form_field_collection_wrappers %}
+ {% for bound_field in fcw.bound_fields %}
+ {% if bound_field.hidden %}
+ {% field_widget bound_field %}
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+</fieldset>
@@ -0,0 +1,21 @@
+<div class="{{ class_names }}" >
+{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %}
+{% for bound_field in bound_fields %}
+ {% if bound_field.has_label_first %}
+ {% field_label bound_field %}
+ {% endif %}
+ {% field_widget bound_field %}
+ {% if not bound_field.has_label_first %}
+ {% field_label bound_field %}
+ {% endif %}
+ {% if change %}
+ {% if bound_field.field.primary_key %}
+ {{ bound_field.original_value }}
+ {% endif %}
+ {% if bound_field.raw_id_admin %}
+ {% if bound_field.existing_display %}&nbsp;<strong>{{ bound_field.existing_display|truncatewords:"14" }}</strong>{% endif %}
+ {% endif %}
+ {% endif %}
+ {% if bound_field.field.help_text %}<p class="help">{{ bound_field.field.help_text }}</p>{% endif %}
+{% endfor %}
+</div>
@@ -0,0 +1,7 @@
+<h3>{% blocktrans %} By {{ title }} {% endblocktrans %}</h3>
+<ul>
+{% for choice in choices %}
+ <li{% if choice.selected %} class="selected"{% endif %}>
+ <a href="{{ choice.query_string }}">{{ choice.display }}</a></li>
+{% endfor %}
+</ul>
@@ -0,0 +1,5 @@
+{% if cl.has_filters %}<div id="changelist-filter">
+<h2>Filter</h2>
+{% for spec in cl.filter_specs %}
+ {% filter cl spec %}
+{% endfor %}</div>{% endif %}
Oops, something went wrong.

0 comments on commit 9dda4ab

Please sign in to comment.