1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

newforms-admin: Made it easier to specify a custom template to be used in the admin section. You can now specify index_template and login_template properties on an AdminSite subclass, and change_form_template, change_list_template, object_history_template and delete_confirmation_template properties on a ModelAdmin subclass.

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7630 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Simon Willison 2008-06-13 15:42:43 +00:00
parent f33bdf7e9c
commit 725293d51a
10 changed files with 131 additions and 34 deletions

View File

@ -59,7 +59,10 @@ def flatten_fieldsets(fieldsets):
class AdminForm(object):
def __init__(self, form, fieldsets, prepopulated_fields):
self.form, self.fieldsets = form, fieldsets
self.prepopulated_fields = [{'field': form[field_name], 'dependencies': [form[f] for f in dependencies]} for field_name, dependencies in prepopulated_fields.items()]
self.prepopulated_fields = [{
'field': form[field_name],
'dependencies': [form[f] for f in dependencies]
} for field_name, dependencies in prepopulated_fields.items()]
def __iter__(self):
for name, options in self.fieldsets:
@ -234,6 +237,12 @@ class ModelAdmin(BaseModelAdmin):
ordering = None
inlines = []
# Custom templates (designed to be over-ridden in subclasses)
change_form_template = None
change_list_template = None
delete_confirmation_template = None
object_history_template = None
def __init__(self, model, admin_site):
self.model = model
self.opts = model._meta
@ -372,6 +381,7 @@ class ModelAdmin(BaseModelAdmin):
if request.POST.has_key("_popup"):
post_url_continue += "?_popup=1"
return HttpResponseRedirect(post_url_continue % pk_value)
if request.POST.has_key("_popup"):
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode.
@ -470,7 +480,7 @@ class ModelAdmin(BaseModelAdmin):
'save_as': self.save_as,
'save_on_top': self.save_on_top,
})
return render_to_response([
return render_to_response(self.change_form_template or [
"admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
"admin/%s/change_form.html" % app_label,
"admin/change_form.html"
@ -626,6 +636,7 @@ class ModelAdmin(BaseModelAdmin):
if ERROR_FLAG in request.GET.keys():
return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
context = {
'title': cl.title,
'is_popup': cl.is_popup,
@ -633,7 +644,7 @@ class ModelAdmin(BaseModelAdmin):
}
context.update({'has_add_permission': self.has_add_permission(request)}),
context.update(extra_context or {})
return render_to_response([
return render_to_response(self.change_list_template or [
'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
'admin/%s/change_list.html' % app_label,
'admin/change_list.html'
@ -676,6 +687,7 @@ class ModelAdmin(BaseModelAdmin):
if not self.has_change_permission(request, None):
return HttpResponseRedirect("../../../../")
return HttpResponseRedirect("../../")
context = {
"title": _("Are you sure?"),
"object_name": opts.verbose_name,
@ -685,7 +697,7 @@ class ModelAdmin(BaseModelAdmin):
"opts": opts,
}
context.update(extra_context or {})
return render_to_response([
return render_to_response(self.delete_confirmation_template or [
"admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()),
"admin/%s/delete_confirmation.html" % app_label,
"admin/delete_confirmation.html"
@ -697,8 +709,10 @@ class ModelAdmin(BaseModelAdmin):
from django.contrib.admin.models import LogEntry
model = self.model
opts = model._meta
action_list = LogEntry.objects.filter(object_id=object_id,
content_type__id__exact=ContentType.objects.get_for_model(model).id).select_related().order_by('action_time')
action_list = LogEntry.objects.filter(
object_id = object_id,
content_type__id__exact = ContentType.objects.get_for_model(model).id
).select_related().order_by('action_time')
# If no history was found, see whether this object even exists.
obj = get_object_or_404(model, pk=object_id)
context = {
@ -708,7 +722,7 @@ class ModelAdmin(BaseModelAdmin):
'object': obj,
}
context.update(extra_context or {})
return render_to_response([
return render_to_response(self.object_history_template or [
"admin/%s/%s/object_history.html" % (opts.app_label, opts.object_name.lower()),
"admin/%s/object_history.html" % opts.app_label,
"admin/object_history.html"

View File

@ -23,23 +23,6 @@ class AlreadyRegistered(Exception):
class NotRegistered(Exception):
pass
def _display_login_form(request, error_message=''):
request.session.set_test_cookie()
if request.POST and request.POST.has_key('post_data'):
# User has failed login BUT has previously saved post data.
post_data = request.POST['post_data']
elif request.POST:
# User's session must have expired; save their post data.
post_data = _encode_post_data(request.POST)
else:
post_data = _encode_post_data({})
return render_to_response('admin/login.html', {
'title': _('Log in'),
'app_path': request.path,
'post_data': post_data,
'error_message': error_message
}, context_instance=template.RequestContext(request))
def _encode_post_data(post_data):
from django.conf import settings
pickled = pickle.dumps(post_data)
@ -56,6 +39,16 @@ def _decode_post_data(encoded_data):
return pickle.loads(pickled)
class AdminSite(object):
"""
An AdminSite object encapsulates an instance of the Django admin application, ready
to be hooked in to your URLConf. Models are registered with the AdminSite using the
register() method, and the root() method can then be used as a Django view function
that presents a full admin interface for the collection of registered models.
"""
index_template = None
login_template = None
def __init__(self):
self._registry = {} # model_class class -> admin_class instance
@ -120,7 +113,6 @@ class AdminSite(object):
# expired sessions and continue through (#5999)
return response
if url == '':
return self.index(request)
elif url == 'password_change':
@ -214,12 +206,12 @@ class AdminSite(object):
message = _("Please log in again, because your session has expired. Don't worry: Your submission has been saved.")
else:
message = ""
return _display_login_form(request, message)
return self.display_login_form(request, message)
# Check that the user accepts cookies.
if not request.session.test_cookie_worked():
message = _("Looks like your browser isn't configured to accept cookies. Please enable cookies, reload this page, and try again.")
return _display_login_form(request, message)
return self.display_login_form(request, message)
# Check the password.
username = request.POST.get('username', None)
@ -235,7 +227,7 @@ class AdminSite(object):
message = _("Usernames cannot contain the '@' character.")
else:
message = _("Your e-mail address is not your username. Try '%s' instead.") % user.username
return _display_login_form(request, message)
return self.display_login_form(request, message)
# The user data is correct; log in the user in and continue.
else:
@ -255,7 +247,7 @@ class AdminSite(object):
request.session.delete_test_cookie()
return http.HttpResponseRedirect(request.path)
else:
return _display_login_form(request, ERROR_MESSAGE)
return self.display_login_form(request, ERROR_MESSAGE)
def index(self, request):
"""
@ -300,11 +292,29 @@ class AdminSite(object):
for app in app_list:
app['models'].sort(lambda x, y: cmp(x['name'], y['name']))
return render_to_response('admin/index.html', {
return render_to_response(self.index_template or 'admin/index.html', {
'title': _('Site administration'),
'app_list': app_list,
}, context_instance=template.RequestContext(request))
def display_login_form(self, request, error_message=''):
request.session.set_test_cookie()
if request.POST and request.POST.has_key('post_data'):
# User has failed login BUT has previously saved post data.
post_data = request.POST['post_data']
elif request.POST:
# User's session must have expired; save their post data.
post_data = _encode_post_data(request.POST)
else:
post_data = _encode_post_data({})
return render_to_response(self.login_template or 'admin/login.html', {
'title': _('Log in'),
'app_path': request.path,
'post_data': post_data,
'error_message': error_message
}, context_instance=template.RequestContext(request))
# This global object represents the default admin site, for the common case.
# You can instantiate AdminSite in your own code to create a custom admin site.
site = AdminSite()

View File

@ -1,7 +1,6 @@
from django.db import models
from django.contrib import admin
class Article(models.Model):
"""
A simple article to test admin views. Test backwards compabilty.
@ -24,6 +23,14 @@ class CustomArticle(models.Model):
date = models.DateTimeField()
class CustomArticleAdmin(admin.ModelAdmin):
"""
Tests various hooks for using custom templates and contexts.
"""
change_list_template = 'custom_admin/change_list.html'
change_form_template = 'custom_admin/change_form.html'
object_history_template = 'custom_admin/object_history.html'
delete_confirmation_template = 'custom_admin/delete_confirmation.html'
def changelist_view(self, request):
"Test that extra_context works"
return super(CustomArticleAdmin, self).changelist_view(request, extra_context={

View File

@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.sites import LOGIN_FORM_KEY, _encode_post_data
# local test models
from models import Article
from models import Article, CustomArticle
def get_perm(Model, perm):
"""Return the permission object, for the Model"""
@ -183,12 +183,63 @@ class AdminViewPermissionsTest(TestCase):
self.failUnlessEqual(Article.objects.get(pk=1).content, '<p>edited article</p>')
self.client.get('/test_admin/admin/logout/')
def testCustomChangelistView(self):
def testCustomModelAdminTemplates(self):
self.client.get('/test_admin/admin/')
self.client.post('/test_admin/admin/', self.super_login)
# Test custom change list template with custom extra context
request = self.client.get('/test_admin/admin/admin_views/customarticle/')
self.failUnlessEqual(request.status_code, 200)
self.assert_("var hello = 'Hello!';" in request.content)
self.assertTemplateUsed(request, 'custom_admin/change_list.html')
# Test custom change form template
request = self.client.get('/test_admin/admin/admin_views/customarticle/add/')
self.assertTemplateUsed(request, 'custom_admin/change_form.html')
# Add an article so we can test delete and history views
post = self.client.post('/test_admin/admin/admin_views/customarticle/add/', {
'content': '<p>great article</p>',
'date_0': '2008-03-18',
'date_1': '10:54:39'
})
self.assertRedirects(post, '/test_admin/admin/admin_views/customarticle/')
self.failUnlessEqual(CustomArticle.objects.all().count(), 1)
# Test custom delete and object history templates
request = self.client.get('/test_admin/admin/admin_views/customarticle/1/delete/')
self.assertTemplateUsed(request, 'custom_admin/delete_confirmation.html')
request = self.client.get('/test_admin/admin/admin_views/customarticle/1/history/')
self.assertTemplateUsed(request, 'custom_admin/object_history.html')
self.client.get('/test_admin/admin/logout/')
def testCustomAdminSiteTemplates(self):
from django.contrib import admin
self.assertEqual(admin.site.index_template, None)
self.assertEqual(admin.site.login_template, None)
self.client.get('/test_admin/admin/logout/')
request = self.client.get('/test_admin/admin/')
self.assertTemplateUsed(request, 'admin/login.html')
self.client.post('/test_admin/admin/', self.changeuser_login)
request = self.client.get('/test_admin/admin/')
self.assertTemplateUsed(request, 'admin/index.html')
self.client.get('/test_admin/admin/logout/')
admin.site.login_template = 'custom_admin/login.html'
admin.site.index_template = 'custom_admin/index.html'
request = self.client.get('/test_admin/admin/')
self.assertTemplateUsed(request, 'custom_admin/login.html')
self.assert_('Hello from a custom login template' in request.content)
self.client.post('/test_admin/admin/', self.changeuser_login)
request = self.client.get('/test_admin/admin/')
self.assertTemplateUsed(request, 'custom_admin/index.html')
self.assert_('Hello from a custom index template' in request.content)
self.client.get('/test_admin/admin/logout/')
admin.site.login_template = None
admin.site.index_template = None
def testDeleteView(self):
"""Delete view should restrict access and actually delete items."""

View File

@ -0,0 +1 @@
{% extends "admin/change_form.html" %}

View File

@ -0,0 +1 @@
{% extends "admin/delete_confirmation.html" %}

View File

@ -0,0 +1,6 @@
{% extends "admin/index.html" %}
{% block content %}
Hello from a custom index template
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,6 @@
{% extends "admin/login.html" %}
{% block content %}
Hello from a custom login template
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1 @@
{% extends "admin/object_history.html" %}