Fixed #27728 -- Allowed overriding admin templatetags templates.
This commit is contained in:
parent
6e52e2554d
commit
5cc28dc752
|
@ -1,7 +1,13 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
{% block actions %}
|
||||||
|
{% block actions-form %}
|
||||||
{% for field in action_form %}{% if field.label %}<label>{{ field.label }} {% endif %}{{ field }}{% if field.label %}</label>{% endif %}{% endfor %}
|
{% for field in action_form %}{% if field.label %}<label>{{ field.label }} {% endif %}{{ field }}{% if field.label %}</label>{% endif %}{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block actions-submit %}
|
||||||
<button type="submit" class="button" title="{% trans "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% trans "Go" %}</button>
|
<button type="submit" class="button" title="{% trans "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% trans "Go" %}</button>
|
||||||
|
{% endblock %}
|
||||||
|
{% block actions-counter %}
|
||||||
{% if actions_selection_counter %}
|
{% if actions_selection_counter %}
|
||||||
<span class="action-counter" data-actions-icnt="{{ cl.result_list|length }}">{{ selection_note }}</span>
|
<span class="action-counter" data-actions-icnt="{{ cl.result_list|length }}">{{ selection_note }}</span>
|
||||||
{% if cl.result_count != cl.result_list|length %}
|
{% if cl.result_count != cl.result_list|length %}
|
||||||
|
@ -12,4 +18,6 @@
|
||||||
<span class="clear"><a href="#">{% trans "Clear selection" %}</a></span>
|
<span class="clear"><a href="#">{% trans "Clear selection" %}</a></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -28,11 +28,7 @@
|
||||||
{% if change %}{% if not is_popup %}
|
{% if change %}{% if not is_popup %}
|
||||||
<ul class="object-tools">
|
<ul class="object-tools">
|
||||||
{% block object-tools-items %}
|
{% block object-tools-items %}
|
||||||
<li>
|
{% change_form_object_tools %}
|
||||||
{% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %}
|
|
||||||
<a href="{% add_preserved_filters history_url %}" class="historylink">{% trans "History" %}</a>
|
|
||||||
</li>
|
|
||||||
{% if has_absolute_url %}<li><a href="{{ absolute_url }}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}{% endif %}
|
{% endif %}{% endif %}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{% load i18n admin_urls %}
|
||||||
|
{% block object-tools-items %}
|
||||||
|
<li>
|
||||||
|
{% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %}
|
||||||
|
<a href="{% add_preserved_filters history_url %}" class="historylink">{% trans "History" %}</a>
|
||||||
|
</li>
|
||||||
|
{% if has_absolute_url %}<li><a href="{{ absolute_url }}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif %}
|
||||||
|
{% endblock %}
|
|
@ -42,14 +42,7 @@
|
||||||
{% block object-tools %}
|
{% block object-tools %}
|
||||||
<ul class="object-tools">
|
<ul class="object-tools">
|
||||||
{% block object-tools-items %}
|
{% block object-tools-items %}
|
||||||
{% if has_add_permission %}
|
{% change_list_object_tools %}
|
||||||
<li>
|
|
||||||
{% url cl.opts|admin_urlname:'add' as add_url %}
|
|
||||||
<a href="{% add_preserved_filters add_url is_popup to_field %}" class="addlink">
|
|
||||||
{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{% load i18n admin_urls %}
|
||||||
|
|
||||||
|
{% block object-tools-items %}
|
||||||
|
{% if has_add_permission %}
|
||||||
|
<li>
|
||||||
|
{% url cl.opts|admin_urlname:'add' as add_url %}
|
||||||
|
<a href="{% add_preserved_filters add_url is_popup to_field %}" class="addlink">
|
||||||
|
{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
|
@ -1,10 +1,16 @@
|
||||||
{% if show %}
|
{% if show %}
|
||||||
<div class="xfull">
|
<div class="xfull">
|
||||||
<ul class="toplinks">
|
<ul class="toplinks">
|
||||||
|
{% block date-hierarchy-toplinks %}
|
||||||
|
{% block date-hierarchy-back %}
|
||||||
{% if back %}<li class="date-back"><a href="{{ back.link }}">‹ {{ back.title }}</a></li>{% endif %}
|
{% if back %}<li class="date-back"><a href="{{ back.link }}">‹ {{ back.title }}</a></li>{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block date-hierarchy-choices %}
|
||||||
{% for choice in choices %}
|
{% for choice in choices %}
|
||||||
<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title }}{% if choice.link %}</a>{% endif %}</li>
|
<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title }}{% if choice.link %}</a>{% endif %}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
{% endblock %}
|
||||||
</ul><br class="clear">
|
</ul><br class="clear">
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% load i18n admin_urls %}
|
{% load i18n admin_urls %}
|
||||||
<div class="submit-row">
|
<div class="submit-row">
|
||||||
|
{% block submit-row %}
|
||||||
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save">{% endif %}
|
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save">{% endif %}
|
||||||
{% if show_delete_link %}
|
{% if show_delete_link %}
|
||||||
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
|
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
|
||||||
|
@ -8,4 +9,5 @@
|
||||||
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew">{% endif %}
|
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew">{% endif %}
|
||||||
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother">{% endif %}
|
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother">{% endif %}
|
||||||
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue">{% endif %}
|
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue">{% endif %}
|
||||||
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,6 +19,8 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from .base import InclusionAdminNode
|
||||||
|
|
||||||
register = Library()
|
register = Library()
|
||||||
|
|
||||||
DOT = '.'
|
DOT = '.'
|
||||||
|
@ -40,7 +42,6 @@ def paginator_number(cl, i):
|
||||||
i + 1)
|
i + 1)
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('admin/pagination.html')
|
|
||||||
def pagination(cl):
|
def pagination(cl):
|
||||||
"""
|
"""
|
||||||
Generate the series of links to the pages in a paginated list.
|
Generate the series of links to the pages in a paginated list.
|
||||||
|
@ -89,6 +90,16 @@ def pagination(cl):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@register.tag(name='pagination')
|
||||||
|
def pagination_tag(parser, token):
|
||||||
|
return InclusionAdminNode(
|
||||||
|
parser, token,
|
||||||
|
func=pagination,
|
||||||
|
template_name='pagination.html',
|
||||||
|
takes_context=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def result_headers(cl):
|
def result_headers(cl):
|
||||||
"""
|
"""
|
||||||
Generate the list column headers.
|
Generate the list column headers.
|
||||||
|
@ -314,7 +325,6 @@ def result_hidden_fields(cl):
|
||||||
yield mark_safe(form[cl.model._meta.pk.name])
|
yield mark_safe(form[cl.model._meta.pk.name])
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag("admin/change_list_results.html")
|
|
||||||
def result_list(cl):
|
def result_list(cl):
|
||||||
"""
|
"""
|
||||||
Display the headers and data list together.
|
Display the headers and data list together.
|
||||||
|
@ -331,7 +341,16 @@ def result_list(cl):
|
||||||
'results': list(results(cl))}
|
'results': list(results(cl))}
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('admin/date_hierarchy.html')
|
@register.tag(name='result_list')
|
||||||
|
def result_list_tag(parser, token):
|
||||||
|
return InclusionAdminNode(
|
||||||
|
parser, token,
|
||||||
|
func=result_list,
|
||||||
|
template_name='change_list_results.html',
|
||||||
|
takes_context=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def date_hierarchy(cl):
|
def date_hierarchy(cl):
|
||||||
"""
|
"""
|
||||||
Display the date hierarchy for date drill-down functionality.
|
Display the date hierarchy for date drill-down functionality.
|
||||||
|
@ -406,7 +425,16 @@ def date_hierarchy(cl):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('admin/search_form.html')
|
@register.tag(name='date_hierarchy')
|
||||||
|
def date_hierarchy_tag(parser, token):
|
||||||
|
return InclusionAdminNode(
|
||||||
|
parser, token,
|
||||||
|
func=date_hierarchy,
|
||||||
|
template_name='date_hierarchy.html',
|
||||||
|
takes_context=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def search_form(cl):
|
def search_form(cl):
|
||||||
"""
|
"""
|
||||||
Display a search form for searching the list.
|
Display a search form for searching the list.
|
||||||
|
@ -418,6 +446,11 @@ def search_form(cl):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@register.tag(name='search_form')
|
||||||
|
def search_form_tag(parser, token):
|
||||||
|
return InclusionAdminNode(parser, token, func=search_form, template_name='search_form.html', takes_context=False)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def admin_list_filter(cl, spec):
|
def admin_list_filter(cl, spec):
|
||||||
tpl = get_template(spec.template)
|
tpl = get_template(spec.template)
|
||||||
|
@ -428,7 +461,6 @@ def admin_list_filter(cl, spec):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('admin/actions.html', takes_context=True)
|
|
||||||
def admin_actions(context):
|
def admin_actions(context):
|
||||||
"""
|
"""
|
||||||
Track the number of times the action field has been rendered on the page,
|
Track the number of times the action field has been rendered on the page,
|
||||||
|
@ -436,3 +468,24 @@ def admin_actions(context):
|
||||||
"""
|
"""
|
||||||
context['action_index'] = context.get('action_index', -1) + 1
|
context['action_index'] = context.get('action_index', -1) + 1
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@register.tag(name='admin_actions')
|
||||||
|
def admin_actions_tag(parser, token):
|
||||||
|
return InclusionAdminNode(parser, token, func=admin_actions, template_name='actions.html')
|
||||||
|
|
||||||
|
|
||||||
|
def change_list_object_tools(context):
|
||||||
|
"""
|
||||||
|
Displays the row of change list object tools.
|
||||||
|
"""
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@register.tag(name='change_list_object_tools')
|
||||||
|
def change_list_object_tools_tag(parser, token):
|
||||||
|
return InclusionAdminNode(
|
||||||
|
parser, token,
|
||||||
|
func=change_list_object_tools,
|
||||||
|
template_name='change_list_object_tools.html',
|
||||||
|
)
|
||||||
|
|
|
@ -3,10 +3,11 @@ import json
|
||||||
from django import template
|
from django import template
|
||||||
from django.template.context import Context
|
from django.template.context import Context
|
||||||
|
|
||||||
|
from .base import InclusionAdminNode
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('admin/prepopulated_fields_js.html', takes_context=True)
|
|
||||||
def prepopulated_fields_js(context):
|
def prepopulated_fields_js(context):
|
||||||
"""
|
"""
|
||||||
Create a list of prepopulated_fields that should render Javascript for
|
Create a list of prepopulated_fields that should render Javascript for
|
||||||
|
@ -39,7 +40,11 @@ def prepopulated_fields_js(context):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('admin/submit_line.html', takes_context=True)
|
@register.tag(name='prepopulated_fields_js')
|
||||||
|
def prepopulated_fields_js_tag(parser, token):
|
||||||
|
return InclusionAdminNode(parser, token, func=prepopulated_fields_js, template_name="prepopulated_fields_js.html")
|
||||||
|
|
||||||
|
|
||||||
def submit_row(context):
|
def submit_row(context):
|
||||||
"""
|
"""
|
||||||
Display the row of buttons for delete and save.
|
Display the row of buttons for delete and save.
|
||||||
|
@ -66,6 +71,27 @@ def submit_row(context):
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
@register.tag(name='submit_row')
|
||||||
|
def submit_row_tag(parser, token):
|
||||||
|
return InclusionAdminNode(parser, token, func=submit_row, template_name='submit_line.html')
|
||||||
|
|
||||||
|
|
||||||
|
def change_form_object_tools(context):
|
||||||
|
"""
|
||||||
|
Displays the row of change form object tools.
|
||||||
|
"""
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@register.tag(name='change_form_object_tools')
|
||||||
|
def change_form_object_tools_tag(parser, token):
|
||||||
|
return InclusionAdminNode(
|
||||||
|
parser, token,
|
||||||
|
func=change_form_object_tools,
|
||||||
|
template_name='change_form_object_tools.html',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def cell_count(inline_admin_form):
|
def cell_count(inline_admin_form):
|
||||||
"""Return the number of cells used in a tabular inline."""
|
"""Return the number of cells used in a tabular inline."""
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
from inspect import getfullargspec
|
||||||
|
|
||||||
|
from django.template.library import InclusionNode, parse_bits
|
||||||
|
|
||||||
|
|
||||||
|
class InclusionAdminNode(InclusionNode):
|
||||||
|
def __init__(self, parser, token, func, template_name, takes_context=True):
|
||||||
|
self.template_name = template_name
|
||||||
|
|
||||||
|
params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(func)
|
||||||
|
if len(params) > 0 and params[0] == 'self':
|
||||||
|
params = params[1:] # ignore 'self'
|
||||||
|
bits = token.split_contents()
|
||||||
|
args, kwargs = parse_bits(
|
||||||
|
parser, bits[1:], params, varargs, varkw, defaults, kwonly, kwonly_defaults, takes_context, bits[0]
|
||||||
|
)
|
||||||
|
super().__init__(
|
||||||
|
func=func, takes_context=takes_context, args=args, kwargs=kwargs, filename=None
|
||||||
|
)
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
opts = context['opts']
|
||||||
|
app_label = opts.app_label.lower()
|
||||||
|
object_name = opts.object_name.lower()
|
||||||
|
self.filename = [
|
||||||
|
'admin/%s/%s/%s' % (app_label, object_name, self.template_name),
|
||||||
|
'admin/%s/%s' % (app_label, self.template_name),
|
||||||
|
'admin/%s' % (self.template_name,),
|
||||||
|
]
|
||||||
|
|
||||||
|
return super().render(context)
|
|
@ -2680,12 +2680,28 @@ Templates which may be overridden per app or model
|
||||||
Not every template in ``contrib/admin/templates/admin`` may be overridden per
|
Not every template in ``contrib/admin/templates/admin`` may be overridden per
|
||||||
app or per model. The following can:
|
app or per model. The following can:
|
||||||
|
|
||||||
|
* ``actions.html``
|
||||||
* ``app_index.html``
|
* ``app_index.html``
|
||||||
* ``change_form.html``
|
* ``change_form.html``
|
||||||
|
* ``change_form_object_tools.html``
|
||||||
* ``change_list.html``
|
* ``change_list.html``
|
||||||
|
* ``change_list_object_tools.html``
|
||||||
|
* ``change_list_results.html``
|
||||||
|
* ``date_hierarchy.html``
|
||||||
* ``delete_confirmation.html``
|
* ``delete_confirmation.html``
|
||||||
* ``object_history.html``
|
* ``object_history.html``
|
||||||
|
* ``pagination.html``
|
||||||
* ``popup_response.html``
|
* ``popup_response.html``
|
||||||
|
* ``prepopulated_fields_js.html``
|
||||||
|
* ``search_form.html``
|
||||||
|
* ``submit_line.html``
|
||||||
|
|
||||||
|
.. versionchanged:: 2.1
|
||||||
|
|
||||||
|
The ability to override the ``actions.html``, ``change_form_object_tools.html``,
|
||||||
|
``change_list_object_tools.html``, ``change_list_results.html``,
|
||||||
|
``date_hierarchy.html``, ``pagination.html``, ``prepopulated_fields_js.html``,
|
||||||
|
``search_form.html``, ``submit_line.html`` templates were added.
|
||||||
|
|
||||||
For those templates that cannot be overridden in this way, you may still
|
For those templates that cannot be overridden in this way, you may still
|
||||||
override them for your entire project. Just place the new version in your
|
override them for your entire project. Just place the new version in your
|
||||||
|
|
|
@ -52,6 +52,14 @@ Minor features
|
||||||
|
|
||||||
* The new :meth:`.ModelAdmin.get_deleted_objects()` method allows customizing
|
* The new :meth:`.ModelAdmin.get_deleted_objects()` method allows customizing
|
||||||
the deletion process of the delete view and the "delete selected" action.
|
the deletion process of the delete view and the "delete selected" action.
|
||||||
|
* The ``actions.html``, ``change_list_results.html``, ``date_hierarchy.html``,
|
||||||
|
``pagination.html``, ``prepopulated_fields_js.html``, ``search_form.html``
|
||||||
|
and ``submit_line.html`` templates can be overridden even per app or
|
||||||
|
per model, other than globally.
|
||||||
|
|
||||||
|
* The admin change list and change form object tools can now be overridden per app,
|
||||||
|
per model or globally with ``change_list_object_tools.html`` and
|
||||||
|
``change_form_object_tools.html`` templates.
|
||||||
|
|
||||||
:mod:`django.contrib.admindocs`
|
:mod:`django.contrib.admindocs`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -119,7 +119,7 @@ class ChangeListTests(TestCase):
|
||||||
cl = m.get_changelist_instance(request)
|
cl = m.get_changelist_instance(request)
|
||||||
cl.formset = None
|
cl.formset = None
|
||||||
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl, 'opts': Child._meta})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
||||||
row_html = build_tbody_html(new_child.id, link, '<td class="field-parent nowrap">-</td>')
|
row_html = build_tbody_html(new_child.id, link, '<td class="field-parent nowrap">-</td>')
|
||||||
|
@ -137,7 +137,7 @@ class ChangeListTests(TestCase):
|
||||||
cl = m.get_changelist_instance(request)
|
cl = m.get_changelist_instance(request)
|
||||||
cl.formset = None
|
cl.formset = None
|
||||||
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl, 'opts': Child._meta})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
||||||
row_html = build_tbody_html(new_child.id, link, '<td class="field-parent nowrap">???</td>')
|
row_html = build_tbody_html(new_child.id, link, '<td class="field-parent nowrap">???</td>')
|
||||||
|
@ -153,7 +153,7 @@ class ChangeListTests(TestCase):
|
||||||
cl = m.get_changelist_instance(request)
|
cl = m.get_changelist_instance(request)
|
||||||
cl.formset = None
|
cl.formset = None
|
||||||
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl, 'opts': Child._meta})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
||||||
row_html = build_tbody_html(
|
row_html = build_tbody_html(
|
||||||
|
@ -176,7 +176,7 @@ class ChangeListTests(TestCase):
|
||||||
cl = m.get_changelist_instance(request)
|
cl = m.get_changelist_instance(request)
|
||||||
cl.formset = None
|
cl.formset = None
|
||||||
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl, 'opts': Child._meta})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
link = reverse('admin:admin_changelist_child_change', args=(new_child.id,))
|
||||||
row_html = build_tbody_html(new_child.id, link, '<td class="field-parent nowrap">%s</td>' % new_parent)
|
row_html = build_tbody_html(new_child.id, link, '<td class="field-parent nowrap">%s</td>' % new_parent)
|
||||||
|
@ -204,7 +204,7 @@ class ChangeListTests(TestCase):
|
||||||
FormSet = m.get_changelist_formset(request)
|
FormSet = m.get_changelist_formset(request)
|
||||||
cl.formset = FormSet(queryset=cl.result_list)
|
cl.formset = FormSet(queryset=cl.result_list)
|
||||||
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
|
||||||
context = Context({'cl': cl})
|
context = Context({'cl': cl, 'opts': Child._meta})
|
||||||
table_output = template.render(context)
|
table_output = template.render(context)
|
||||||
# make sure that hidden fields are in the correct place
|
# make sure that hidden fields are in the correct place
|
||||||
hiddenfields_div = (
|
hiddenfields_div = (
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{% extends "admin/actions.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block actions-submit %}
|
||||||
|
<button type="submit" class="button override-actions" title="{% trans "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% trans "Go" %}</button>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% extends "admin/change_form_object_tools.html" %}
|
||||||
|
{% load i18n admin_urls %}
|
||||||
|
|
||||||
|
{% block object-tools-items %}
|
||||||
|
<li><a href="#" id="change-form-export" class="override-change_form_object_tools change-form-object-tools-item">{% trans "Export" %}</a></li>
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% extends "admin/change_list_object_tools.html" %}
|
||||||
|
{% load i18n admin_urls %}
|
||||||
|
|
||||||
|
{% block object-tools-items %}
|
||||||
|
<li><a href="#" id="change-list-export" class="override-change_list_object_tools change-list-object-tools-item">{% trans "Export" %}</a></li>
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,38 @@
|
||||||
|
{% load i18n static %}
|
||||||
|
{% if result_hidden_fields %}
|
||||||
|
<div class="hiddenfields">{# DIV for HTML validation #}
|
||||||
|
{% for item in result_hidden_fields %}{{ item }}{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if results %}
|
||||||
|
<div class="results override-change_list_results">
|
||||||
|
<table id="result_list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% for header in result_headers %}
|
||||||
|
<th scope="col" {{ header.class_attrib }}>
|
||||||
|
{% if header.sortable %}
|
||||||
|
{% if header.sort_priority > 0 %}
|
||||||
|
<div class="sortoptions">
|
||||||
|
<a class="sortremove" href="{{ header.url_remove }}" title="{% trans "Remove from sorting" %}"></a>
|
||||||
|
{% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktrans with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktrans %}">{{ header.sort_priority }}</span>{% endif %}
|
||||||
|
<a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% trans "Toggle sorting" %}"></a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
|
||||||
|
<div class="clear"></div>
|
||||||
|
</th>{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for result in results %}
|
||||||
|
{% if result.form.non_field_errors %}
|
||||||
|
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr class="{% cycle 'row1' 'row2' %}">{% for item in result %}{{ item }}{% endfor %}</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{% extends "admin/date_hierarchy.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block date-hierarchy-choices %}
|
||||||
|
<select id="date-selector" class="override-date_hierarchy">{% for choice in choices %}
|
||||||
|
<option{% if choice.link %} value="{{ choice.link }}"{% endif %}>{{ choice.title }}</option>
|
||||||
|
{% endfor %}</select>
|
||||||
|
<button id="date-selected">{% trans "Go" %}</button>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{% load admin_list %}
|
||||||
|
{% load i18n %}
|
||||||
|
<p class="paginator override-pagination">
|
||||||
|
{% if pagination_required %}
|
||||||
|
{% for i in page_range %}
|
||||||
|
{% paginator_number cl i %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{{ cl.result_count }} {% if cl.result_count == 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endif %}
|
||||||
|
{% if show_all_url %} <a href="{{ show_all_url }}" class="showall">{% trans 'Show all' %}</a>{% endif %}
|
||||||
|
{% if cl.formset and cl.result_count %}<input type="submit" name="_save" class="default" value="{% trans 'Save' %}"/>{% endif %}
|
||||||
|
</p>
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% load l10n static %}
|
||||||
|
<script type="text/javascript"
|
||||||
|
id="django-admin-prepopulated-fields-constants"
|
||||||
|
class="override-prepopulated_fields_js"
|
||||||
|
src="{% static "admin/js/prepopulate_init.js" %}"
|
||||||
|
data-prepopulated-fields="{{ prepopulated_fields_json }}">
|
||||||
|
</script>
|
|
@ -0,0 +1,16 @@
|
||||||
|
{% load i18n static %}
|
||||||
|
{% if cl.search_fields %}
|
||||||
|
<div id="toolbar" class="override-search_form"><form id="changelist-search" method="get">
|
||||||
|
<div><!-- DIV needed for valid HTML -->
|
||||||
|
<label for="searchbar"><img src="{% static "admin/img/search.svg" %}" alt="Search" /></label>
|
||||||
|
<input type="text" size="40" name="{{ search_var }}" value="{{ cl.query }}" id="searchbar" autofocus />
|
||||||
|
<input type="submit" value="{% trans 'Search' %}" />
|
||||||
|
{% if show_result_count %}
|
||||||
|
<span class="small quiet">{% blocktrans count counter=cl.result_count %}{{ counter }} result{% plural %}{{ counter }} results{% endblocktrans %} (<a href="?{% if cl.is_popup %}_popup=1{% endif %}">{% if cl.show_full_result_count %}{% blocktrans with full_result_count=cl.full_result_count %}{{ full_result_count }} total{% endblocktrans %}{% else %}{% trans "Show all" %}{% endif %}</a>)</span>
|
||||||
|
{% endif %}
|
||||||
|
{% for pair in cl.params.items %}
|
||||||
|
{% if pair.0 != search_var %}<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}"/>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</form></div>
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{% extends "admin/submit_line.html" %}
|
||||||
|
{% load i18n admin_urls %}
|
||||||
|
|
||||||
|
{% block submit-row %}
|
||||||
|
{% if show_publish %}<input type="submit" value="{% trans 'Publish' %}" class="default" name="_publish" />{% endif %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
|
@ -7,9 +7,10 @@ from django.contrib.auth.admin import UserAdmin
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.test import RequestFactory, TestCase
|
from django.test import RequestFactory, TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
from .admin import site
|
from .admin import ArticleAdmin, site
|
||||||
from .models import Question
|
from .models import Article, Question
|
||||||
from .tests import AdminViewBasicTestCase
|
from .tests import AdminViewBasicTestCase
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +29,46 @@ class AdminTemplateTagsTest(AdminViewBasicTestCase):
|
||||||
self.assertIs(template_context['extra'], True)
|
self.assertIs(template_context['extra'], True)
|
||||||
self.assertIs(template_context['show_save'], True)
|
self.assertIs(template_context['show_save'], True)
|
||||||
|
|
||||||
|
def test_can_override_change_form_templatetags(self):
|
||||||
|
"""
|
||||||
|
admin_modify templatetags can follow the 'standard' search patter admin/app_label/model/template.html
|
||||||
|
"""
|
||||||
|
factory = RequestFactory()
|
||||||
|
article = Article.objects.all()[0]
|
||||||
|
request = factory.get(reverse('admin:admin_views_article_change', args=[article.pk]))
|
||||||
|
request.user = self.superuser
|
||||||
|
admin = ArticleAdmin(Article, site)
|
||||||
|
extra_context = {'show_publish': True, 'extra': True}
|
||||||
|
response = admin.change_view(request, str(article.pk), extra_context=extra_context)
|
||||||
|
response.render()
|
||||||
|
self.assertIs(response.context_data['show_publish'], True)
|
||||||
|
self.assertIs(response.context_data['extra'], True)
|
||||||
|
content = force_text(response.content)
|
||||||
|
self.assertIs('name="_save"' in content, True)
|
||||||
|
self.assertIs('name="_publish"' in content, True)
|
||||||
|
self.assertIs('override-change_form_object_tools' in content, True)
|
||||||
|
self.assertIs('override-prepopulated_fields_js' in content, True)
|
||||||
|
|
||||||
|
def test_can_override_change_list_templatetags(self):
|
||||||
|
"""
|
||||||
|
admin_list templatetags can follow the 'standard' search patter admin/app_label/model/template.html
|
||||||
|
"""
|
||||||
|
factory = RequestFactory()
|
||||||
|
request = factory.get(reverse('admin:admin_views_article_changelist'))
|
||||||
|
request.user = self.superuser
|
||||||
|
admin = ArticleAdmin(Article, site)
|
||||||
|
admin.date_hierarchy = 'date'
|
||||||
|
admin.search_fields = ('title', 'content',)
|
||||||
|
response = admin.changelist_view(request)
|
||||||
|
response.render()
|
||||||
|
content = force_text(response.content)
|
||||||
|
self.assertIs('override-actions' in content, True)
|
||||||
|
self.assertIs('override-change_list_object_tools' in content, True)
|
||||||
|
self.assertIs('override-change_list_results' in content, True)
|
||||||
|
self.assertIs('override-date_hierarchy' in content, True)
|
||||||
|
self.assertIs('override-pagination' in content, True)
|
||||||
|
self.assertIs('override-search_form' in content, True)
|
||||||
|
|
||||||
|
|
||||||
class DateHierarchyTests(TestCase):
|
class DateHierarchyTests(TestCase):
|
||||||
factory = RequestFactory()
|
factory = RequestFactory()
|
||||||
|
|
Loading…
Reference in New Issue