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