Django

Code

root/django/branches/newforms-admin/django/contrib/admindocs/views.py

Revision 7934, 16.0 kB (checked in by brosner, 5 months ago)

newforms-admin: Fixed #7466 -- Generate a root_path for admindocs views so the userlinks are properly linked. Thanks handelaar for the report.

  • Property svn:eol-style set to native
  • Property svn:keywords set to LastChangedRevision
Line 
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, get_host
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.translation import ugettext as _
13 from django.utils.safestring import mark_safe
14 import inspect, os, re
15
16 # Exclude methods starting with these strings from documentation
17 MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_')
18
19 class GenericSite(object):
20     domain = 'example.com'
21     name = 'my site'
22
23 def doc_index(request):
24     if not utils.docutils_is_available:
25         return missing_docutils_page(request)
26     root_path = re.sub(re.escape('doc/') + '$', '', request.path)
27     return render_to_response('admin_doc/index.html', {
28         'root_path': root_path,
29     }, context_instance=RequestContext(request))
30 doc_index = staff_member_required(doc_index)
31
32 def bookmarklets(request):
33     # Hack! This couples this view to the URL it lives at.
34     admin_root = request.path[:-len('doc/bookmarklets/')]
35     root_path = re.sub(re.escape('doc/bookmarklets/') + '$', '', request.path)
36     return render_to_response('admin_doc/bookmarklets.html', {
37         'root_path': root_path,
38         'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)),
39     }, context_instance=RequestContext(request))
40 bookmarklets = staff_member_required(bookmarklets)
41
42 def template_tag_index(request):
43     if not utils.docutils_is_available:
44         return missing_docutils_page(request)
45
46     load_all_installed_template_libraries()
47
48     tags = []
49     for module_name, library in template.libraries.items():
50         for tag_name, tag_func in library.tags.items():
51             title, body, metadata = utils.parse_docstring(tag_func.__doc__)
52             if title:
53                 title = utils.parse_rst(title, 'tag', _('tag:') + tag_name)
54             if body:
55                 body = utils.parse_rst(body, 'tag', _('tag:') + tag_name)
56             for key in metadata:
57                 metadata[key] = utils.parse_rst(metadata[key], 'tag', _('tag:') + tag_name)
58             if library in template.builtins:
59                 tag_library = None
60             else:
61                 tag_library = module_name.split('.')[-1]
62             tags.append({
63                 'name': tag_name,
64                 'title': title,
65                 'body': body,
66                 'meta': metadata,
67                 'library': tag_library,
68             })
69     root_path = re.sub(re.escape('doc/tags/') + '$', '', request.path)
70     return render_to_response('admin_doc/template_tag_index.html', {
71         'root_path': root_path,
72         'tags': tags
73     }, context_instance=RequestContext(request))
74 template_tag_index = staff_member_required(template_tag_index)
75
76 def template_filter_index(request):
77     if not utils.docutils_is_available:
78         return missing_docutils_page(request)
79
80     load_all_installed_template_libraries()
81
82     filters = []
83     for module_name, library in template.libraries.items():
84         for filter_name, filter_func in library.filters.items():
85             title, body, metadata = utils.parse_docstring(filter_func.__doc__)
86             if title:
87                 title = utils.parse_rst(title, 'filter', _('filter:') + filter_name)
88             if body:
89                 body = utils.parse_rst(body, 'filter', _('filter:') + filter_name)
90             for key in metadata:
91                 metadata[key] = utils.parse_rst(metadata[key], 'filter', _('filter:') + filter_name)
92             if library in template.builtins:
93                 tag_library = None
94             else:
95                 tag_library = module_name.split('.')[-1]
96             filters.append({
97                 'name': filter_name,
98                 'title': title,
99                 'body': body,
100                 'meta': metadata,
101                 'library': tag_library,
102             })
103     root_path = re.sub(re.escape('doc/filters/') + '$', '', request.path)
104     return render_to_response('admin_doc/template_filter_index.html', {
105         'root_path': root_path,
106         'filters': filters
107     }, context_instance=RequestContext(request))
108 template_filter_index = staff_member_required(template_filter_index)
109
110 def view_index(request):
111     if not utils.docutils_is_available:
112         return missing_docutils_page(request)
113
114     if settings.ADMIN_FOR:
115         settings_modules = [__import__(m, {}, {}, ['']) for m in settings.ADMIN_FOR]
116     else:
117         settings_modules = [settings]
118
119     views = []
120     for settings_mod in settings_modules:
121         urlconf = __import__(settings_mod.ROOT_URLCONF, {}, {}, [''])
122         view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
123         if Site._meta.installed:
124             site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
125         else:
126             site_obj = GenericSite()
127         for (func, regex) in view_functions:
128             views.append({
129                 'name': func.__name__,
130                 'module': func.__module__,
131                 'site_id': settings_mod.SITE_ID,
132                 'site': site_obj,
133                 'url': simplify_regex(regex),
134             })
135     root_path = re.sub(re.escape('doc/views/') + '$', '', request.path)
136     return render_to_response('admin_doc/view_index.html', {
137         'root_path': root_path,
138         'views': views
139     }, context_instance=RequestContext(request))
140 view_index = staff_member_required(view_index)
141
142 def view_detail(request, view):
143     if not utils.docutils_is_available:
144         return missing_docutils_page(request)
145
146     mod, func = urlresolvers.get_mod_func(view)
147     try:
148         view_func = getattr(__import__(mod, {}, {}, ['']), func)
149     except (ImportError, AttributeError):
150         raise Http404
151     title, body, metadata = utils.parse_docstring(view_func.__doc__)
152     if title:
153         title = utils.parse_rst(title, 'view', _('view:') + view)
154     if body:
155         body = utils.parse_rst(body, 'view', _('view:') + view)
156     for key in metadata:
157         metadata[key] = utils.parse_rst(metadata[key], 'model', _('view:') + view)
158     root_path = re.sub(re.escape('doc/views/%s/' % view) + '$', '', request.path)
159     return render_to_response('admin_doc/view_detail.html', {
160         'root_path': root_path,
161         'name': view,
162         'summary': title,
163         'body': body,
164         'meta': metadata,
165     }, context_instance=RequestContext(request))
166 view_detail = staff_member_required(view_detail)
167
168 def model_index(request):
169     if not utils.docutils_is_available:
170         return missing_docutils_page(request)
171     m_list = [m._meta for m in models.get_models()]
172     root_path = re.sub(re.escape('doc/models/') + '$', '', request.path)
173     return render_to_response('admin_doc/model_index.html', {
174         'root_path': root_path,
175         'models': m_list
176     }, context_instance=RequestContext(request))
177 model_index = staff_member_required(model_index)
178
179 def model_detail(request, app_label, model_name):
180     if not utils.docutils_is_available:
181         return missing_docutils_page(request)
182        
183     # Get the model class.
184     try:
185         app_mod = models.get_app(app_label)
186     except ImproperlyConfigured:
187         raise Http404, _("App %r not found") % app_label
188     model = None
189     for m in models.get_models(app_mod):
190         if m._meta.object_name.lower() == model_name:
191             model = m
192             break
193     if model is None:
194         raise Http404, _("Model %(model_name)r not found in app %(app_label)r") % {'model_name': model_name, 'app_label': app_label}
195
196     opts = model._meta
197
198     # Gather fields/field descriptions.
199     fields = []
200     for field in opts.fields:
201         # ForeignKey is a special case since the field will actually be a
202         # descriptor that returns the other object
203         if isinstance(field, models.ForeignKey):
204             data_type = related_object_name = field.rel.to.__name__
205             app_label = field.rel.to._meta.app_label
206             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)
207         else:
208             data_type = get_readable_field_data_type(field)
209             verbose = field.verbose_name
210         fields.append({
211             'name': field.name,
212             'data_type': data_type,
213             'verbose': verbose,
214             'help_text': field.help_text,
215         })
216
217     # Gather model methods.
218     for func_name, func in model.__dict__.items():
219         if (inspect.isfunction(func) and len(inspect.getargspec(func)[0]) == 1):
220             try:
221                 for exclude in MODEL_METHODS_EXCLUDE:
222                     if func_name.startswith(exclude):
223                         raise StopIteration
224             except StopIteration:
225                 continue
226             verbose = func.__doc__
227             if verbose:
228                 verbose = utils.parse_rst(utils.trim_docstring(verbose), 'model', _('model:') + opts.module_name)
229             fields.append({
230                 'name': func_name,
231                 'data_type': get_return_data_type(func_name),
232                 'verbose': verbose,
233             })
234
235     # Gather related objects
236     for rel in opts.get_all_related_objects():
237         verbose = _("related `%(app_label)s.%(object_name)s` objects") % {'app_label': rel.opts.app_label, 'object_name': rel.opts.object_name}
238         accessor = rel.get_accessor_name()
239         fields.append({
240             'name'      : "%s.all" % accessor,
241             'data_type' : 'List',
242             'verbose'   : utils.parse_rst(_("all %s") % verbose , 'model', _('model:') + opts.module_name),
243         })
244         fields.append({
245             'name'      : "%s.count" % accessor,
246             'data_type' : 'Integer',
247             'verbose'   : utils.parse_rst(_("number of %s") % verbose , 'model', _('model:') + opts.module_name),
248         })
249     root_path = re.sub(re.escape('doc/models/%s.%s/' % (app_label, model_name)) + '$', '', request.path)
250     return render_to_response('admin_doc/model_detail.html', {
251         'root_path': root_path,
252         'name': '%s.%s' % (opts.app_label, opts.object_name),
253         'summary': _("Fields on %s objects") % opts.object_name,
254         'description': model.__doc__,
255         'fields': fields,
256     }, context_instance=RequestContext(request))
257 model_detail = staff_member_required(model_detail)
258
259 def template_detail(request, template):
260     templates = []
261     for site_settings_module in settings.ADMIN_FOR:
262         settings_mod = __import__(site_settings_module, {}, {}, [''])
263         if Site._meta.installed:
264             site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
265         else:
266             site_obj = GenericSite()
267         for dir in settings_mod.TEMPLATE_DIRS:
268             template_file = os.path.join(dir, "%s.html" % template)
269             templates.append({
270                 'file': template_file,
271                 'exists': os.path.exists(template_file),
272                 'contents': lambda: os.path.exists(template_file) and open(template_file).read() or '',
273                 'site_id': settings_mod.SITE_ID,
274                 'site': site_obj,
275                 'order': list(settings_mod.TEMPLATE_DIRS).index(dir),
276             })
277     root_path = re.sub(re.escape('doc/templates/%s/' % template) + '$', '', request.path)
278     return render_to_response('admin_doc/template_detail.html', {
279         'root_path': root_path,
280         'name': template,
281         'templates': templates,
282     }, context_instance=RequestContext(request))
283 template_detail = staff_member_required(template_detail)
284
285 ####################
286 # Helper functions #
287 ####################
288
289 def missing_docutils_page(request):
290     """Display an error message for people without docutils"""
291     return render_to_response('admin_doc/missing_docutils.html')
292
293 def load_all_installed_template_libraries():
294     # Load/register all template tag libraries from installed apps.
295     for e in templatetags.__path__:
296         libraries = [os.path.splitext(p)[0] for p in os.listdir(e) if p.endswith('.py') and p[0].isalpha()]
297         for library_name in libraries:
298             try:
299                 lib = template.get_library("django.templatetags.%s" % library_name.split('.')[-1])
300             except template.InvalidTemplateLibrary:
301                 pass
302
303 def get_return_data_type(func_name):
304     """Return a somewhat-helpful data type given a function name"""
305     if func_name.startswith('get_'):
306         if func_name.endswith('_list'):
307             return 'List'
308         elif func_name.endswith('_count'):
309             return 'Integer'
310     return ''
311
312 # Maps Field objects to their human-readable data types, as strings.
313 # Column-type strings can contain format strings; they'll be interpolated
314 # against the values of Field.__dict__ before being output.
315 # If a column type is set to None, it won't be included in the output.
316 DATA_TYPE_MAPPING = {
317     'AutoField'                 : _('Integer'),
318     'BooleanField'              : _('Boolean (Either True or False)'),
319     'CharField'                 : _('String (up to %(max_length)s)'),
320     'CommaSeparatedIntegerField': _('Comma-separated integers'),
321     'DateField'                 : _('Date (without time)'),
322     'DateTimeField'             : _('Date (with time)'),
323     'DecimalField'              : _('Decimal number'),
324     'EmailField'                : _('E-mail address'),
325     'FileField'                 : _('File path'),
326     'FilePathField'             : _('File path'),
327     'FloatField'                : _('Floating point number'),
328     'ForeignKey'                : _('Integer'),
329     'ImageField'                : _('File path'),
330     'IntegerField'              : _('Integer'),
331     'IPAddressField'            : _('IP address'),
332     'ManyToManyField'           : '',
333     'NullBooleanField'          : _('Boolean (Either True, False or None)'),
334     'OneToOneField'             : _('Relation to parent model'),
335     'PhoneNumberField'          : _('Phone number'),
336     'PositiveIntegerField'      : _('Integer'),
337     'PositiveSmallIntegerField' : _('Integer'),
338     'SlugField'                 : _('String (up to %(max_length)s)'),
339     'SmallIntegerField'         : _('Integer'),
340     'TextField'                 : _('Text'),
341     'TimeField'                 : _('Time'),
342     'URLField'                  : _('URL'),
343     'USStateField'              : _('U.S. state (two uppercase letters)'),
344     'XMLField'                  : _('XML text'),
345 }
346
347 def get_readable_field_data_type(field):
348     return DATA_TYPE_MAPPING[field.get_internal_type()] % field.__dict__
349
350 def extract_views_from_urlpatterns(urlpatterns, base=''):
351     """
352     Return a list of views from a list of urlpatterns.
353
354     Each object in the returned list is a two-tuple: (view_func, regex)
355     """
356     views = []
357     for p in urlpatterns:
358         if hasattr(p, '_get_callback'):
359             try:
360                 views.append((p._get_callback(), base + p.regex.pattern))
361             except ViewDoesNotExist:
362                 continue
363         elif hasattr(p, '_get_url_patterns'):
364             try:
365                 patterns = p.url_patterns
366             except ImportError:
367                 continue
368             views.extend(extract_views_from_urlpatterns(patterns, base + p.regex.pattern))
369         else:
370             raise TypeError, _("%s does not appear to be a urlpattern object") % p
371     return views
372
373 named_group_matcher = re.compile(r'\(\?P(<\w+>).+?\)')
374 non_named_group_matcher = re.compile(r'\(.*?\)')
375
376 def simplify_regex(pattern):
377     """
378     Clean up urlpattern regexes into something somewhat readable by Mere Humans:
379     turns something like "^(?P<sport_slug>\w+)/athletes/(?P<athlete_slug>\w+)/$"
380     into "<sport_slug>/athletes/<athlete_slug>/"
381     """
382     # handle named groups first
383     pattern = named_group_matcher.sub(lambda m: m.group(1), pattern)
384
385     # handle non-named groups
386     pattern = non_named_group_matcher.sub("<var>", pattern)
387
388     # clean up any outstanding regex-y characters.
389     pattern = pattern.replace('^', '').replace('$', '').replace('?', '').replace('//', '/').replace('\\', '')
390     if not pattern.startswith('/'):
391         pattern = '/' + pattern
392     return pattern
Note: See TracBrowser for help on using the browser.