Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #21386 -- Removed admindocs dependence on sites framework

* Removed ADMIN_FOR setting and warn warning
* Group view functions by namespace instead of site
* Added a test verifying namespaces are listed

Thanks to Claude Paroz for reviewing and ideas for improvement.
  • Loading branch information...
commit a39d672ec7d53637805a61b45a51bc0e7d297a36 1 parent f1b3ab9
@Bouke Bouke authored claudep committed
View
13 django/contrib/admindocs/templates/admin_doc/template_detail.html
@@ -15,15 +15,12 @@
{% block content %}
<h1>{% blocktrans %}Template: "{{ name }}"{% endblocktrans %}</h1>
-{% regroup templates|dictsort:"site_id" by site as templates_by_site %}
-{% for group in templates_by_site %}
- <h2>{% blocktrans with group.grouper as grouper %}Search path for template "{{ name }}" on {{ grouper }}:{% endblocktrans %}</h2>
- <ol>
- {% for template in group.list|dictsort:"order" %}
- <li><code>{{ template.file }}</code>{% if not template.exists %} <em>{% trans '(does not exist)' %}</em>{% endif %}</li>
- {% endfor %}
- </ol>
+<h2>{% blocktrans %}Search path for template "{{ name }}":{% endblocktrans %}</h2>
+<ol>
+{% for template in templates|dictsort:"order" %}
+ <li><code>{{ template.file }}</code>{% if not template.exists %} <em>{% trans '(does not exist)' %}</em>{% endif %}</li>
{% endfor %}
+</ol>
<p class="small"><a href="{% url 'django-admindocs-docroot' %}">&lsaquo; {% trans 'Back to Documentation' %}</a></p>
{% endblock %}
View
31 django/contrib/admindocs/templates/admin_doc/view_index.html
@@ -15,29 +15,40 @@
<h1>{% trans 'View documentation' %}</h1>
-{% regroup views|dictsort:"site_id" by site as views_by_site %}
+{% regroup views|dictsort:'namespace' by namespace as views_by_ns %}
<div id="content-related" class="sidebar">
<div class="module">
-<h2>{% trans 'Jump to site' %}</h2>
+<h2>{% trans 'Jump to namespace' %}</h2>
<ul>
- {% for site_views in views_by_site %}
- <li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li>
- {% endfor %}
+{% for ns_views in views_by_ns %}
+ <li><a href="#ns|{{ ns_views.grouper }}">
+ {% if ns_views.grouper %}{{ ns_views.grouper }}
+ {% else %}{% trans "Empty namespace" %}{% endif %}
+ </a></li>
+{% endfor %}
</ul>
</div>
</div>
<div id="content-main">
-{% for site_views in views_by_site %}
+{% for ns_views in views_by_ns %}
<div class="module">
-<h2 id="site{{ site_views.grouper.id }}">{% blocktrans with site_views.grouper.name as name %}Views by URL on {{ name }}{% endblocktrans %}</h2>
-
-{% for view in site_views.list|dictsort:"url" %}
+<h2 id="ns|{{ ns_views.grouper }}">
+{% if ns_views.grouper %}
+ {% blocktrans with ns_views.grouper as name %}Views by namespace {{ name }}{% endblocktrans %}
+{% else %}
+ {% blocktrans %}Views by empty namespace{% endblocktrans %}
+{% endif %}
+</h2>
+
+{% for view in ns_views.list|dictsort:"url" %}
{% ifchanged %}
<h3><a href="{% url 'django-admindocs-views-detail' view=view.full_name %}">{{ view.url }}</a></h3>
-<p class="small quiet">{% blocktrans with view.full_name as name %}View function: {{ name }}{% endblocktrans %}</p>
+<p class="small quiet">{% blocktrans with view.full_name as full_name and view.url_name as url_name %}
+ View function: <code>{{ full_name }}</code>. Name: <code>{{ url_name }}</code>.
+{% endblocktrans %}</p>
<p>{{ view.title }}</p>
<hr />
{% endifchanged %}
View
74 django/contrib/admindocs/views.py
@@ -2,6 +2,7 @@
import inspect
import os
import re
+import warnings
from django import template
from django.conf import settings
@@ -13,7 +14,6 @@
from django.http import Http404
from django.core import urlresolvers
from django.contrib.admindocs import utils
-from django.contrib.sites.models import Site
from django.utils.decorators import method_decorator
from django.utils._os import upath
from django.utils import six
@@ -23,10 +23,10 @@
# Exclude methods starting with these strings from documentation
MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_')
-
-class GenericSite(object):
- domain = 'example.com'
- name = 'my site'
+if getattr(settings, 'ADMIN_FOR', None):
+ warnings.warn('The ADMIN_FOR setting has been removed, you can remove '
+ 'this setting from your configuration.', DeprecationWarning,
+ stacklevel=2)
class BaseAdminDocsView(TemplateView):
@@ -129,26 +129,17 @@ class ViewIndexView(BaseAdminDocsView):
template_name = 'admin_doc/view_index.html'
def get_context_data(self, **kwargs):
- if settings.ADMIN_FOR:
- settings_modules = [import_module(m) for m in settings.ADMIN_FOR]
- else:
- 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:
- 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),
- })
+ urlconf = import_module(settings.ROOT_URLCONF)
+ view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
+ for (func, regex, namespace, name) in view_functions:
+ views.append({
+ 'full_name': '%s.%s' % (func.__module__, getattr(func, '__name__', func.__class__.__name__)),
+ 'url': simplify_regex(regex),
+ 'url_name': ':'.join((namespace or []) + (name and [name] or [])),
+ 'namespace': ':'.join((namespace or [])),
+ 'name': name,
+ })
kwargs.update({'views': views})
return super(ViewIndexView, self).get_context_data(**kwargs)
@@ -292,22 +283,14 @@ class TemplateDetailView(BaseAdminDocsView):
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),
- })
+ for dir in settings.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 '',
+ 'order': list(settings.TEMPLATE_DIRS).index(dir),
+ })
kwargs.update({
'name': template,
'templates': templates,
@@ -356,7 +339,7 @@ def get_readable_field_data_type(field):
return field.description % field.__dict__
-def extract_views_from_urlpatterns(urlpatterns, base=''):
+def extract_views_from_urlpatterns(urlpatterns, base='', namespace=None):
"""
Return a list of views from a list of urlpatterns.
@@ -369,10 +352,15 @@ def extract_views_from_urlpatterns(urlpatterns, base=''):
patterns = p.url_patterns
except ImportError:
continue
- views.extend(extract_views_from_urlpatterns(patterns, base + p.regex.pattern))
+ views.extend(extract_views_from_urlpatterns(
+ patterns,
+ base + p.regex.pattern,
+ (namespace or []) + (p.namespace and [p.namespace] or [])
+ ))
elif hasattr(p, 'callback'):
try:
- views.append((p.callback, base + p.regex.pattern))
+ views.append((p.callback, base + p.regex.pattern,
+ namespace, p.name))
except ViewDoesNotExist:
continue
else:
View
2  docs/ref/contrib/admin/admindocs.txt
@@ -28,8 +28,6 @@ the following:
``r'^admin/'`` entry, so that requests to ``/admin/doc/`` don't get
handled by the latter entry.
* Install the docutils Python module (http://docutils.sf.net/).
-* **Optional:** Linking to templates requires the :setting:`ADMIN_FOR`
- setting to be configured.
* **Optional:** Using the admindocs bookmarklets requires
``django.contrib.admindocs.middleware.XViewMiddleware`` to be installed.
View
19 docs/ref/settings.txt
@@ -2097,25 +2097,6 @@ The default value for the X-Frame-Options header used by
:doc:`clickjacking protection </ref/clickjacking/>` documentation.
-Admindocs
-=========
-
-Settings for :mod:`django.contrib.admindocs`.
-
-.. setting:: ADMIN_FOR
-
-ADMIN_FOR
----------
-
-Default: ``()`` (Empty tuple)
-
-Used for admin-site settings modules, this should be a tuple of settings
-modules (in the format ``'foo.bar.baz'``) for which this site is an admin.
-
-The admin site uses this in its automatically-introspected documentation of
-models, views and template tags.
-
-
Auth
====
View
6 docs/releases/1.7.txt
@@ -880,3 +880,9 @@ Callable arguments were evaluated when a queryset was constructed rather than
when it was evaluated, thus this feature didn't offer any benefit compared to
evaluating arguments before passing them to queryset and created confusion that
the arguments may have been evaluated at query time.
+
+``ADMIN_FOR`` setting
+~~~~~~~~~~~~~~~~~~~~~
+
+The ``ADMIN_FOR`` feature, part of the admindocs, has been removed. You can
+remove the setting from your configuration at your convenience.
View
31 tests/admin_docs/tests.py
@@ -1,5 +1,7 @@
import unittest
+from django.conf import settings
+from django.contrib.sites.models import Site
from django.contrib.admindocs import utils
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
@@ -7,6 +9,33 @@
from django.test.utils import override_settings
+class MiscTests(TestCase):
+ urls = 'admin_docs.urls'
+
+ def setUp(self):
+ self._old_installed = Site._meta.app_config.installed
+ User.objects.create_superuser('super', None, 'secret')
+ self.client.login(username='super', password='secret')
+
+ def tearDown(self):
+ Site._meta.app_config.installed = self._old_installed
+
+ @override_settings(
+ SITE_ID=None,
+ INSTALLED_APPS=[app for app in settings.INSTALLED_APPS
+ if app != 'django.contrib.sites'],
+ )
+ def test_no_sites_framework(self):
+ """
+ Without the sites framework, should not access SITE_ID or Site
+ objects. Deleting settings is fine here as UserSettingsHolder is used.
+ """
+ Site._meta.app_config.installed = False
+ Site.objects.all().delete()
+ del settings.SITE_ID
+ self.client.get('/admindocs/views/') # should not raise
+
+
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
@unittest.skipUnless(utils.docutils_is_available, "no docutils installed.")
class AdminDocViewTests(TestCase):
@@ -46,6 +75,8 @@ def test_view_index(self):
self.assertContains(response,
'<h3><a href="/admindocs/views/django.contrib.admindocs.views.BaseAdminDocsView/">/admindocs/</a></h3>',
html=True)
+ self.assertContains(response, 'Views by namespace test')
+ self.assertContains(response, 'Name: <code>test:func</code>.')
def test_view_detail(self):
response = self.client.get(
View
7 tests/admin_docs/urls.py
@@ -1,11 +1,16 @@
-from django.conf.urls import include, patterns
+from django.conf.urls import include, patterns, url
from django.contrib import admin
from . import views
+ns_patterns = patterns('',
+ url(r'^xview/func/$', views.xview_dec(views.xview), name='func'),
+)
+
urlpatterns = patterns('',
(r'^admin/', include(admin.site.urls)),
(r'^admindocs/', include('django.contrib.admindocs.urls')),
+ (r'^', include(ns_patterns, namespace='test')),
(r'^xview/func/$', views.xview_dec(views.xview)),
(r'^xview/class/$', views.xview_dec(views.XViewClass.as_view())),
)
Please sign in to comment.
Something went wrong with that request. Please try again.