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