Updated admindocs to use class-based views

Thanks Bouke Haarsma for the review.
This commit is contained in:
Claude Paroz 2013-11-21 08:48:29 +01:00
parent 1718b5256c
commit d6cc37d601
6 changed files with 361 additions and 287 deletions

View File

@ -4,7 +4,7 @@
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; {% trans 'Documentation' %}</a> &rsaquo; {% trans 'Documentation' %}
</div> </div>
{% endblock %} {% endblock %}
{% block title %}{% trans 'Documentation' %}{% endblock %} {% block title %}{% trans 'Documentation' %}{% endblock %}

View File

@ -4,7 +4,7 @@
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; {% trans 'Documentation' %}</a> &rsaquo; {% trans 'Documentation' %}
</div> </div>
{% endblock %} {% endblock %}
{% block title %}{% trans 'Please install docutils' %}{% endblock %} {% block title %}{% trans 'Please install docutils' %}{% endblock %}

View File

@ -3,39 +3,39 @@ from django.contrib.admindocs import views
urlpatterns = patterns('', urlpatterns = patterns('',
url('^$', url('^$',
views.doc_index, views.BaseAdminDocsView.as_view(template_name='admin_doc/index.html'),
name='django-admindocs-docroot' name='django-admindocs-docroot'
), ),
url('^bookmarklets/$', url('^bookmarklets/$',
views.bookmarklets, views.BookmarkletsView.as_view(),
name='django-admindocs-bookmarklets' name='django-admindocs-bookmarklets'
), ),
url('^tags/$', url('^tags/$',
views.template_tag_index, views.TemplateTagIndexView.as_view(),
name='django-admindocs-tags' name='django-admindocs-tags'
), ),
url('^filters/$', url('^filters/$',
views.template_filter_index, views.TemplateFilterIndexView.as_view(),
name='django-admindocs-filters' name='django-admindocs-filters'
), ),
url('^views/$', url('^views/$',
views.view_index, views.ViewIndexView.as_view(),
name='django-admindocs-views-index' name='django-admindocs-views-index'
), ),
url('^views/(?P<view>[^/]+)/$', url('^views/(?P<view>[^/]+)/$',
views.view_detail, views.ViewDetailView.as_view(),
name='django-admindocs-views-detail' name='django-admindocs-views-detail'
), ),
url('^models/$', url('^models/$',
views.model_index, views.ModelIndexView.as_view(),
name='django-admindocs-models-index' name='django-admindocs-models-index'
), ),
url('^models/(?P<app_label>[^\.]+)\.(?P<model_name>[^/]+)/$', url('^models/(?P<app_label>[^\.]+)\.(?P<model_name>[^/]+)/$',
views.model_detail, views.ModelDetailView.as_view(),
name='django-admindocs-models-detail' name='django-admindocs-models-detail'
), ),
url('^templates/(?P<template>.*)/$', url('^templates/(?P<template>.*)/$',
views.template_detail, views.TemplateDetailView.as_view(),
name='django-admindocs-templates' name='django-admindocs-templates'
), ),
) )

View File

