Added django.contrib.databrowse
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5011 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
83c4c53120
commit
30fb62d71a
|
@ -0,0 +1 @@
|
||||||
|
from django.contrib.databrowse.sites import DatabrowsePlugin, ModelDatabrowse, DatabrowseSite, site
|
|
@ -0,0 +1,188 @@
|
||||||
|
"""
|
||||||
|
These classes are light wrappers around Django's database API that provide
|
||||||
|
convenience functionality and permalink functions for the databrowse app.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
|
||||||
|
from django.db import models
|
||||||
|
from django.utils import dateformat
|
||||||
|
from django.utils.text import capfirst
|
||||||
|
from django.utils.translation import get_date_formats
|
||||||
|
|
||||||
|
class EasyModel(object):
|
||||||
|
def __init__(self, site, model):
|
||||||
|
self.site = site
|
||||||
|
self.model = model
|
||||||
|
self.model_list = site.registry.keys()
|
||||||
|
self.verbose_name = model._meta.verbose_name
|
||||||
|
self.verbose_name_plural = model._meta.verbose_name_plural
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<EasyModel for %s>' % self.model._meta.object_name
|
||||||
|
|
||||||
|
def model_databrowse(self):
|
||||||
|
"Returns the ModelDatabrowse class for this model."
|
||||||
|
return self.site.registry[self.model]
|
||||||
|
|
||||||
|
def url(self):
|
||||||
|
return '%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name)
|
||||||
|
|
||||||
|
def objects(self, **kwargs):
|
||||||
|
for obj in self.model._default_manager.filter(**kwargs):
|
||||||
|
yield EasyInstance(self, obj)
|
||||||
|
|
||||||
|
def object_by_pk(self, pk):
|
||||||
|
return EasyInstance(self, self.model._default_manager.get(pk=pk))
|
||||||
|
|
||||||
|
def sample_objects(self):
|
||||||
|
for obj in self.model._default_manager.all()[:3]:
|
||||||
|
yield EasyInstance(self, obj)
|
||||||
|
|
||||||
|
def field(self, name):
|
||||||
|
try:
|
||||||
|
f = self.model._meta.get_field(name)
|
||||||
|
except models.FieldDoesNotExist:
|
||||||
|
return None
|
||||||
|
return EasyField(self, f)
|
||||||
|
|
||||||
|
def fields(self):
|
||||||
|
return [EasyField(self, f) for f in (self.model._meta.fields + self.model._meta.many_to_many)]
|
||||||
|
|
||||||
|
class EasyField(object):
|
||||||
|
def __init__(self, easy_model, field):
|
||||||
|
self.model, self.field = easy_model, field
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<EasyField for %s.%s>' % (self.model.model._meta.object_name, self.field.name)
|
||||||
|
|
||||||
|
def choices(self):
|
||||||
|
for value, label in self.field.choices:
|
||||||
|
yield EasyChoice(self.model, self, value, label)
|
||||||
|
|
||||||
|
def url(self):
|
||||||
|
if self.field.choices:
|
||||||
|
return '%s%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name)
|
||||||
|
elif self.field.rel:
|
||||||
|
return '%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name)
|
||||||
|
|
||||||
|
class EasyChoice(object):
|
||||||
|
def __init__(self, easy_model, field, value, label):
|
||||||
|
self.model, self.field = easy_model, field
|
||||||
|
self.value, self.label = value, label
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name)
|
||||||
|
|
||||||
|
def url(self):
|
||||||
|
return '%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, self.value)
|
||||||
|
|
||||||
|
class EasyInstance(object):
|
||||||
|
def __init__(self, easy_model, instance):
|
||||||
|
self.model, self.instance = easy_model, instance
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val())
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
val = str(self.instance)
|
||||||
|
if len(val) > 30:
|
||||||
|
return val[:30] + '...'
|
||||||
|
return val
|
||||||
|
|
||||||
|
def pk(self):
|
||||||
|
return self.instance._get_pk_val()
|
||||||
|
|
||||||
|
def url(self):
|
||||||
|
return '%s%s/%s/objects/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.pk())
|
||||||
|
|
||||||
|
def fields(self):
|
||||||
|
"""
|
||||||
|
Generator that yields EasyInstanceFields for each field in this
|
||||||
|
EasyInstance's model.
|
||||||
|
"""
|
||||||
|
for f in self.model.model._meta.fields + self.model.model._meta.many_to_many:
|
||||||
|
yield EasyInstanceField(self.model, self, f)
|
||||||
|
|
||||||
|
def related_objects(self):
|
||||||
|
"""
|
||||||
|
Generator that yields dictionaries of all models that have this
|
||||||
|
EasyInstance's model as a ForeignKey or ManyToManyField, along with
|
||||||
|
lists of related objects.
|
||||||
|
"""
|
||||||
|
for rel_object in self.model.model._meta.get_all_related_objects() + self.model.model._meta.get_all_related_many_to_many_objects():
|
||||||
|
if rel_object.model not in self.model.model_list:
|
||||||
|
continue # Skip models that aren't in the model_list
|
||||||
|
em = EasyModel(self.model.site, rel_object.model)
|
||||||
|
yield {
|
||||||
|
'model': em,
|
||||||
|
'related_field': rel_object.field.verbose_name,
|
||||||
|
'object_list': [EasyInstance(em, i) for i in getattr(self.instance, rel_object.get_accessor_name()).all()],
|
||||||
|
}
|
||||||
|
|
||||||
|
class EasyInstanceField(object):
|
||||||
|
def __init__(self, easy_model, instance, field):
|
||||||
|
self.model, self.field, self.instance = easy_model, field, instance
|
||||||
|
self.raw_value = getattr(instance.instance, field.name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<EasyInstanceField for %s.%s>' % (self.model.model._meta.object_name, self.field.name)
|
||||||
|
|
||||||
|
def values(self):
|
||||||
|
"""
|
||||||
|
Returns a list of values for this field for this instance. It's a list
|
||||||
|
so we can accomodate many-to-many fields.
|
||||||
|
"""
|
||||||
|
if self.field.rel:
|
||||||
|
if isinstance(self.field.rel, models.ManyToOneRel):
|
||||||
|
objs = getattr(self.instance.instance, self.field.name)
|
||||||
|
elif isinstance(self.field.rel, models.ManyToManyRel): # ManyToManyRel
|
||||||
|
return list(getattr(self.instance.instance, self.field.name).all())
|
||||||
|
elif self.field.choices:
|
||||||
|
objs = dict(self.field.choices).get(self.raw_value, EMPTY_CHANGELIST_VALUE)
|
||||||
|
elif isinstance(self.field, models.DateField) or isinstance(self.field, models.TimeField):
|
||||||
|
if self.raw_value:
|
||||||
|
date_format, datetime_format, time_format = get_date_formats()
|
||||||
|
if isinstance(self.field, models.DateTimeField):
|
||||||
|
objs = capfirst(dateformat.format(self.raw_value, datetime_format))
|
||||||
|
elif isinstance(self.field, models.TimeField):
|
||||||
|
objs = capfirst(dateformat.time_format(self.raw_value, time_format))
|
||||||
|
else:
|
||||||
|
objs = capfirst(dateformat.format(self.raw_value, date_format))
|
||||||
|
else:
|
||||||
|
objs = EMPTY_CHANGELIST_VALUE
|
||||||
|
elif isinstance(self.field, models.BooleanField) or isinstance(self.field, models.NullBooleanField):
|
||||||
|
objs = {True: 'Yes', False: 'No', None: 'Unknown'}[self.raw_value]
|
||||||
|
else:
|
||||||
|
objs = self.raw_value
|
||||||
|
return [objs]
|
||||||
|
|
||||||
|
def urls(self):
|
||||||
|
"Returns a list of (value, URL) tuples."
|
||||||
|
# First, check the urls() method for each plugin.
|
||||||
|
plugin_urls = []
|
||||||
|
for plugin_name, plugin in self.model.model_databrowse().plugins.items():
|
||||||
|
urls = plugin.urls(plugin_name, self)
|
||||||
|
if urls is not None:
|
||||||
|
#plugin_urls.append(urls)
|
||||||
|
values = self.values()
|
||||||
|
return zip(self.values(), urls)
|
||||||
|
if self.field.rel:
|
||||||
|
m = EasyModel(self.model.site, self.field.rel.to)
|
||||||
|
if self.field.rel.to in self.model.model_list:
|
||||||
|
lst = []
|
||||||
|
for value in self.values():
|
||||||
|
url = '%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, value._get_pk_val())
|
||||||
|
lst.append((str(value), url))
|
||||||
|
else:
|
||||||
|
lst = [(value, None) for value in self.values()]
|
||||||
|
elif self.field.choices:
|
||||||
|
lst = []
|
||||||
|
for value in self.values():
|
||||||
|
url = '%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, self.raw_value)
|
||||||
|
lst.append((value, url))
|
||||||
|
elif isinstance(self.field, models.URLField):
|
||||||
|
val = self.values()[0]
|
||||||
|
lst = [(val, val)]
|
||||||
|
else:
|
||||||
|
lst = [(self.values()[0], None)]
|
||||||
|
return lst
|
|
@ -0,0 +1,84 @@
|
||||||
|
from django import http
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.databrowse.datastructures import EasyModel
|
||||||
|
from django.contrib.databrowse.sites import DatabrowsePlugin
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
from django.utils.text import capfirst
|
||||||
|
from django.utils.translation import get_date_formats
|
||||||
|
from django.views.generic import date_based
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
class CalendarPlugin(DatabrowsePlugin):
|
||||||
|
def __init__(self, field_names=None):
|
||||||
|
self.field_names = field_names
|
||||||
|
|
||||||
|
def field_dict(self, model):
|
||||||
|
"""
|
||||||
|
Helper function that returns a dictionary of all DateFields or
|
||||||
|
DateTimeFields in the given model. If self.field_names is set, it takes
|
||||||
|
take that into account when building the dictionary.
|
||||||
|
"""
|
||||||
|
if self.field_names is None:
|
||||||
|
return dict([(f.name, f) for f in model._meta.fields if isinstance(f, models.DateField)])
|
||||||
|
else:
|
||||||
|
return dict([(f.name, f) for f in model._meta.fields if isinstance(f, models.DateField) and f.name in self.field_names])
|
||||||
|
|
||||||
|
def model_index_html(self, request, model, site):
|
||||||
|
fields = self.field_dict(model)
|
||||||
|
if not fields:
|
||||||
|
return ''
|
||||||
|
return '<p class="filter"><strong>View calendar by:</strong> %s</p>' % \
|
||||||
|
', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, capfirst(f.verbose_name)) for f in fields.values()])
|
||||||
|
|
||||||
|
def urls(self, plugin_name, easy_instance_field):
|
||||||
|
if isinstance(easy_instance_field.field, models.DateField):
|
||||||
|
return ['%s%s/%s/%s/%s/%s/' % (easy_instance_field.model.url(),
|
||||||
|
plugin_name, easy_instance_field.field.name,
|
||||||
|
easy_instance_field.raw_value.year,
|
||||||
|
easy_instance_field.raw_value.strftime('%b').lower(),
|
||||||
|
easy_instance_field.raw_value.day)]
|
||||||
|
|
||||||
|
def model_view(self, request, model_databrowse, url):
|
||||||
|
self.model, self.site = model_databrowse.model, model_databrowse.site
|
||||||
|
self.fields = self.field_dict(self.model)
|
||||||
|
|
||||||
|
# If the model has no DateFields, there's no point in going further.
|
||||||
|
if not self.fields:
|
||||||
|
raise http.Http404('The requested model has no calendars.')
|
||||||
|
|
||||||
|
if url is None:
|
||||||
|
return self.homepage_view(request)
|
||||||
|
url_bits = url.split('/')
|
||||||
|
if self.fields.has_key(url_bits[0]):
|
||||||
|
return self.calendar_view(request, self.fields[url_bits[0]], *url_bits[1:])
|
||||||
|
|
||||||
|
raise http.Http404('The requested page does not exist.')
|
||||||
|
|
||||||
|
def homepage_view(self, request):
|
||||||
|
easy_model = EasyModel(self.site, self.model)
|
||||||
|
field_list = self.fields.values()
|
||||||
|
field_list.sort(lambda x, y: cmp(x.verbose_name, y.verbose_name))
|
||||||
|
return render_to_response('databrowse/calendar_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list})
|
||||||
|
|
||||||
|
def calendar_view(self, request, field, year=None, month=None, day=None):
|
||||||
|
easy_model = EasyModel(self.site, self.model)
|
||||||
|
extra_context = {'root_url': self.site.root_url, 'model': easy_model, 'field': field}
|
||||||
|
if day is not None:
|
||||||
|
# TODO: The objects in this template should be EasyInstances
|
||||||
|
return date_based.archive_day(request, year, month, day, self.model.objects.all(), field.name,
|
||||||
|
template_name='databrowse/calendar_day.html', allow_empty=False, allow_future=True,
|
||||||
|
extra_context=extra_context)
|
||||||
|
elif month is not None:
|
||||||
|
return date_based.archive_month(request, year, month, self.model.objects.all(), field.name,
|
||||||
|
template_name='databrowse/calendar_month.html', allow_empty=False, allow_future=True,
|
||||||
|
extra_context=extra_context)
|
||||||
|
elif year is not None:
|
||||||
|
return date_based.archive_year(request, year, self.model.objects.all(), field.name,
|
||||||
|
template_name='databrowse/calendar_year.html', allow_empty=False, allow_future=True,
|
||||||
|
extra_context=extra_context)
|
||||||
|
else:
|
||||||
|
return date_based.archive_index(request, self.model.objects.all(), field.name,
|
||||||
|
template_name='databrowse/calendar_main.html', allow_empty=True, allow_future=True,
|
||||||
|
extra_context=extra_context)
|
||||||
|
assert False, ('%s, %s, %s, %s' % (field, year, month, day))
|
|
@ -0,0 +1,72 @@
|
||||||
|
from django import http
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.databrowse.datastructures import EasyModel
|
||||||
|
from django.contrib.databrowse.sites import DatabrowsePlugin
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
from django.utils.text import capfirst
|
||||||
|
from django.views.generic import date_based
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
class FieldChoicePlugin(DatabrowsePlugin):
|
||||||
|
def __init__(self, field_filter=None):
|
||||||
|
# If field_filter is given, it should be a callable that takes a
|
||||||
|
# Django database Field instance and returns True if that field should
|
||||||
|
# be included. If field_filter is None, that all fields will be used.
|
||||||
|
self.field_filter = field_filter
|
||||||
|
|
||||||
|
def field_dict(self, model):
|
||||||
|
"""
|
||||||
|
Helper function that returns a dictionary of all fields in the given
|
||||||
|
model. If self.field_filter is set, it only includes the fields that
|
||||||
|
match the filter.
|
||||||
|
"""
|
||||||
|
if self.field_filter:
|
||||||
|
return dict([(f.name, f) for f in model._meta.fields if self.field_filter(f)])
|
||||||
|
else:
|
||||||
|
return dict([(f.name, f) for f in model._meta.fields if not f.rel and not f.primary_key and not f.unique and not isinstance(f, (models.AutoField, models.TextField))])
|
||||||
|
|
||||||
|
def model_index_html(self, request, model, site):
|
||||||
|
fields = self.field_dict(model)
|
||||||
|
if not fields:
|
||||||
|
return ''
|
||||||
|
return '<p class="filter"><strong>View by:</strong> %s</p>' % \
|
||||||
|
', '.join(['<a href="fields/%s/">%s</a>' % (f.name, capfirst(f.verbose_name)) for f in fields.values()])
|
||||||
|
|
||||||
|
def urls(self, plugin_name, easy_instance_field):
|
||||||
|
if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():
|
||||||
|
return ['%s%s/%s/%s/' % (easy_instance_field.model.url(),
|
||||||
|
plugin_name, easy_instance_field.field.name,
|
||||||
|
easy_instance_field.raw_value)]
|
||||||
|
|
||||||
|
def model_view(self, request, model_databrowse, url):
|
||||||
|
self.model, self.site = model_databrowse.model, model_databrowse.site
|
||||||
|
self.fields = self.field_dict(self.model)
|
||||||
|
|
||||||
|
# If the model has no fields with choices, there's no point in going
|
||||||
|
# further.
|
||||||
|
if not self.fields:
|
||||||
|
raise http.Http404('The requested model has no fields.')
|
||||||
|
|
||||||
|
if url is None:
|
||||||
|
return self.homepage_view(request)
|
||||||
|
url_bits = url.split('/', 1)
|
||||||
|
if self.fields.has_key(url_bits[0]):
|
||||||
|
return self.field_view(request, self.fields[url_bits[0]], *url_bits[1:])
|
||||||
|
|
||||||
|
raise http.Http404('The requested page does not exist.')
|
||||||
|
|
||||||
|
def homepage_view(self, request):
|
||||||
|
easy_model = EasyModel(self.site, self.model)
|
||||||
|
field_list = self.fields.values()
|
||||||
|
field_list.sort(lambda x, y: cmp(x.verbose_name, y.verbose_name))
|
||||||
|
return render_to_response('databrowse/fieldchoice_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list})
|
||||||
|
|
||||||
|
def field_view(self, request, field, value=None):
|
||||||
|
easy_model = EasyModel(self.site, self.model)
|
||||||
|
easy_field = easy_model.field(field.name)
|
||||||
|
if value is not None:
|
||||||
|
obj_list = easy_model.objects(**{field.name: value})
|
||||||
|
return render_to_response('databrowse/fieldchoice_detail.html', {'root_url': self.site.root_url, 'model': easy_model, 'field': easy_field, 'value': value, 'object_list': obj_list})
|
||||||
|
obj_list = [v[field.name] for v in self.model._default_manager.distinct().order_by(field.name).values(field.name)]
|
||||||
|
return render_to_response('databrowse/fieldchoice_list.html', {'root_url': self.site.root_url, 'model': easy_model, 'field': easy_field, 'object_list': obj_list})
|
|
@ -0,0 +1,14 @@
|
||||||
|
from django import http
|
||||||
|
from django.contrib.databrowse.datastructures import EasyModel
|
||||||
|
from django.contrib.databrowse.sites import DatabrowsePlugin
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
import urlparse
|
||||||
|
|
||||||
|
class ObjectDetailPlugin(DatabrowsePlugin):
|
||||||
|
def model_view(self, request, model_databrowse, url):
|
||||||
|
# If the object ID wasn't provided, redirect to the model page, which is one level up.
|
||||||
|
if url is None:
|
||||||
|
return http.HttpResponseRedirect(urlparse.urljoin(request.path, '../'))
|
||||||
|
easy_model = EasyModel(model_databrowse.site, model_databrowse.model)
|
||||||
|
obj = easy_model.object_by_pk(url)
|
||||||
|
return render_to_response('databrowse/object_detail.html', {'object': obj, 'root_url': model_databrowse.site.root_url})
|
|
@ -0,0 +1,148 @@
|
||||||
|
from django import http
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.databrowse.datastructures import EasyModel, EasyChoice
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
|
||||||
|
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 = '\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()}
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="{{ LANGUAGE_CODE }}" xml:lang="{{ LANGUAGE_CODE }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
|
||||||
|
<head>
|
||||||
|
<title>{% block title %}{% endblock %}</title>
|
||||||
|
<style type="text/css">
|
||||||
|
* { margin:0; padding:0; }
|
||||||
|
body { background:#eee; color:#333; font:76%/1.6 "Lucida Grande","Bitstream Vera Sans",Verdana,sans-serif; }
|
||||||
|
a { color: #5b80b2; text-decoration:none; }
|
||||||
|
a:hover { text-decoration:underline; }
|
||||||
|
a img { border:none; }
|
||||||
|
h1 { font-size:1.8em; color:#666; margin:0.4em 0 0.2em 0; }
|
||||||
|
h2 { font-size:1.5em; color:#666; margin:1em 0 0.2em 0; }
|
||||||
|
p { margin:0.5em 0 1em 0; }
|
||||||
|
.odd { background-color:#EDF3FE; }
|
||||||
|
.quiet { color:#666; }
|
||||||
|
/* FILTERS */
|
||||||
|
.filter { color:#999; font-size:0.9em; float:left; margin-bottom:10px; margin-right:20px; }
|
||||||
|
.filter strong { color:#666; }
|
||||||
|
/* OBJECT LISTS */
|
||||||
|
.objectlist { clear:both; margin:0 -20px; color:#666; }
|
||||||
|
.objectlist li a { display:block; padding:1em 20px; }
|
||||||
|
.objectlist li a:hover { background:#5b80b2; color:#3B5572; color:#fff; text-decoration:none; }
|
||||||
|
.related h2 { font-size: 1em; margin-bottom: 0.6em; }
|
||||||
|
.related .objectlist li a { padding: 0.6em 20px; }
|
||||||
|
.related .objectlist li.odd { background:#eee; }
|
||||||
|
/* OBJECT DETAIL */
|
||||||
|
.objectinfo { border-collapse:collapse; color:#666; margin:0 -20px; }
|
||||||
|
.objectinfo td, .objectinfo th { padding:1em 20px; vertical-align:top; }
|
||||||
|
.objectinfo td { width:100%; }
|
||||||
|
.objectinfo th { text-align:left; white-space:nowrap; }
|
||||||
|
/* MODEL GROUPS */
|
||||||
|
.modelgroup { color:#999; font-size:0.9em; margin:0 -20px; }
|
||||||
|
.modelgroup h2 { font-size:1.2em; margin:0; }
|
||||||
|
.modelgroup h2 a { display: block; padding: 0.83em 20px; }
|
||||||
|
.modelgroup h2 a:hover { text-decoration: none; color: #fff; }
|
||||||
|
.modelgroup p { float:left; margin:-2.65em 0 0 14em; position:relative; }
|
||||||
|
.modelgroup p a { white-space:nowrap; }
|
||||||
|
.modelgroup a.more { color:#999; }
|
||||||
|
.modelgroup:hover { background:#5b80b2; color:#becfe5; }
|
||||||
|
.modelgroup:hover p a { color:#becfe5; }
|
||||||
|
.modelgroup:hover a { color:#fff; }
|
||||||
|
.modelgroup:hover a.more { color:#fff; }
|
||||||
|
/* BREADCRUMBS */
|
||||||
|
#breadcrumbs { padding:10px 0; color:#999; font-size:0.9em; }
|
||||||
|
/* HEADER */
|
||||||
|
#header a { display:block; background:#eee; color:#676868; padding:10px 20px; font-weight:bold; font-size:1em; text-decoration:none; border-bottom:1px solid #ddd; }
|
||||||
|
#header a:hover { text-decoration:underline; }
|
||||||
|
/* CONTENT */
|
||||||
|
#content { background:#fff; border-bottom:1px solid #ddd; padding:0 20px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body id="{% block bodyid %}page{% endblock %}">
|
||||||
|
<div id="header"><a href="{{ root_url }}">Databrowse</a></div>
|
||||||
|
<div id="content">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} {{ day|date:"F j, Y" }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../../">Calendars</a> / <a href="../../../">By {{ field.verbose_name }}</a> / <a href="../../">{{ day.year }}</a> / <a href="../">{{ day|date:"F" }}</a> / {{ day.day }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} on {{ day|date:"F j, Y" }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for object in object_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Calendars{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / Calendars</div>
|
||||||
|
|
||||||
|
<h1>Calendars</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for field in field_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ field.name }}/">{{ model.verbose_name_plural|capfirst }} by {{ field.verbose_name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ field.verbose_name|capfirst }} calendar{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../">Calendars</a> / By {{ field.verbose_name }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst }} by {{ field.verbose_name }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for year in date_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ year.year }}/">{{ year.year }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ month|date:"F Y" }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../">Calendars</a> / <a href="../../">By {{ field.verbose_name }}</a> / <a href="../">{{ month.year }}</a> / {{ month|date:"F" }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ month|date:"F Y" }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for object in object_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ year }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../">Calendars</a> / <a href="../">By {{ field.verbose_name }}</a> / {{ year }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst }} with {{ field.verbose_name }} in {{ year }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for month in date_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ month|date:"M"|lower }}/">{{ month|date:"F" }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ model.verbose_name_plural|capfirst }} by {{ field.field.verbose_name }}: {{ value|escape }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="{{ field.url }}">By {{ field.field.verbose_name }}</a> / {{ value|escape }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst }} by {{ field.field.verbose_name }}: {{ value|escape }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for object in object_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ model.verbose_name_plural|capfirst }} by {{ field.field.verbose_name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / By {{ field.field.verbose_name }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst }} by {{ field.field.verbose_name }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for choice in field.choices %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ choice.url }}">{{ choice.label }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ model.verbose_name_plural|capfirst|escape }} with {{ field.field.verbose_name|escape }} {{ value|escape }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../">Fields</a> / <a href="../">By {{ field.field.verbose_name|escape }}</a> / {{ value|escape }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst|escape }} with {{ field.field.verbose_name|escape }} {{ value|escape }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for object in object_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Browsable fields in {{ model.verbose_name_plural|escape }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / Fields</div>
|
||||||
|
|
||||||
|
<h1>Browsable fields in {{ model.verbose_name_plural }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for field in field_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ field.name }}/">{{ model.verbose_name_plural|capfirst }} by {{ field.verbose_name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ model.verbose_name_plural|capfirst|escape }} by {{ field.field.verbose_name|escape }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../">Fields</a> / By {{ field.field.verbose_name|escape }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst|escape }} by {{ field.field.verbose_name|escape }}</h1>
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for object in object_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ object }}/">{{ object|escape }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Databrowse{% endblock %}
|
||||||
|
|
||||||
|
{% block bodyid %}homepage{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% for model in model_list %}
|
||||||
|
<div class="modelgroup {% cycle even,odd %}">
|
||||||
|
<h2><a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a></h2>
|
||||||
|
<p>
|
||||||
|
{% for object in model.sample_objects %}
|
||||||
|
<a href="{{ object.url }}">{{ object }}</a>,
|
||||||
|
{% endfor %}
|
||||||
|
<a class="more" href="{{ model.url }}">More →</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ model.verbose_name_plural|capfirst }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / {{ model.verbose_name_plural|capfirst }}</div>
|
||||||
|
|
||||||
|
<h1>{{ model.verbose_name_plural|capfirst }}</h1>
|
||||||
|
|
||||||
|
{{ plugin_html }}
|
||||||
|
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for object in model.objects %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,41 @@
|
||||||
|
{% extends "databrowse/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ object.model.verbose_name|capfirst }}: {{ object }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ object.model.url }}">{{ object.model.verbose_name_plural|capfirst }}</a> / {{ object }}</div>
|
||||||
|
|
||||||
|
<h1>{{ object.model.verbose_name|capfirst }}: {{ object }}</h1>
|
||||||
|
|
||||||
|
<table class="objectinfo">
|
||||||
|
{% for field in object.fields %}
|
||||||
|
<tr class="{% cycle odd,even %}">
|
||||||
|
<th>{{ field.field.verbose_name|capfirst }}</th>
|
||||||
|
<td>
|
||||||
|
{% if field.urls %}
|
||||||
|
{% for urlvalue in field.urls %}
|
||||||
|
{% if urlvalue.1 %}<a href="{{ urlvalue.1 }}">{% endif %}{{ urlvalue.0 }}{% if urlvalue.1 %}</a>{% endif %}{% if not forloop.last %}, {% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}None{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% for related_object in object.related_objects %}
|
||||||
|
<div class="related">
|
||||||
|
<h2>Appears in "{{ related_object.related_field }}" in the following {{ related_object.model.verbose_name_plural }}:</h2>
|
||||||
|
{% if related_object.object_list %}
|
||||||
|
<ul class="objectlist">
|
||||||
|
{% for object in related_object.object_list %}
|
||||||
|
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p class="quiet">(None)</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,20 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
from django.contrib.databrowse import views
|
||||||
|
|
||||||
|
# Note: The views in this URLconf all require a 'models' argument,
|
||||||
|
# which is a list of model classes (*not* instances).
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
#(r'^$', views.homepage),
|
||||||
|
#(r'^([^/]+)/([^/]+)/$', views.model_detail),
|
||||||
|
|
||||||
|
(r'^([^/]+)/([^/]+)/fields/(\w+)/$', views.choice_list),
|
||||||
|
(r'^([^/]+)/([^/]+)/fields/(\w+)/(.*)/$', views.choice_detail),
|
||||||
|
|
||||||
|
#(r'^([^/]+)/([^/]+)/calendars/(\w+)/$', views.calendar_main),
|
||||||
|
#(r'^([^/]+)/([^/]+)/calendars/(\w+)/(\d{4})/$', views.calendar_year),
|
||||||
|
#(r'^([^/]+)/([^/]+)/calendars/(\w+)/(\d{4})/(\w{3})/$', views.calendar_month),
|
||||||
|
#(r'^([^/]+)/([^/]+)/calendars/(\w+)/(\d{4})/(\w{3})/(\d{1,2})/$', views.calendar_day),
|
||||||
|
|
||||||
|
#(r'^([^/]+)/([^/]+)/objects/(.*)/$', views.object_detail),
|
||||||
|
)
|
|
@ -0,0 +1,23 @@
|
||||||
|
from django.db.models import FieldDoesNotExist, DateTimeField
|
||||||
|
from django.http import Http404
|
||||||
|
from django.shortcuts import render_to_response
|
||||||
|
from django.contrib.databrowse.datastructures import EasyModel, EasyChoice
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
###########
|
||||||
|
# CHOICES #
|
||||||
|
###########
|
||||||
|
|
||||||
|
def choice_list(request, app_label, module_name, field_name, models):
|
||||||
|
m, f = lookup_field(app_label, module_name, field_name, models)
|
||||||
|
return render_to_response('databrowse/choice_list.html', {'model': m, 'field': f})
|
||||||
|
|
||||||
|
def choice_detail(request, app_label, module_name, field_name, field_val, models):
|
||||||
|
m, f = lookup_field(app_label, module_name, field_name, models)
|
||||||
|
try:
|
||||||
|
label = dict(f.field.choices)[field_val]
|
||||||
|
except KeyError:
|
||||||
|
raise Http404('Invalid choice value given')
|
||||||
|
obj_list = m.objects(**{f.field.name: field_val})
|
||||||
|
return render_to_response('databrowse/choice_detail.html', {'model': m, 'field': f, 'value': label, 'object_list': obj_list})
|
|
@ -0,0 +1,54 @@
|
||||||
|
==========
|
||||||
|
Databrowse
|
||||||
|
==========
|
||||||
|
|
||||||
|
Databrowse is a Django application that lets you browse your data.
|
||||||
|
|
||||||
|
As the Django admin dynamically creates an admin interface by introspecting
|
||||||
|
your models, Databrowse dynamically creates a rich, browsable Web site by
|
||||||
|
introspecting your models.
|
||||||
|
|
||||||
|
.. admonition:: Note
|
||||||
|
|
||||||
|
Databrowse is **very** new and is currently under active development. It
|
||||||
|
may change substantially before the next Django release.
|
||||||
|
|
||||||
|
With that said, it's easy to use, and it doesn't require writing any
|
||||||
|
code. So you can play around with it today, with very little investment in
|
||||||
|
time or coding.
|
||||||
|
|
||||||
|
How to use Databrowse
|
||||||
|
=====================
|
||||||
|
|
||||||
|
1. Point Django at the default Databrowse templates. There are two ways to
|
||||||
|
do this:
|
||||||
|
|
||||||
|
* Add ``'django.contrib.databrowse'`` to your ``INSTALLED_APPS``
|
||||||
|
setting. This will work if your ``TEMPLATE_LOADERS`` setting includes
|
||||||
|
the ``app_directories`` template loader (which is the case by
|
||||||
|
default). See the `template loader docs`_ for more.
|
||||||
|
|
||||||
|
* Otherwise, determine the full filesystem path to the
|
||||||
|
``django/contrib/databrowse/templates`` directory, and add that
|
||||||
|
directory to your ``TEMPLATE_DIRS`` setting.
|
||||||
|
|
||||||
|
2. Register a number of models with the Databrowse site::
|
||||||
|
|
||||||
|
from django.contrib import databrowse
|
||||||
|
|
||||||
|
databrowse.site.register(SomeModel)
|
||||||
|
databrowse.site.register(SomeOtherModel)
|
||||||
|
|
||||||
|
Note that you should register the model *classes*, not instances.
|
||||||
|
|
||||||
|
It doesn't matter where you put this, as long as it gets executed at
|
||||||
|
some point. A good place for it is in your URLconf file (``urls.py``).
|
||||||
|
|
||||||
|
3. Add the following line to your URLconf::
|
||||||
|
|
||||||
|
(r'^databrowse/(.*)', databrowse.site.root),
|
||||||
|
|
||||||
|
The prefix doesn't matter -- you can use ``databrowse/`` or ``db/`` or
|
||||||
|
whatever you'd like.
|
||||||
|
|
||||||
|
4. Run the Django server and visit ``/databrowse/`` in your browser.
|
Loading…
Reference in New Issue