|
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()} |