1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

boulder-oracle-sprint: Merged to [5013]

git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5014 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Boulder Sprinters 2007-04-16 23:24:58 +00:00
parent 9699399eee
commit e2a3b9e1bd
29 changed files with 940 additions and 15 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})

56
docs/databrowse.txt Normal file
View File

@ -0,0 +1,56 @@
==========
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.
.. _template loader docs: ../templates_python/#loader-types

View File

@ -332,7 +332,7 @@ sqlall [appname appname ...]
Prints the CREATE TABLE and initial-data SQL statements for the given appnames. Prints the CREATE TABLE and initial-data SQL statements for the given appnames.
Refer to the description of ``sqlinitialdata`` for an explanation of how to Refer to the description of ``sqlcustom`` for an explanation of how to
specify initial data. specify initial data.
sqlclear [appname appname ...] sqlclear [appname appname ...]

View File

@ -58,6 +58,7 @@ installed.
If you're on Windows, check out the unofficial `compiled Windows version`_. If you're on Windows, check out the unofficial `compiled Windows version`_.
* If you're using MySQL, you'll need MySQLdb_, version 1.2.1p2 or higher. * If you're using MySQL, you'll need MySQLdb_, version 1.2.1p2 or higher.
You will also want to read the database-specific notes for the `MySQL backend`_.
* If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher. * If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher.
@ -69,6 +70,7 @@ installed.
.. _MySQLdb: http://sourceforge.net/projects/mysql-python .. _MySQLdb: http://sourceforge.net/projects/mysql-python
.. _SQLite: http://www.sqlite.org/ .. _SQLite: http://www.sqlite.org/
.. _pysqlite: http://initd.org/tracker/pysqlite .. _pysqlite: http://initd.org/tracker/pysqlite
.. _MySQL backend: ../databases/
Remove any old versions of Django Remove any old versions of Django
================================= =================================

View File

