app/django/contrib/databrowse/sites.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 from django import http
       
     2 from django.db import models
       
     3 from django.contrib.databrowse.datastructures import EasyModel, EasyChoice
       
     4 from django.shortcuts import render_to_response
       
     5 from django.utils.safestring import mark_safe
       
     6 
       
     7 class AlreadyRegistered(Exception):
       
     8     pass
       
     9 
       
    10 class NotRegistered(Exception):
       
    11     pass
       
    12 
       
    13 class DatabrowsePlugin(object):
       
    14     def urls(self, plugin_name, easy_instance_field):
       
    15         """
       
    16         Given an EasyInstanceField object, returns a list of URLs for this
       
    17         plugin's views of this object. These URLs should be absolute.
       
    18 
       
    19         Returns None if the EasyInstanceField object doesn't get a
       
    20         list of plugin-specific URLs.
       
    21         """
       
    22         return None
       
    23 
       
    24     def model_index_html(self, request, model, site):
       
    25         """
       
    26         Returns a snippet of HTML to include on the model index page.
       
    27         """
       
    28         return ''
       
    29 
       
    30     def model_view(self, request, model_databrowse, url):
       
    31         """
       
    32         Handles main URL routing for a plugin's model-specific pages.
       
    33         """
       
    34         raise NotImplementedError
       
    35 
       
    36 class ModelDatabrowse(object):
       
    37     plugins = {}
       
    38 
       
    39     def __init__(self, model, site):
       
    40         self.model = model
       
    41         self.site = site
       
    42 
       
    43     def root(self, request, url):
       
    44         """
       
    45         Handles main URL routing for the databrowse app.
       
    46 
       
    47         `url` is the remainder of the URL -- e.g. 'objects/3'.
       
    48         """
       
    49         # Delegate to the appropriate method, based on the URL.
       
    50         if url is None:
       
    51             return self.main_view(request)
       
    52         try:
       
    53             plugin_name, rest_of_url = url.split('/', 1)
       
    54         except ValueError: # need more than 1 value to unpack
       
    55             plugin_name, rest_of_url = url, None
       
    56         try:
       
    57             plugin = self.plugins[plugin_name]
       
    58         except KeyError:
       
    59             raise http.Http404('A plugin with the requested name does not exist.')
       
    60         return plugin.model_view(request, self, rest_of_url)
       
    61 
       
    62     def main_view(self, request):
       
    63         easy_model = EasyModel(self.site, self.model)
       
    64         html_snippets = mark_safe(u'\n'.join([p.model_index_html(request, self.model, self.site) for p in self.plugins.values()]))
       
    65         return render_to_response('databrowse/model_detail.html', {
       
    66             'model': easy_model,
       
    67             'root_url': self.site.root_url,
       
    68             'plugin_html': html_snippets,
       
    69         })
       
    70 
       
    71 class DatabrowseSite(object):
       
    72     def __init__(self):
       
    73         self.registry = {} # model_class -> databrowse_class
       
    74         self.root_url = None
       
    75 
       
    76     def register(self, model_or_iterable, databrowse_class=None, **options):
       
    77         """
       
    78         Registers the given model(s) with the given databrowse site.
       
    79 
       
    80         The model(s) should be Model classes, not instances.
       
    81 
       
    82         If a databrowse class isn't given, it will use DefaultModelDatabrowse
       
    83         (the default databrowse options).
       
    84 
       
    85         If a model is already registered, this will raise AlreadyRegistered.
       
    86         """
       
    87         databrowse_class = databrowse_class or DefaultModelDatabrowse
       
    88         if issubclass(model_or_iterable, models.Model):
       
    89             model_or_iterable = [model_or_iterable]
       
    90         for model in model_or_iterable:
       
    91             if model in self.registry:
       
    92                 raise AlreadyRegistered('The model %s is already registered' % model.__class__.__name__)
       
    93             self.registry[model] = databrowse_class
       
    94 
       
    95     def unregister(self, model_or_iterable):
       
    96         """
       
    97         Unregisters the given model(s).
       
    98 
       
    99         If a model isn't already registered, this will raise NotRegistered.
       
   100         """
       
   101         if issubclass(model_or_iterable, models.Model):
       
   102             model_or_iterable = [model_or_iterable]
       
   103         for model in model_or_iterable:
       
   104             if model not in self.registry:
       
   105                 raise NotRegistered('The model %s is not registered' % model.__class__.__name__)
       
   106             del self.registry[model]
       
   107 
       
   108     def root(self, request, url):
       
   109         """
       
   110         Handles main URL routing for the databrowse app.
       
   111 
       
   112         `url` is the remainder of the URL -- e.g. 'comments/comment/'.
       
   113         """
       
   114         self.root_url = request.path[:len(request.path) - len(url)]
       
   115         url = url.rstrip('/') # Trim trailing slash, if it exists.
       
   116 
       
   117         if url == '':
       
   118             return self.index(request)
       
   119         elif '/' in url:
       
   120             return self.model_page(request, *url.split('/', 2))
       
   121 
       
   122         raise http.Http404('The requested databrowse page does not exist.')
       
   123 
       
   124     def index(self, request):
       
   125         m_list = [EasyModel(self, m) for m in self.registry.keys()]
       
   126         return render_to_response('databrowse/homepage.html', {'model_list': m_list, 'root_url': self.root_url})
       
   127 
       
   128     def model_page(self, request, app_label, model_name, rest_of_url=None):
       
   129         """
       
   130         Handles the model-specific functionality of the databrowse site, delegating
       
   131         to the appropriate ModelDatabrowse class.
       
   132         """
       
   133         model = models.get_model(app_label, model_name)
       
   134         if model is None:
       
   135             raise http.Http404("App %r, model %r, not found." % (app_label, model_name))
       
   136         try:
       
   137             databrowse_class = self.registry[model]
       
   138         except KeyError:
       
   139             raise http.Http404("This model exists but has not been registered with databrowse.")
       
   140         return databrowse_class(model, self).root(request, rest_of_url)
       
   141 
       
   142 site = DatabrowseSite()
       
   143 
       
   144 from django.contrib.databrowse.plugins.calendars import CalendarPlugin
       
   145 from django.contrib.databrowse.plugins.objects import ObjectDetailPlugin
       
   146 from django.contrib.databrowse.plugins.fieldchoices import FieldChoicePlugin
       
   147 
       
   148 class DefaultModelDatabrowse(ModelDatabrowse):
       
   149     plugins = {'objects': ObjectDetailPlugin(), 'calendars': CalendarPlugin(), 'fields': FieldChoicePlugin()}