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