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:
Adrian Holovaty 2007-04-16 21:56:10 +00:00
parent 83c4c53120
commit 30fb62d71a
24 changed files with 913 additions and 0 deletions

View File

@ -0,0 +1 @@
from django.contrib.databrowse.sites import DatabrowsePlugin, ModelDatabrowse, DatabrowseSite, site

View File

@ -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

View File

@ -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))

View File

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

View File

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

View File

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

View File

@ -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>

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 &rarr;</a>
</p>
</div>
{% endfor %}
{% endblock %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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),
)

View File

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

54
docs/databrowse.txt Normal file
View File

@ -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.