@ -21,7 +21,7 @@ A companion to this document is the `official repository of model examples`_.
(In the Django source distribution, these examples are in the (In the Django source distribution, these examples are in the
``tests/modeltests`` directory.) ``tests/modeltests`` directory.)
.. _Database API reference: http://www.djangoproject.com/documentation/db_api/ .. _Database API reference: ../db-api/
.. _official repository of model examples: http://www.djangoproject.com/documentation/models/ .. _official repository of model examples: http://www.djangoproject.com/documentation/models/
Quick example Quick example
@ -57,7 +57,7 @@ Some technical notes:
syntax, but it's worth noting Django uses SQL tailored to the database syntax, but it's worth noting Django uses SQL tailored to the database
backend specified in your `settings file`_. backend specified in your `settings file`_.
.. _settings file: http://www.djangoproject.com/documentation/settings/ .. _settings file: ../settings/
Fields Fields
====== ======
@ -501,7 +501,7 @@ For each model field that has ``choices`` set, Django will add a method to
retrieve the human-readable name for the field's current value. See retrieve the human-readable name for the field's current value. See
`get_FOO_display`_ in the database API documentation. `get_FOO_display`_ in the database API documentation.
.. _get_FOO_display: ../db_api/#get-foo-display .. _get_FOO_display: ../db-api/#get-foo-display
Finally, note that choices can be any iterable object -- not necessarily a Finally, note that choices can be any iterable object -- not necessarily a
list or tuple. This lets you construct choices dynamically. But if you find list or tuple. This lets you construct choices dynamically. But if you find
@ -626,7 +626,7 @@ that takes the parameters ``field_data, all_data`` and raises
Django comes with quite a few validators. They're in ``django.core.validators``. Django comes with quite a few validators. They're in ``django.core.validators``.
.. _validator docs: http://www.djangoproject.com/documentation/forms/#validators .. _validator docs: ../forms/#validators
Verbose field names Verbose field names
------------------- -------------------
@ -792,8 +792,8 @@ relationship should work. All are optional:
the related object. the related object.
======================= ============================================================ ======================= ============================================================
.. _`Database API reference`: http://www.djangoproject.com/documentation/db_api/ .. _`Database API reference`: ../db-api/
.. _related objects documentation: http://www.djangoproject.com/documentation/db_api/#related-objects .. _related objects documentation: ../db-api/#related-objects
Many-to-many relationships Many-to-many relationships
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -963,7 +963,7 @@ Example::
See the `docs for latest()`_ for more. See the `docs for latest()`_ for more.
.. _docs for latest(): http://www.djangoproject.com/documentation/db_api/#latest-field-name-none .. _docs for latest(): ../db-api/#latest-field-name-none
``order_with_respect_to`` ``order_with_respect_to``
------------------------- -------------------------
@ -1397,7 +1397,7 @@ if one of the ``list_display`` fields is a ``ForeignKey``.
For more on ``select_related()``, see `the select_related() docs`_. For more on ``select_related()``, see `the select_related() docs`_.
.. _the select_related() docs: http://www.djangoproject.com/documentation/db_api/#select-related .. _the select_related() docs: ../db-api/#select-related
``ordering`` ``ordering``
------------ ------------
@ -1502,7 +1502,7 @@ The way ``Manager`` classes work is documented in the `Retrieving objects`_
section of the database API docs, but this section specifically touches on section of the database API docs, but this section specifically touches on
model options that customize ``Manager`` behavior. model options that customize ``Manager`` behavior.
.. _Retrieving objects: http://www.djangoproject.com/documentation/db_api/#retrieving-objects .. _Retrieving objects: ../db-api/#retrieving-objects
Manager names Manager names
------------- -------------
@ -1825,7 +1825,7 @@ just the ``where``, ``tables`` and ``params`` arguments to the standard lookup
API. See `Other lookup options`_. API. See `Other lookup options`_.
.. _Python DB-API: http://www.python.org/peps/pep-0249.html .. _Python DB-API: http://www.python.org/peps/pep-0249.html
.. _Other lookup options: http://www.djangoproject.com/documentation/db_api/#extra-params-select-where-tables .. _Other lookup options: ../db-api/#extra-params-select-where-tables
Overriding default model methods Overriding default model methods
-------------------------------- --------------------------------
@ -1858,7 +1858,7 @@ You can also prevent saving::
else: else:
super(Blog, self).save() # Call the "real" save() method. super(Blog, self).save() # Call the "real" save() method.
.. _database API docs: http://www.djangoproject.com/documentation/db_api/ .. _database API docs: ../db-api/
Models across files Models across files
=================== ===================
@ -1915,7 +1915,7 @@ Each SQL file, if given, is expected to contain valid SQL. The SQL files are
piped directly into the database after all of the models' table-creation piped directly into the database after all of the models' table-creation
statements have been executed. statements have been executed.
The SQL files are read by the ``sqlinitialdata``, ``sqlreset``, ``sqlall`` and The SQL files are read by the ``sqlcustom``, ``sqlreset``, ``sqlall`` and
``reset`` commands in ``manage.py``. Refer to the `manage.py documentation`_ ``reset`` commands in ``manage.py``. Refer to the `manage.py documentation`_
for more information. for more information.
@ -1924,7 +1924,7 @@ order in which they're executed. The only thing you can assume is that, by the
time your custom data files are executed, all the database tables already will time your custom data files are executed, all the database tables already will
have been created. have been created.
.. _`manage.py documentation`: http://www.djangoproject.com/documentation/django_admin/#sqlinitialdata-appname-appname .. _`manage.py documentation`: ../django_admin/#sqlcustom-appname-appname
Database-backend-specific SQL data Database-backend-specific SQL data
---------------------------------- ----------------------------------

View File

@ -1293,3 +1293,11 @@ A collection of template filters that implement these common markup languages:
* Textile * Textile
* Markdown * Markdown
* ReST (ReStructured Text) * ReST (ReStructured Text)
django.contrib.webdesign
------------------------
A collection of template tags that can be useful while designing a website,
such as a generator of Lorem Ipsum text. See the `webdesign documentation`_.
.. _webdesign documentation: ../webdesign/

View File

@ -382,7 +382,7 @@ If you're interested, also run the following commands:
statements for this app. statements for this app.
* ``python manage.py sqlall polls`` -- A combination of all the SQL from * ``python manage.py sqlall polls`` -- A combination of all the SQL from
the 'sql', 'sqlinitialdata', and 'sqlindexes' commands. the 'sql', 'sqlcustom', and 'sqlindexes' commands.
Looking at the output of those commands can help you understand what's actually Looking at the output of those commands can help you understand what's actually
happening under the hood. happening under the hood.