@ -4,19 +4,19 @@ import os
import re import re
from django import template from django import template
from django.template import RequestContext
from django.conf import settings from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.db import models from django.db import models
from django.shortcuts import render_to_response
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
from django.http import Http404 from django.http import Http404
from django.core import urlresolvers from django.core import urlresolvers
from django.contrib.admindocs import utils from django.contrib.admindocs import utils
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.utils.decorators import method_decorator
from django.utils._os import upath from django.utils._os import upath
from django.utils import six from django.utils import six
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import TemplateView
# Exclude methods starting with these strings from documentation # Exclude methods starting with these strings from documentation
MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_') MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_')
@ -27,302 +27,300 @@ class GenericSite(object):
name = 'my site' name = 'my site'
@staff_member_required class BaseAdminDocsView(TemplateView):
def doc_index(request): """
if not utils.docutils_is_available: Base view for admindocs views.
return missing_docutils_page(request) """
return render_to_response('admin_doc/index.html', { @method_decorator(staff_member_required)
'root_path': urlresolvers.reverse('admin:index'), def dispatch(self, *args, **kwargs):
}, context_instance=RequestContext(request)) if not utils.docutils_is_available:
# Display an error message for people without docutils
self.template_name = 'admin_doc/missing_docutils.html'
return self.render_to_response({})
return super(BaseAdminDocsView, self).dispatch(*args, **kwargs)
def get_context_data(self, **kwargs):
kwargs.update({'root_path': urlresolvers.reverse('admin:index')})
return super(BaseAdminDocsView, self).get_context_data(**kwargs)
@staff_member_required class BookmarkletsView(BaseAdminDocsView):
def bookmarklets(request): template_name = 'admin_doc/bookmarklets.html'
admin_root = urlresolvers.reverse('admin:index')
return render_to_response('admin_doc/bookmarklets.html', { def get_context_data(self, **kwargs):
'root_path': admin_root, context = super(BookmarkletsView, self).get_context_data(**kwargs)
'admin_url': "%s://%s%s" % (request.scheme, request.get_host(), admin_root), context.update({
}, context_instance=RequestContext(request)) 'admin_url': "%s://%s%s" % (
self.request.scheme, self.request.get_host(), context['root_path'])
})
return context
@staff_member_required class TemplateTagIndexView(BaseAdminDocsView):
def template_tag_index(request): template_name = 'admin_doc/template_tag_index.html'
if not utils.docutils_is_available:
return missing_docutils_page(request)
load_all_installed_template_libraries() def get_context_data(self, **kwargs):
load_all_installed_template_libraries()
tags = [] tags = []
app_libs = list(six.iteritems(template.libraries)) app_libs = list(six.iteritems(template.libraries))
builtin_libs = [(None, lib) for lib in template.builtins] builtin_libs = [(None, lib) for lib in template.builtins]
for module_name, library in builtin_libs + app_libs: for module_name, library in builtin_libs + app_libs:
for tag_name, tag_func in library.tags.items(): for tag_name, tag_func in library.tags.items():
title, body, metadata = utils.parse_docstring(tag_func.__doc__) title, body, metadata = utils.parse_docstring(tag_func.__doc__)
if title: if title:
title = utils.parse_rst(title, 'tag', _('tag:') + tag_name) title = utils.parse_rst(title, 'tag', _('tag:') + tag_name)
if body: if body:
body = utils.parse_rst(body, 'tag', _('tag:') + tag_name) body = utils.parse_rst(body, 'tag', _('tag:') + tag_name)
for key in metadata: for key in metadata:
metadata[key] = utils.parse_rst(metadata[key], 'tag', _('tag:') + tag_name) metadata[key] = utils.parse_rst(metadata[key], 'tag', _('tag:') + tag_name)
if library in template.builtins: if library in template.builtins:
tag_library = '' tag_library = ''
else: else:
tag_library = module_name.split('.')[-1] tag_library = module_name.split('.')[-1]
tags.append({ tags.append({
'name': tag_name, 'name': tag_name,
'title': title, 'title': title,
'body': body, 'body': body,
'meta': metadata, 'meta': metadata,
'library': tag_library, 'library': tag_library,
}) })
return render_to_response('admin_doc/template_tag_index.html', { kwargs.update({'tags': tags})
'root_path': urlresolvers.reverse('admin:index'), return super(TemplateTagIndexView, self).get_context_data(**kwargs)
'tags': tags
}, context_instance=RequestContext(request))
@staff_member_required class TemplateFilterIndexView(BaseAdminDocsView):
def template_filter_index(request): template_name = 'admin_doc/template_filter_index.html'
if not utils.docutils_is_available:
return missing_docutils_page(request)
load_all_installed_template_libraries() def get_context_data(self, **kwargs):
load_all_installed_template_libraries()
filters = [] filters = []
app_libs = list(six.iteritems(template.libraries)) app_libs = list(six.iteritems(template.libraries))
builtin_libs = [(None, lib) for lib in template.builtins] builtin_libs = [(None, lib) for lib in template.builtins]
for module_name, library in builtin_libs + app_libs: for module_name, library in builtin_libs + app_libs:
for filter_name, filter_func in library.filters.items(): for filter_name, filter_func in library.filters.items():
title, body, metadata = utils.parse_docstring(filter_func.__doc__) title, body, metadata = utils.parse_docstring(filter_func.__doc__)
if title: if title:
title = utils.parse_rst(title, 'filter', _('filter:') + filter_name) title = utils.parse_rst(title, 'filter', _('filter:') + filter_name)
if body: if body:
body = utils.parse_rst(body, 'filter', _('filter:') + filter_name) body = utils.parse_rst(body, 'filter', _('filter:') + filter_name)
for key in metadata: for key in metadata:
metadata[key] = utils.parse_rst(metadata[key], 'filter', _('filter:') + filter_name) metadata[key] = utils.parse_rst(metadata[key], 'filter', _('filter:') + filter_name)
if library in template.builtins: if library in template.builtins:
tag_library = '' tag_library = ''
else: else:
tag_library = module_name.split('.')[-1] tag_library = module_name.split('.')[-1]
filters.append({ filters.append({
'name': filter_name, 'name': filter_name,
'title': title, 'title': title,
'body': body, 'body': body,
'meta': metadata, 'meta': metadata,
'library': tag_library, 'library': tag_library,
}) })
return render_to_response('admin_doc/template_filter_index.html', { kwargs.update({'filters': filters})
'root_path': urlresolvers.reverse('admin:index'), return super(TemplateFilterIndexView, self).get_context_data(**kwargs)
'filters': filters
}, context_instance=RequestContext(request))
@staff_member_required class ViewIndexView(BaseAdminDocsView):
def view_index(request): template_name = 'admin_doc/view_index.html'
if not utils.docutils_is_available:
return missing_docutils_page(request)
if settings.ADMIN_FOR: def get_context_data(self, **kwargs):
settings_modules = [import_module(m) for m in settings.ADMIN_FOR] if settings.ADMIN_FOR:
else: settings_modules = [import_module(m) for m in settings.ADMIN_FOR]
settings_modules = [settings]
views = []
for settings_mod in settings_modules:
urlconf = import_module(settings_mod.ROOT_URLCONF)
view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
if Site._meta.installed:
site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
else: else:
site_obj = GenericSite() settings_modules = [settings]
for (func, regex) in view_functions:
views.append({ views = []
'full_name': '%s.%s' % (func.__module__, getattr(func, '__name__', func.__class__.__name__)), for settings_mod in settings_modules:
'site_id': settings_mod.SITE_ID, urlconf = import_module(settings_mod.ROOT_URLCONF)
'site': site_obj, view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
'url': simplify_regex(regex), if Site._meta.installed:
site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
else:
site_obj = GenericSite()
for (func, regex) in view_functions:
views.append({
'full_name': '%s.%s' % (func.__module__, getattr(func, '__name__', func.__class__.__name__)),
'site_id': settings_mod.SITE_ID,
'site': site_obj,
'url': simplify_regex(regex),
})
kwargs.update({'views': views})
return super(ViewIndexView, self).get_context_data(**kwargs)
class ViewDetailView(BaseAdminDocsView):
template_name = 'admin_doc/view_detail.html'
def get_context_data(self, **kwargs):
view = self.kwargs['view']
mod, func = urlresolvers.get_mod_func(view)
try:
view_func = getattr(import_module(mod), func)
except (ImportError, AttributeError):
raise Http404
title, body, metadata = utils.parse_docstring(view_func.__doc__)
if title:
title = utils.parse_rst(title, 'view', _('view:') + view)
if body:
body = utils.parse_rst(body, 'view', _('view:') + view)
for key in metadata:
metadata[key] = utils.parse_rst(metadata[key], 'model', _('view:') + view)
kwargs.update({
'name': view,
'summary': title,
'body': body,
'meta': metadata,
})
return super(ViewDetailView, self).get_context_data(**kwargs)
class ModelIndexView(BaseAdminDocsView):
template_name = 'admin_doc/model_index.html'
def get_context_data(self, **kwargs):
m_list = [m._meta for m in models.get_models()]
kwargs.update({'models': m_list})
return super(ModelIndexView, self).get_context_data(**kwargs)
class ModelDetailView(BaseAdminDocsView):
template_name = 'admin_doc/model_detail.html'
def get_context_data(self, **kwargs):
# Get the model class.
try:
app_mod = models.get_app(self.kwargs['app_label'])
except ImproperlyConfigured:
raise Http404(_("App %r not found") % self.kwargs['app_label'])
model = None
for m in models.get_models(app_mod):
if m._meta.model_name == self.kwargs['model_name']:
model = m
break
if model is None:
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % {
'model_name': self.kwargs['model_name'], 'app_label': self.kwargs['app_label']})
opts = model._meta
# Gather fields/field descriptions.
fields = []
for field in opts.fields:
# ForeignKey is a special case since the field will actually be a
# descriptor that returns the other object
if isinstance(field, models.ForeignKey):
data_type = field.rel.to.__name__
app_label = field.rel.to._meta.app_label
verbose = utils.parse_rst(
(_("the related `%(app_label)s.%(data_type)s` object") % {
'app_label': app_label, 'data_type': data_type,
}),
'model',
_('model:') + data_type,
)
else:
data_type = get_readable_field_data_type(field)
verbose = field.verbose_name
fields.append({
'name': field.name,
'data_type': data_type,
'verbose': verbose,
'help_text': field.help_text,
}) })
return render_to_response('admin_doc/view_index.html', {
'root_path': urlresolvers.reverse('admin:index'),
'views': views
}, context_instance=RequestContext(request))
# Gather many-to-many fields.
@staff_member_required for field in opts.many_to_many:
def view_detail(request, view):
if not utils.docutils_is_available:
return missing_docutils_page(request)
mod, func = urlresolvers.get_mod_func(view)
try:
view_func = getattr(import_module(mod), func)
except (ImportError, AttributeError):
raise Http404
title, body, metadata = utils.parse_docstring(view_func.__doc__)
if title:
title = utils.parse_rst(title, 'view', _('view:') + view)
if body:
body = utils.parse_rst(body, 'view', _('view:') + view)
for key in metadata:
metadata[key] = utils.parse_rst(metadata[key], 'model', _('view:') + view)
return render_to_response('admin_doc/view_detail.html', {
'root_path': urlresolvers.reverse('admin:index'),
'name': view,
'summary': title,
'body': body,
'meta': metadata,
}, context_instance=RequestContext(request))
@staff_member_required
def model_index(request):
if not utils.docutils_is_available:
return missing_docutils_page(request)
m_list = [m._meta for m in models.get_models()]
return render_to_response('admin_doc/model_index.html', {
'root_path': urlresolvers.reverse('admin:index'),
'models': m_list
}, context_instance=RequestContext(request))
@staff_member_required
def model_detail(request, app_label, model_name):
if not utils.docutils_is_available:
return missing_docutils_page(request)
# Get the model class.
try:
app_mod = models.get_app(app_label)
except ImproperlyConfigured:
raise Http404(_("App %r not found") % app_label)
model = None
for m in models.get_models(app_mod):
if m._meta.model_name == model_name:
model = m
break
if model is None:
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % {'model_name': model_name, 'app_label': app_label})
opts = model._meta
# Gather fields/field descriptions.
fields = []
for field in opts.fields:
# ForeignKey is a special case since the field will actually be a
# descriptor that returns the other object
if isinstance(field, models.ForeignKey):
data_type = field.rel.to.__name__ data_type = field.rel.to.__name__
app_label = field.rel.to._meta.app_label app_label = field.rel.to._meta.app_label
verbose = utils.parse_rst( verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': app_label, 'object_name': data_type}
(_("the related `%(app_label)s.%(data_type)s` object") % {
'app_label': app_label, 'data_type': data_type,
}),
'model',
_('model:') + data_type,
)
else:
data_type = get_readable_field_data_type(field)
verbose = field.verbose_name
fields.append({
'name': field.name,
'data_type': data_type,
'verbose': verbose,
'help_text': field.help_text,
})
# Gather many-to-many fields.
for field in opts.many_to_many:
data_type = field.rel.to.__name__
app_label = field.rel.to._meta.app_label
verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': app_label, 'object_name': data_type}
fields.append({
'name': "%s.all" % field.name,
"data_type": 'List',
'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name),
})
fields.append({
'name': "%s.count" % field.name,
'data_type': 'Integer',
'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name),
})
# Gather model methods.
for func_name, func in model.__dict__.items():
if (inspect.isfunction(func) and len(inspect.getargspec(func)[0]) == 1):
try:
for exclude in MODEL_METHODS_EXCLUDE:
if func_name.startswith(exclude):
raise StopIteration
except StopIteration:
continue
verbose = func.__doc__
if verbose:
verbose = utils.parse_rst(utils.trim_docstring(verbose), 'model', _('model:') + opts.model_name)
fields.append({ fields.append({
'name': func_name, 'name': "%s.all" % field.name,
'data_type': get_return_data_type(func_name), "data_type": 'List',
'verbose': verbose, 'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name),
})
fields.append({
'name': "%s.count" % field.name,
'data_type': 'Integer',
'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name),
}) })
# Gather related objects # Gather model methods.
for rel in opts.get_all_related_objects() + opts.get_all_related_many_to_many_objects(): for func_name, func in model.__dict__.items():
verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': rel.opts.app_label, 'object_name': rel.opts.object_name} if (inspect.isfunction(func) and len(inspect.getargspec(func)[0]) == 1):
accessor = rel.get_accessor_name() try:
fields.append({ for exclude in MODEL_METHODS_EXCLUDE:
'name': "%s.all" % accessor, if func_name.startswith(exclude):
'data_type': 'List', raise StopIteration
'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name), except StopIteration:
}) continue
fields.append({ verbose = func.__doc__
'name': "%s.count" % accessor, if verbose:
'data_type': 'Integer', verbose = utils.parse_rst(utils.trim_docstring(verbose), 'model', _('model:') + opts.model_name)
'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name), fields.append({
}) 'name': func_name,
return render_to_response('admin_doc/model_detail.html', { 'data_type': get_return_data_type(func_name),
'root_path': urlresolvers.reverse('admin:index'), 'verbose': verbose,
'name': '%s.%s' % (opts.app_label, opts.object_name), })
# Translators: %s is an object type name
'summary': _("Attributes on %s objects") % opts.object_name,
'description': model.__doc__,
'fields': fields,
}, context_instance=RequestContext(request))
# Gather related objects
@staff_member_required for rel in opts.get_all_related_objects() + opts.get_all_related_many_to_many_objects():
def template_detail(request, template): verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': rel.opts.app_label, 'object_name': rel.opts.object_name}
templates = [] accessor = rel.get_accessor_name()
for site_settings_module in settings.ADMIN_FOR: fields.append({
settings_mod = import_module(site_settings_module) 'name': "%s.all" % accessor,
if Site._meta.installed: 'data_type': 'List',
site_obj = Site.objects.get(pk=settings_mod.SITE_ID) 'verbose': utils.parse_rst(_("all %s") % verbose, 'model', _('model:') + opts.model_name),
else:
site_obj = GenericSite()
for dir in settings_mod.TEMPLATE_DIRS:
template_file = os.path.join(dir, template)
templates.append({
'file': template_file,
'exists': os.path.exists(template_file),
'contents': lambda: open(template_file).read() if os.path.exists(template_file) else '',
'site_id': settings_mod.SITE_ID,
'site': site_obj,
'order': list(settings_mod.TEMPLATE_DIRS).index(dir),
}) })
return render_to_response('admin_doc/template_detail.html', { fields.append({
'root_path': urlresolvers.reverse('admin:index'), 'name': "%s.count" % accessor,
'name': template, 'data_type': 'Integer',
'templates': templates, 'verbose': utils.parse_rst(_("number of %s") % verbose, 'model', _('model:') + opts.model_name),
}, context_instance=RequestContext(request)) })
kwargs.update({
'name': '%s.%s' % (opts.app_label, opts.object_name),
# Translators: %s is an object type name
'summary': _("Attributes on %s objects") % opts.object_name,
'description': model.__doc__,
'fields': fields,
})
return super(ModelDetailView, self).get_context_data(**kwargs)
class TemplateDetailView(BaseAdminDocsView):
template_name = 'admin_doc/template_detail.html'
def get_context_data(self, **kwargs):
template = self.kwargs['template']
templates = []
for site_settings_module in settings.ADMIN_FOR:
settings_mod = import_module(site_settings_module)
if Site._meta.installed:
site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
else:
site_obj = GenericSite()
for dir in settings_mod.TEMPLATE_DIRS:
template_file = os.path.join(dir, template)
templates.append({
'file': template_file,
'exists': os.path.exists(template_file),
'contents': lambda: open(template_file).read() if os.path.exists(template_file) else '',
'site_id': settings_mod.SITE_ID,
'site': site_obj,
'order': list(settings_mod.TEMPLATE_DIRS).index(dir),
})
kwargs.update({
'name': template,
'templates': templates,
})
return super(TemplateDetailView, self).get_context_data(**kwargs)
#################### ####################
# Helper functions # # Helper functions #
#################### ####################
def missing_docutils_page(request):
"""Display an error message for people without docutils"""
return render_to_response('admin_doc/missing_docutils.html')
def load_all_installed_template_libraries(): def load_all_installed_template_libraries():
# Load/register all template tag libraries from installed apps. # Load/register all template tag libraries from installed apps.
for module_name in template.get_templatetags_modules(): for module_name in template.get_templatetags_modules():

