| 1 |
from django import http |
|---|
| 2 |
from django.db import models |
|---|
| 3 |
from django.contrib.databrowse.datastructures import EasyModel |
|---|
| 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.__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.__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()} |
|---|