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:
parent
f33bdf7e9c
commit
725293d51a
@ -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:
|
||||
@ -233,6 +236,12 @@ class ModelAdmin(BaseModelAdmin):
|
||||
save_on_top = False
|
||||
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
|
||||
@ -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"
|
||||
|
@ -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()
|
||||
|
@ -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={
|
||||
|
@ -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,13 +183,64 @@ 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."""
|
||||
|
||||
|
1
tests/templates/custom_admin/change_form.html
Normal file
1
tests/templates/custom_admin/change_form.html
Normal file
@ -0,0 +1 @@
|
||||
{% extends "admin/change_form.html" %}
|
1
tests/templates/custom_admin/delete_confirmation.html
Normal file
1
tests/templates/custom_admin/delete_confirmation.html
Normal file
@ -0,0 +1 @@
|
||||
{% extends "admin/delete_confirmation.html" %}
|
6
tests/templates/custom_admin/index.html
Normal file
6
tests/templates/custom_admin/index.html
Normal file
@ -0,0 +1,6 @@
|
||||
{% extends "admin/index.html" %}
|
||||
|
||||
{% block content %}
|
||||
Hello from a custom index template
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
6
tests/templates/custom_admin/login.html
Normal file
6
tests/templates/custom_admin/login.html
Normal file
@ -0,0 +1,6 @@
|
||||
{% extends "admin/login.html" %}
|
||||
|
||||
{% block content %}
|
||||
Hello from a custom login template
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
1
tests/templates/custom_admin/object_history.html
Normal file
1
tests/templates/custom_admin/object_history.html
Normal file
@ -0,0 +1 @@
|
||||
{% extends "admin/object_history.html" %}
|
Loading…
x
Reference in New Issue
Block a user