View File

@ -1,16 +1,89 @@
import unittest import unittest
try:
import docutils
except ImportError:
docutils = None
from django.contrib.admindocs import utils from django.contrib.admindocs import utils
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.")
class AdminDocViewTests(TestCase):
fixtures = ['data.xml']
urls = 'admin_docs.urls'
def setUp(self):
self.client.login(username='super', password='secret')
def test_index(self):
self.client.logout()
response = self.client.get(reverse('django-admindocs-docroot'))
# Should display the login screen
self.assertContains(response,
'<input type="hidden" name="next" value="/admindocs/" />', html=True)
self.client.login(username='super', password='secret')
response = self.client.get(reverse('django-admindocs-docroot'))
self.assertContains(response, '<h1>Documentation</h1>', html=True)
def test_bookmarklets(self):
response = self.client.get(reverse('django-admindocs-bookmarklets'))
self.assertContains(response, 'http://testserver/admin/doc/views/')
def test_templatetag_index(self):
response = self.client.get(reverse('django-admindocs-tags'))
self.assertContains(response, '<h3 id="built_in-extends">extends</h3>', html=True)
def test_templatefilter_index(self):
response = self.client.get(reverse('django-admindocs-filters'))
self.assertContains(response, '<h3 id="built_in-first">first</h3>', html=True)
def test_view_index(self):
response = self.client.get(reverse('django-admindocs-views-index'))
self.assertContains(response,
'<h3><a href="/admindocs/views/django.contrib.admindocs.views.BaseAdminDocsView/">/admindocs/</a></h3>',
html=True)
def test_view_detail(self):
response = self.client.get(
reverse('django-admindocs-views-detail',
args=['django.contrib.admindocs.views.BaseAdminDocsView']))
# View docstring
self.assertContains(response, 'Base view for admindocs views.')
def test_model_index(self):
response = self.client.get(reverse('django-admindocs-models-index'))
self.assertContains(response, '<h2 id="app-auth">Auth</h2>', html=True)
def test_model_detail(self):
response = self.client.get(reverse('django-admindocs-models-detail',
args=['auth', 'user']))
# Check for attribute, many-to-many field
self.assertContains(response,
'<tr><td>email</td><td>Email address</td><td>email address</td></tr>', html=True)
self.assertContains(response,
'<tr><td>user_permissions.all</td><td>List</td><td>'
'<p>all related <a class="reference external" href="/admindocs/models/auth.permission/">'
'auth.Permission</a> objects</p></td></tr>', html=True)
def test_template_detail(self):
response = self.client.get(reverse('django-admindocs-templates',
args=['admin_doc/template_detail.html']))
self.assertContains(response,
'<h1>Template: "admin_doc/template_detail.html"</h1>', html=True)
def test_missing_docutils(self):
utils.docutils_is_available = False
try:
response = self.client.get(reverse('django-admindocs-docroot'))
self.assertContains(response,
'<h3>The admin documentation system requires Python\'s '
'<a href="http://docutils.sf.net/">docutils</a> library.</h3>',
html=True)
finally:
utils.docutils_is_available = True
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class XViewMiddlewareTest(TestCase): class XViewMiddlewareTest(TestCase):
fixtures = ['data.xml'] fixtures = ['data.xml']
@ -53,7 +126,7 @@ class XViewMiddlewareTest(TestCase):
self.assertFalse('X-View' in response) self.assertFalse('X-View' in response)
@unittest.skipUnless(docutils, "no docutils installed.") @unittest.skipUnless(utils.docutils_is_available, "no docutils installed.")
class DefaultRoleTest(TestCase): class DefaultRoleTest(TestCase):
urls = 'admin_docs.urls' urls = 'admin_docs.urls'
@ -81,6 +154,7 @@ class DefaultRoleTest(TestCase):
when ``publish_parts`` is used directly, by setting it to when ``publish_parts`` is used directly, by setting it to
``cmsreference``. See #6681. ``cmsreference``. See #6681.
""" """
import docutils
self.assertNotEqual(docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE, self.assertNotEqual(docutils.parsers.rst.roles.DEFAULT_INTERPRETED_ROLE,
'cmsreference') 'cmsreference')
source = 'reST, `interpreted text`, default role.' source = 'reST, `interpreted text`, default role.'

View File

@ -1,8 +1,10 @@
from django.conf.urls import include, patterns from django.conf.urls import include, patterns
from django.contrib import admin
from . import views from . import views
urlpatterns = patterns('', urlpatterns = patterns('',
(r'^admin/', include(admin.site.urls)),
(r'^admindocs/', include('django.contrib.admindocs.urls')), (r'^admindocs/', include('django.contrib.admindocs.urls')),
(r'^xview/func/$', views.xview_dec(views.xview)), (r'^xview/func/$', views.xview_dec(views.xview)),
(r'^xview/class/$', views.xview_dec(views.XViewClass.as_view())), (r'^xview/class/$', views.xview_dec(views.XViewClass.as_view())),