summaryrefslogtreecommitdiffstats
path: root/webapp/django/contrib/databrowse/sites.py
blob: 9ef35dbc1cab9580e6a8022d51365e0993702b6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
from django import http
from django.db import models
from django.contrib.databrowse.datastructures import EasyModel
from django.shortcuts import render_to_response
from django.utils.safestring import mark_safe

class AlreadyRegistered(Exception):
    pass

class NotRegistered(Exception):
    pass

class DatabrowsePlugin(object):
    def urls(self, plugin_name, easy_instance_field):
        """
        Given an EasyInstanceField object, returns a list of URLs for this
        plugin's views of this object. These URLs should be absolute.

        Returns None if the EasyInstanceField object doesn't get a
        list of plugin-specific URLs.
        """
        return None

    def model_index_html(self, request, model, site):
        """
        Returns a snippet of HTML to include on the model index page.
        """
        return ''

    def model_view(self, request, model_databrowse, url):
        """
        Handles main URL routing for a plugin's model-specific pages.
        """
        raise NotImplementedError

class ModelDatabrowse(object):
    plugins = {}

    def __init__(self, model, site):
        self.model = model
        self.site = site

    def root(self, request, url):
        """
        Handles main URL routing for the databrowse app.

        `url` is the remainder of the URL -- e.g. 'objects/3'.
        """
        # Delegate to the appropriate method, based on the URL.
        if url is None:
            return self.main_view(request)
        try:
            plugin_name, rest_of_url = url.split('/', 1)
        except ValueError: # need more than 1 value to unpack
            plugin_name, rest_of_url = url, None
        try:
            plugin = self.plugins[plugin_name]
        except KeyError:
            raise http.Http404('A plugin with the requested name does not exist.')
        return plugin.model_view(request, self, rest_of_url)

    def main_view(self, request):
        easy_model = EasyModel(self.site, self.model)
        html_snippets = mark_safe(u'\n'.join([p.model_index_html(request, self.model, self.site) for p in self.plugins.values()]))
        return render_to_response('databrowse/model_detail.html', {
            'model': easy_model,
            'root_url': self.site.root_url,
            'plugin_html': html_snippets,
        })

class DatabrowseSite(object):
    def __init__(self):
        self.registry = {} # model_class -> databrowse_class
        self.root_url = None

    def register(self, model_or_iterable, databrowse_class=None, **options):
        """
        Registers the given model(s) with the given databrowse site.

        The model(s) should be Model classes, not instances.

        If a databrowse class isn't given, it will use DefaultModelDatabrowse
        (the default databrowse options).

        If a model is already registered, this will raise AlreadyRegistered.
        """
        databrowse_class = databrowse_class or DefaultModelDatabrowse
        if issubclass(model_or_iterable, models.Model):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            if model in self.registry:
                raise AlreadyRegistered('The model %s is already registered' % model.__class__.__name__)
            self.registry[model] = databrowse_class

    def unregister(self, model_or_iterable):
        """
        Unregisters the given model(s).

        If a model isn't already registered, this will raise NotRegistered.
        """
        if issubclass(model_or_iterable, models.Model):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            if model not in self.registry:
                raise NotRegistered('The model %s is not registered' % model.__class__.__name__)
            del self.registry[model]

    def root(self, request, url):
        """
        Handles main URL routing for the databrowse app.

        `url` is the remainder of the URL -- e.g. 'comments/comment/'.
        """
        self.root_url = request.path[:len(request.path) - len(url)]
        url = url.rstrip('/') # Trim trailing slash, if it exists.

        if url == '':
            return self.index(request)
        elif '/' in url:
            return self.model_page(request, *url.split('/', 2))

        raise http.Http404('The requested databrowse page does not exist.')

    def index(self, request):
        m_list = [EasyModel(self, m) for m in self.registry.keys()]
        return render_to_response('databrowse/homepage.html', {'model_list': m_list, 'root_url': self.root_url})

    def model_page(self, request, app_label, model_name, rest_of_url=None):
        """
        Handles the model-specific functionality of the databrowse site, delegating
        to the appropriate ModelDatabrowse class.
        """
        model = models.get_model(app_label, model_name)
        if model is None:
            raise http.Http404("App %r, model %r, not found." % (app_label, model_name))
        try:
            databrowse_class = self.registry[model]
        except KeyError:
            raise http.Http404("This model exists but has not been registered with databrowse.")
        return databrowse_class(model, self).root(request, rest_of_url)

site = DatabrowseSite()

from django.contrib.databrowse.plugins.calendars import CalendarPlugin
from django.contrib.databrowse.plugins.objects import ObjectDetailPlugin
from django.contrib.databrowse.plugins.fieldchoices import FieldChoicePlugin

class DefaultModelDatabrowse(ModelDatabrowse):
    plugins = {'objects': ObjectDetailPlugin(), 'calendars': CalendarPlugin(), 'fields': FieldChoicePlugin()}