app/django/contrib/databrowse/datastructures.py
changeset 54 03e267d67478
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 """
       
     2 These classes are light wrappers around Django's database API that provide
       
     3 convenience functionality and permalink functions for the databrowse app.
       
     4 """
       
     5 
       
     6 from django.db import models
       
     7 from django.utils import dateformat
       
     8 from django.utils.text import capfirst
       
     9 from django.utils.translation import get_date_formats
       
    10 from django.utils.encoding import smart_unicode, smart_str, iri_to_uri
       
    11 from django.utils.safestring import mark_safe
       
    12 from django.db.models.query import QuerySet
       
    13 
       
    14 EMPTY_VALUE = '(None)'
       
    15 DISPLAY_SIZE = 100
       
    16 
       
    17 class EasyModel(object):
       
    18     def __init__(self, site, model):
       
    19         self.site = site
       
    20         self.model = model
       
    21         self.model_list = site.registry.keys()
       
    22         self.verbose_name = model._meta.verbose_name
       
    23         self.verbose_name_plural = model._meta.verbose_name_plural
       
    24 
       
    25     def __repr__(self):
       
    26         return '<EasyModel for %s>' % smart_str(self.model._meta.object_name)
       
    27 
       
    28     def model_databrowse(self):
       
    29         "Returns the ModelDatabrowse class for this model."
       
    30         return self.site.registry[self.model]
       
    31 
       
    32     def url(self):
       
    33         return mark_safe('%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name))
       
    34 
       
    35     def objects(self, **kwargs):
       
    36         return self.get_query_set().filter(**kwargs)
       
    37 
       
    38     def get_query_set(self):
       
    39         easy_qs = self.model._default_manager.get_query_set()._clone(klass=EasyQuerySet)
       
    40         easy_qs._easymodel = self
       
    41         return easy_qs
       
    42 
       
    43     def object_by_pk(self, pk):
       
    44         return EasyInstance(self, self.model._default_manager.get(pk=pk))
       
    45 
       
    46     def sample_objects(self):
       
    47         for obj in self.model._default_manager.all()[:3]:
       
    48             yield EasyInstance(self, obj)
       
    49 
       
    50     def field(self, name):
       
    51         try:
       
    52             f = self.model._meta.get_field(name)
       
    53         except models.FieldDoesNotExist:
       
    54             return None
       
    55         return EasyField(self, f)
       
    56 
       
    57     def fields(self):
       
    58         return [EasyField(self, f) for f in (self.model._meta.fields + self.model._meta.many_to_many)]
       
    59 
       
    60 class EasyField(object):
       
    61     def __init__(self, easy_model, field):
       
    62         self.model, self.field = easy_model, field
       
    63 
       
    64     def __repr__(self):
       
    65         return smart_str(u'<EasyField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
       
    66 
       
    67     def choices(self):
       
    68         for value, label in self.field.choices:
       
    69             yield EasyChoice(self.model, self, value, label)
       
    70 
       
    71     def url(self):
       
    72         if self.field.choices:
       
    73             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))
       
    74         elif self.field.rel:
       
    75             return mark_safe('%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name))
       
    76 
       
    77 class EasyChoice(object):
       
    78     def __init__(self, easy_model, field, value, label):
       
    79         self.model, self.field = easy_model, field
       
    80         self.value, self.label = value, label
       
    81 
       
    82     def __repr__(self):
       
    83         return smart_str(u'<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
       
    84 
       
    85     def url(self):
       
    86         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)))
       
    87 
       
    88 class EasyInstance(object):
       
    89     def __init__(self, easy_model, instance):
       
    90         self.model, self.instance = easy_model, instance
       
    91 
       
    92     def __repr__(self):
       
    93         return smart_str(u'<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val()))
       
    94 
       
    95     def __unicode__(self):
       
    96         val = smart_unicode(self.instance)
       
    97         if len(val) > DISPLAY_SIZE:
       
    98             return val[:DISPLAY_SIZE] + u'...'
       
    99         return val
       
   100 
       
   101     def __str__(self):
       
   102         return self.__unicode__().encode('utf-8')
       
   103 
       
   104     def pk(self):
       
   105         return self.instance._get_pk_val()
       
   106 
       
   107     def url(self):
       
   108         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())))
       
   109 
       
   110     def fields(self):
       
   111         """
       
   112         Generator that yields EasyInstanceFields for each field in this
       
   113         EasyInstance's model.
       
   114         """
       
   115         for f in self.model.model._meta.fields + self.model.model._meta.many_to_many:
       
   116             yield EasyInstanceField(self.model, self, f)
       
   117 
       
   118     def related_objects(self):
       
   119         """
       
   120         Generator that yields dictionaries of all models that have this
       
   121         EasyInstance's model as a ForeignKey or ManyToManyField, along with
       
   122         lists of related objects.
       
   123         """
       
   124         for rel_object in self.model.model._meta.get_all_related_objects() + self.model.model._meta.get_all_related_many_to_many_objects():
       
   125             if rel_object.model not in self.model.model_list:
       
   126                 continue # Skip models that aren't in the model_list
       
   127             em = EasyModel(self.model.site, rel_object.model)
       
   128             yield {
       
   129                 'model': em,
       
   130                 'related_field': rel_object.field.verbose_name,
       
   131                 'object_list': [EasyInstance(em, i) for i in getattr(self.instance, rel_object.get_accessor_name()).all()],
       
   132             }
       
   133 
       
   134 class EasyInstanceField(object):
       
   135     def __init__(self, easy_model, instance, field):
       
   136         self.model, self.field, self.instance = easy_model, field, instance
       
   137         self.raw_value = getattr(instance.instance, field.name)
       
   138 
       
   139     def __repr__(self):
       
   140         return smart_str(u'<EasyInstanceField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
       
   141 
       
   142     def values(self):
       
   143         """
       
   144         Returns a list of values for this field for this instance. It's a list
       
   145         so we can accomodate many-to-many fields.
       
   146         """
       
   147         # This import is deliberately inside the function because it causes
       
   148         # some settings to be imported, and we don't want to do that at the
       
   149         # module level.
       
   150         if self.field.rel:
       
   151             if isinstance(self.field.rel, models.ManyToOneRel):
       
   152                 objs = getattr(self.instance.instance, self.field.name)
       
   153             elif isinstance(self.field.rel, models.ManyToManyRel): # ManyToManyRel
       
   154                 return list(getattr(self.instance.instance, self.field.name).all())
       
   155         elif self.field.choices:
       
   156             objs = dict(self.field.choices).get(self.raw_value, EMPTY_VALUE)
       
   157         elif isinstance(self.field, models.DateField) or isinstance(self.field, models.TimeField):
       
   158             if self.raw_value:
       
   159                 date_format, datetime_format, time_format = get_date_formats()
       
   160                 if isinstance(self.field, models.DateTimeField):
       
   161                     objs = capfirst(dateformat.format(self.raw_value, datetime_format))
       
   162                 elif isinstance(self.field, models.TimeField):
       
   163                     objs = capfirst(dateformat.time_format(self.raw_value, time_format))
       
   164                 else:
       
   165                     objs = capfirst(dateformat.format(self.raw_value, date_format))
       
   166             else:
       
   167                 objs = EMPTY_VALUE
       
   168         elif isinstance(self.field, models.BooleanField) or isinstance(self.field, models.NullBooleanField):
       
   169             objs = {True: 'Yes', False: 'No', None: 'Unknown'}[self.raw_value]
       
   170         else:
       
   171             objs = self.raw_value
       
   172         return [objs]
       
   173 
       
   174     def urls(self):
       
   175         "Returns a list of (value, URL) tuples."
       
   176         # First, check the urls() method for each plugin.
       
   177         plugin_urls = []
       
   178         for plugin_name, plugin in self.model.model_databrowse().plugins.items():
       
   179             urls = plugin.urls(plugin_name, self)
       
   180             if urls is not None:
       
   181                 #plugin_urls.append(urls)
       
   182                 values = self.values()
       
   183                 return zip(self.values(), urls)
       
   184         if self.field.rel:
       
   185             m = EasyModel(self.model.site, self.field.rel.to)
       
   186             if self.field.rel.to in self.model.model_list:
       
   187                 lst = []
       
   188                 for value in self.values():
       
   189                     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())))
       
   190                     lst.append((smart_unicode(value), url))
       
   191             else:
       
   192                 lst = [(value, None) for value in self.values()]
       
   193         elif self.field.choices:
       
   194             lst = []
       
   195             for value in self.values():
       
   196                 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)))
       
   197                 lst.append((value, url))
       
   198         elif isinstance(self.field, models.URLField):
       
   199             val = self.values()[0]
       
   200             lst = [(val, iri_to_uri(val))]
       
   201         else:
       
   202             lst = [(self.values()[0], None)]
       
   203         return lst
       
   204 
       
   205 class EasyQuerySet(QuerySet):
       
   206     """
       
   207     When creating (or cloning to) an `EasyQuerySet`, make sure to set the
       
   208     `_easymodel` variable to the related `EasyModel`.
       
   209     """
       
   210     def iterator(self, *args, **kwargs):
       
   211         for obj in super(EasyQuerySet, self).iterator(*args, **kwargs):
       
   212             yield EasyInstance(self._easymodel, obj)
       
   213 
       
   214     def _clone(self, *args, **kwargs):
       
   215         c = super(EasyQuerySet, self)._clone(*args, **kwargs)
       
   216         c._easymodel = self._easymodel
       
   217         return c