App Engine Python SDK version 1.9.12
[gae.git] / python / lib / django-1.2 / django / contrib / admindocs / views.py
blob5bfa0f7184e94976c5c360d8740fb9be6f8e19ca
1 from django import template, templatetags
2 from django.template import RequestContext
3 from django.conf import settings
4 from django.contrib.admin.views.decorators import staff_member_required
5 from django.db import models
6 from django.shortcuts import render_to_response
7 from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
8 from django.http import Http404
9 from django.core import urlresolvers
10 from django.contrib.admindocs import utils
11 from django.contrib.sites.models import Site
12 from django.utils.importlib import import_module
13 from django.utils.translation import ugettext as _
14 from django.utils.safestring import mark_safe
15 import inspect, os, re
17 # Exclude methods starting with these strings from documentation
18 MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_')
20 class GenericSite(object):
21 domain = 'example.com'
22 name = 'my site'
24 def get_root_path():
25 try:
26 return urlresolvers.reverse('admin:index')
27 except urlresolvers.NoReverseMatch:
28 from django.contrib import admin
29 try:
30 return urlresolvers.reverse(admin.site.root, args=[''])
31 except urlresolvers.NoReverseMatch:
32 return getattr(settings, "ADMIN_SITE_ROOT_URL", "/admin/")
34 def doc_index(request):
35 if not utils.docutils_is_available:
36 return missing_docutils_page(request)
37 return render_to_response('admin_doc/index.html', {
38 'root_path': get_root_path(),
39 }, context_instance=RequestContext(request))
40 doc_index = staff_member_required(doc_index)
42 def bookmarklets(request):
43 admin_root = get_root_path()
44 return render_to_response('admin_doc/bookmarklets.html', {
45 'root_path': admin_root,
46 'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)),
47 }, context_instance=RequestContext(request))
48 bookmarklets = staff_member_required(bookmarklets)
50 def template_tag_index(request):
51 if not utils.docutils_is_available:
52 return missing_docutils_page(request)
54 load_all_installed_template_libraries()
56 tags = []
57 app_libs = template.libraries.items()
58 builtin_libs = [(None, lib) for lib in template.builtins]
59 for module_name, library in builtin_libs + app_libs:
60 for tag_name, tag_func in library.tags.items():
61 title, body, metadata = utils.parse_docstring(tag_func.__doc__)
62 if title:
63 title = utils.parse_rst(title, 'tag', _('tag:') + tag_name)
64 if body:
65 body = utils.parse_rst(body, 'tag', _('tag:') + tag_name)
66 for key in metadata:
67 metadata[key] = utils.parse_rst(metadata[key], 'tag', _('tag:') + tag_name)
68 if library in template.builtins:
69 tag_library = None
70 else:
71 tag_library = module_name.split('.')[-1]
72 tags.append({
73 'name': tag_name,
74 'title': title,
75 'body': body,
76 'meta': metadata,
77 'library': tag_library,
79 return render_to_response('admin_doc/template_tag_index.html', {
80 'root_path': get_root_path(),
81 'tags': tags
82 }, context_instance=RequestContext(request))
83 template_tag_index = staff_member_required(template_tag_index)
85 def template_filter_index(request):
86 if not utils.docutils_is_available:
87 return missing_docutils_page(request)
89 load_all_installed_template_libraries()
91 filters = []
92 app_libs = template.libraries.items()
93 builtin_libs = [(None, lib) for lib in template.builtins]
94 for module_name, library in builtin_libs + app_libs:
95 for filter_name, filter_func in library.filters.items():
96 title, body, metadata = utils.parse_docstring(filter_func.__doc__)
97 if title:
98 title = utils.parse_rst(title, 'filter', _('filter:') + filter_name)
99 if body:
100 body = utils.parse_rst(body, 'filter', _('filter:') + filter_name)
101 for key in metadata:
102 metadata[key] = utils.parse_rst(metadata[key], 'filter', _('filter:') + filter_name)
103 if library in template.builtins:
104 tag_library = None
105 else:
106 tag_library = module_name.split('.')[-1]
107 filters.append({
108 'name': filter_name,
109 'title': title,
110 'body': body,
111 'meta': metadata,
112 'library': tag_library,
114 return render_to_response('admin_doc/template_filter_index.html', {
115 'root_path': get_root_path(),
116 'filters': filters
117 }, context_instance=RequestContext(request))
118 template_filter_index = staff_member_required(template_filter_index)
120 def view_index(request):
121 if not utils.docutils_is_available:
122 return missing_docutils_page(request)
124 if settings.ADMIN_FOR:
125 settings_modules = [import_module(m) for m in settings.ADMIN_FOR]
126 else:
127 settings_modules = [settings]
129 views = []
130 for settings_mod in settings_modules:
131 urlconf = import_module(settings_mod.ROOT_URLCONF)
132 view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
133 if Site._meta.installed:
134 site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
135 else:
136 site_obj = GenericSite()
137 for (func, regex) in view_functions:
138 views.append({
139 'name': getattr(func, '__name__', func.__class__.__name__),
140 'module': func.__module__,
141 'site_id': settings_mod.SITE_ID,
142 'site': site_obj,
143 'url': simplify_regex(regex),
145 return render_to_response('admin_doc/view_index.html', {
146 'root_path': get_root_path(),
147 'views': views
148 }, context_instance=RequestContext(request))
149 view_index = staff_member_required(view_index)
151 def view_detail(request, view):
152 if not utils.docutils_is_available:
153 return missing_docutils_page(request)
155 mod, func = urlresolvers.get_mod_func(view)
156 try:
157 view_func = getattr(import_module(mod), func)
158 except (ImportError, AttributeError):
159 raise Http404
160 title, body, metadata = utils.parse_docstring(view_func.__doc__)
161 if title:
162 title = utils.parse_rst(title, 'view', _('view:') + view)
163 if body:
164 body = utils.parse_rst(body, 'view', _('view:') + view)
165 for key in metadata:
166 metadata[key] = utils.parse_rst(metadata[key], 'model', _('view:') + view)
167 return render_to_response('admin_doc/view_detail.html', {
168 'root_path': get_root_path(),
169 'name': view,
170 'summary': title,
171 'body': body,
172 'meta': metadata,
173 }, context_instance=RequestContext(request))
174 view_detail = staff_member_required(view_detail)
176 def model_index(request):
177 if not utils.docutils_is_available:
178 return missing_docutils_page(request)
179 m_list = [m._meta for m in models.get_models()]
180 return render_to_response('admin_doc/model_index.html', {
181 'root_path': get_root_path(),
182 'models': m_list
183 }, context_instance=RequestContext(request))
184 model_index = staff_member_required(model_index)
186 def model_detail(request, app_label, model_name):
187 if not utils.docutils_is_available:
188 return missing_docutils_page(request)
190 # Get the model class.
191 try:
192 app_mod = models.get_app(app_label)
193 except ImproperlyConfigured:
194 raise Http404(_("App %r not found") % app_label)
195 model = None
196 for m in models.get_models(app_mod):
197 if m._meta.object_name.lower() == model_name:
198 model = m
199 break
200 if model is None:
201 raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % {'model_name': model_name, 'app_label': app_label})
203 opts = model._meta
205 # Gather fields/field descriptions.
206 fields = []
207 for field in opts.fields:
208 # ForeignKey is a special case since the field will actually be a
209 # descriptor that returns the other object
210 if isinstance(field, models.ForeignKey):
211 data_type = related_object_name = field.rel.to.__name__
212 app_label = field.rel.to._meta.app_label
213 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)
214 else:
215 data_type = get_readable_field_data_type(field)
216 verbose = field.verbose_name
217 fields.append({
218 'name': field.name,
219 'data_type': data_type,
220 'verbose': verbose,
221 'help_text': field.help_text,
224 # Gather many-to-many fields.
225 for field in opts.many_to_many:
226 data_type = related_object_name = field.rel.to.__name__
227 app_label = field.rel.to._meta.app_label
228 verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': app_label, 'object_name': data_type}
229 fields.append({
230 'name': "%s.all" % field.name,
231 "data_type": 'List',
232 'verbose': utils.parse_rst(_("all %s") % verbose , 'model', _('model:') + opts.module_name),
234 fields.append({
235 'name' : "%s.count" % field.name,
236 'data_type' : 'Integer',
237 'verbose' : utils.parse_rst(_("number of %s") % verbose , 'model', _('model:') + opts.module_name),
240 # Gather model methods.
241 for func_name, func in model.__dict__.items():
242 if (inspect.isfunction(func) and len(inspect.getargspec(func)[0]) == 1):
243 try:
244 for exclude in MODEL_METHODS_EXCLUDE:
245 if func_name.startswith(exclude):
246 raise StopIteration
247 except StopIteration:
248 continue
249 verbose = func.__doc__
250 if verbose:
251 verbose = utils.parse_rst(utils.trim_docstring(verbose), 'model', _('model:') + opts.module_name)
252 fields.append({
253 'name': func_name,
254 'data_type': get_return_data_type(func_name),
255 'verbose': verbose,
258 # Gather related objects
259 for rel in opts.get_all_related_objects() + opts.get_all_related_many_to_many_objects():
260 verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': rel.opts.app_label, 'object_name': rel.opts.object_name}
261 accessor = rel.get_accessor_name()
262 fields.append({
263 'name' : "%s.all" % accessor,
264 'data_type' : 'List',
265 'verbose' : utils.parse_rst(_("all %s") % verbose , 'model', _('model:') + opts.module_name),
267 fields.append({
268 'name' : "%s.count" % accessor,
269 'data_type' : 'Integer',
270 'verbose' : utils.parse_rst(_("number of %s") % verbose , 'model', _('model:') + opts.module_name),
272 return render_to_response('admin_doc/model_detail.html', {
273 'root_path': get_root_path(),
274 'name': '%s.%s' % (opts.app_label, opts.object_name),
275 'summary': _("Fields on %s objects") % opts.object_name,
276 'description': model.__doc__,
277 'fields': fields,
278 }, context_instance=RequestContext(request))
279 model_detail = staff_member_required(model_detail)
281 def template_detail(request, template):
282 templates = []
283 for site_settings_module in settings.ADMIN_FOR:
284 settings_mod = import_module(site_settings_module)
285 if Site._meta.installed:
286 site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
287 else:
288 site_obj = GenericSite()
289 for dir in settings_mod.TEMPLATE_DIRS:
290 template_file = os.path.join(dir, template)
291 templates.append({
292 'file': template_file,
293 'exists': os.path.exists(template_file),
294 'contents': lambda: os.path.exists(template_file) and open(template_file).read() or '',
295 'site_id': settings_mod.SITE_ID,
296 'site': site_obj,
297 'order': list(settings_mod.TEMPLATE_DIRS).index(dir),
299 return render_to_response('admin_doc/template_detail.html', {
300 'root_path': get_root_path(),
301 'name': template,
302 'templates': templates,
303 }, context_instance=RequestContext(request))
304 template_detail = staff_member_required(template_detail)
306 ####################
307 # Helper functions #
308 ####################
310 def missing_docutils_page(request):
311 """Display an error message for people without docutils"""
312 return render_to_response('admin_doc/missing_docutils.html')
314 def load_all_installed_template_libraries():
315 # Load/register all template tag libraries from installed apps.
316 for module_name in template.get_templatetags_modules():
317 mod = import_module(module_name)
318 libraries = [
319 os.path.splitext(p)[0]
320 for p in os.listdir(os.path.dirname(mod.__file__))
321 if p.endswith('.py') and p[0].isalpha()
323 for library_name in libraries:
324 try:
325 lib = template.get_library(library_name)
326 except template.InvalidTemplateLibrary, e:
327 pass
329 def get_return_data_type(func_name):
330 """Return a somewhat-helpful data type given a function name"""
331 if func_name.startswith('get_'):
332 if func_name.endswith('_list'):
333 return 'List'
334 elif func_name.endswith('_count'):
335 return 'Integer'
336 return ''
338 def get_readable_field_data_type(field):
339 """Returns the description for a given field type, if it exists,
340 Fields' descriptions can contain format strings, which will be interpolated
341 against the values of field.__dict__ before being output."""
343 return field.description % field.__dict__
345 def extract_views_from_urlpatterns(urlpatterns, base=''):
347 Return a list of views from a list of urlpatterns.
349 Each object in the returned list is a two-tuple: (view_func, regex)
351 views = []
352 for p in urlpatterns:
353 if hasattr(p, '_get_callback'):
354 try:
355 views.append((p._get_callback(), base + p.regex.pattern))
356 except ViewDoesNotExist:
357 continue
358 elif hasattr(p, '_get_url_patterns'):
359 try:
360 patterns = p.url_patterns
361 except ImportError:
362 continue
363 views.extend(extract_views_from_urlpatterns(patterns, base + p.regex.pattern))
364 else:
365 raise TypeError(_("%s does not appear to be a urlpattern object") % p)
366 return views
368 named_group_matcher = re.compile(r'\(\?P(<\w+>).+?\)')
369 non_named_group_matcher = re.compile(r'\(.*?\)')
371 def simplify_regex(pattern):
373 Clean up urlpattern regexes into something somewhat readable by Mere Humans:
374 turns something like "^(?P<sport_slug>\w+)/athletes/(?P<athlete_slug>\w+)/$"
375 into "<sport_slug>/athletes/<athlete_slug>/"
377 # handle named groups first
378 pattern = named_group_matcher.sub(lambda m: m.group(1), pattern)
380 # handle non-named groups
381 pattern = non_named_group_matcher.sub("<var>", pattern)
383 # clean up any outstanding regex-y characters.
384 pattern = pattern.replace('^', '').replace('$', '').replace('?', '').replace('//', '/').replace('\\', '')
385 if not pattern.startswith('/'):
386 pattern = '/' + pattern
387 return pattern