diff options
Diffstat (limited to 'webapp/django/contrib/databrowse/datastructures.py')
-rw-r--r-- | webapp/django/contrib/databrowse/datastructures.py | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/webapp/django/contrib/databrowse/datastructures.py b/webapp/django/contrib/databrowse/datastructures.py new file mode 100644 index 0000000000..5fdbdbe134 --- /dev/null +++ b/webapp/django/contrib/databrowse/datastructures.py @@ -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 |