app/django/contrib/databrowse/datastructures.py
changeset 54 03e267d67478
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/django/contrib/databrowse/datastructures.py	Fri Jul 18 18:22:23 2008 +0000
@@ -0,0 +1,217 @@
+"""
+These classes are light wrappers around Django's database API that provide
+convenience functionality and permalink functions for the databrowse app.
+"""
+
+from django.db import models
+from django.utils import dateformat
+from django.utils.text import capfirst
+from django.utils.translation import get_date_formats
+from django.utils.encoding import smart_unicode, smart_str, iri_to_uri
+from django.utils.safestring import mark_safe
+from django.db.models.query import QuerySet
+
+EMPTY_VALUE = '(None)'
+DISPLAY_SIZE = 100
+
+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>' % smart_str(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 mark_safe('%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name))
+
+    def objects(self, **kwargs):
+        return self.get_query_set().filter(**kwargs)
+
+    def get_query_set(self):
+        easy_qs = self.model._default_manager.get_query_set()._clone(klass=EasyQuerySet)
+        easy_qs._easymodel = self
+        return easy_qs
+
+    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 smart_str(u'<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 mark_safe('%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 mark_safe('%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 smart_str(u'<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
+
+    def url(self):
+        return mark_safe('%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, iri_to_uri(self.value)))
+
+class EasyInstance(object):
+    def __init__(self, easy_model, instance):
+        self.model, self.instance = easy_model, instance
+
+    def __repr__(self):
+        return smart_str(u'<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val()))
+
+    def __unicode__(self):
+        val = smart_unicode(self.instance)
+        if len(val) > DISPLAY_SIZE:
+            return val[:DISPLAY_SIZE] + u'...'
+        return val
+
+    def __str__(self):
+        return self.__unicode__().encode('utf-8')
+
+    def pk(self):
+        return self.instance._get_pk_val()
+
+    def url(self):
+        return mark_safe('%s%s/%s/objects/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, iri_to_uri(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 smart_str(u'<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.
+        """
+        # This import is deliberately inside the function because it causes
+        # some settings to be imported, and we don't want to do that at the
+        # module level.
+        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_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_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 = mark_safe('%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, iri_to_uri(value._get_pk_val())))
+                    lst.append((smart_unicode(value), url))
+            else:
+                lst = [(value, None) for value in self.values()]
+        elif self.field.choices:
+            lst = []
+            for value in self.values():
+                url = mark_safe('%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, iri_to_uri(self.raw_value)))
+                lst.append((value, url))
+        elif isinstance(self.field, models.URLField):
+            val = self.values()[0]
+            lst = [(val, iri_to_uri(val))]
+        else:
+            lst = [(self.values()[0], None)]
+        return lst
+
+class EasyQuerySet(QuerySet):
+    """
+    When creating (or cloning to) an `EasyQuerySet`, make sure to set the
+    `_easymodel` variable to the related `EasyModel`.
+    """
+    def iterator(self, *args, **kwargs):
+        for obj in super(EasyQuerySet, self).iterator(*args, **kwargs):
+            yield EasyInstance(self._easymodel, obj)
+
+    def _clone(self, *args, **kwargs):
+        c = super(EasyQuerySet, self)._clone(*args, **kwargs)
+        c._easymodel = self._easymodel
+        return c