Permalink
Browse files

Added django.contrib.databrowse

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5011 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 83c4c53 commit 30fb62d71af0186c98c7d5208a72609f74943092 @adrianholovaty adrianholovaty committed Apr 16, 2007
Showing with 913 additions and 0 deletions.
  1. +1 −0 django/contrib/databrowse/__init__.py
  2. +188 −0 django/contrib/databrowse/datastructures.py
  3. 0 django/contrib/databrowse/plugins/__init__.py
  4. +84 −0 django/contrib/databrowse/plugins/calendars.py
  5. +72 −0 django/contrib/databrowse/plugins/fieldchoices.py
  6. +14 −0 django/contrib/databrowse/plugins/objects.py
  7. +148 −0 django/contrib/databrowse/sites.py
  8. +58 −0 django/contrib/databrowse/templates/databrowse/base.html
  9. +17 −0 django/contrib/databrowse/templates/databrowse/calendar_day.html
  10. +17 −0 django/contrib/databrowse/templates/databrowse/calendar_homepage.html
  11. +17 −0 django/contrib/databrowse/templates/databrowse/calendar_main.html
  12. +17 −0 django/contrib/databrowse/templates/databrowse/calendar_month.html
  13. +17 −0 django/contrib/databrowse/templates/databrowse/calendar_year.html
  14. +17 −0 django/contrib/databrowse/templates/databrowse/choice_detail.html
  15. +17 −0 django/contrib/databrowse/templates/databrowse/choice_list.html
  16. +17 −0 django/contrib/databrowse/templates/databrowse/fieldchoice_detail.html
  17. +17 −0 django/contrib/databrowse/templates/databrowse/fieldchoice_homepage.html
  18. +17 −0 django/contrib/databrowse/templates/databrowse/fieldchoice_list.html
  19. +21 −0 django/contrib/databrowse/templates/databrowse/homepage.html
  20. +19 −0 django/contrib/databrowse/templates/databrowse/model_detail.html
  21. +41 −0 django/contrib/databrowse/templates/databrowse/object_detail.html
  22. +20 −0 django/contrib/databrowse/urls.py
  23. +23 −0 django/contrib/databrowse/views.py
  24. +54 −0 docs/databrowse.txt
@@ -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})
Oops, something went wrong.

0 comments on commit 30fb62d

Please sign in to comment.