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