| 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 |
|---|