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