Merged the queryset-refactor branch into trunk.
[fdr-django.git] / django / db / models / options.py
blob8fcaed485e8c796dff89d391ecb95e68ead956d1
1 import re
2 from bisect import bisect
3 try:
4 set
5 except NameError:
6 from sets import Set as set # Python 2.3 fallback
8 from django.conf import settings
9 from django.db.models.related import RelatedObject
10 from django.db.models.fields.related import ManyToManyRel
11 from django.db.models.fields import AutoField, FieldDoesNotExist
12 from django.db.models.fields.proxy import OrderWrt
13 from django.db.models.loading import get_models, app_cache_ready
14 from django.db.models import Manager
15 from django.utils.translation import activate, deactivate_all, get_language, string_concat
16 from django.utils.encoding import force_unicode, smart_str
17 from django.utils.datastructures import SortedDict
19 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
20 get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip()
22 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
23 'unique_together', 'permissions', 'get_latest_by',
24 'order_with_respect_to', 'app_label', 'db_tablespace',
25 'abstract')
27 class Options(object):
28 def __init__(self, meta):
29 self.local_fields, self.local_many_to_many = [], []
30 self.module_name, self.verbose_name = None, None
31 self.verbose_name_plural = None
32 self.db_table = ''
33 self.ordering = []
34 self.unique_together = []
35 self.permissions = []
36 self.object_name, self.app_label = None, None
37 self.get_latest_by = None
38 self.order_with_respect_to = None
39 self.db_tablespace = settings.DEFAULT_TABLESPACE
40 self.admin = None
41 self.meta = meta
42 self.pk = None
43 self.has_auto_field, self.auto_field = False, None
44 self.one_to_one_field = None
45 self.abstract = False
46 self.parents = SortedDict()
48 def contribute_to_class(self, cls, name):
49 cls._meta = self
50 self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
51 # First, construct the default values for these options.
52 self.object_name = cls.__name__
53 self.module_name = self.object_name.lower()
54 self.verbose_name = get_verbose_name(self.object_name)
56 # Next, apply any overridden values from 'class Meta'.
57 if self.meta:
58 meta_attrs = self.meta.__dict__.copy()
59 del meta_attrs['__module__']
60 del meta_attrs['__doc__']
61 for attr_name in DEFAULT_NAMES:
62 if attr_name in meta_attrs:
63 setattr(self, attr_name, meta_attrs.pop(attr_name))
64 elif hasattr(self.meta, attr_name):
65 setattr(self, attr_name, getattr(self.meta, attr_name))
67 # unique_together can be either a tuple of tuples, or a single
68 # tuple of two strings. Normalize it to a tuple of tuples, so that
69 # calling code can uniformly expect that.
70 ut = meta_attrs.pop('unique_together', getattr(self, 'unique_together'))
71 if ut and not isinstance(ut[0], (tuple, list)):
72 ut = (ut,)
73 setattr(self, 'unique_together', ut)
75 # verbose_name_plural is a special case because it uses a 's'
76 # by default.
77 setattr(self, 'verbose_name_plural', meta_attrs.pop('verbose_name_plural', string_concat(self.verbose_name, 's')))
79 # Any leftover attributes must be invalid.
80 if meta_attrs != {}:
81 raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
82 else:
83 self.verbose_name_plural = string_concat(self.verbose_name, 's')
84 del self.meta
86 def _prepare(self, model):
87 from django.db import connection
88 from django.db.backends.util import truncate_name
89 if self.order_with_respect_to:
90 self.order_with_respect_to = self.get_field(self.order_with_respect_to)
91 self.ordering = ('_order',)
92 else:
93 self.order_with_respect_to = None
95 if self.pk is None:
96 if self.parents:
97 # Promote the first parent link in lieu of adding yet another
98 # field.
99 field = self.parents.value_for_index(0)
100 field.primary_key = True
101 self.pk = field
102 else:
103 auto = AutoField(verbose_name='ID', primary_key=True,
104 auto_created=True)
105 model.add_to_class('id', auto)
107 # If the db_table wasn't provided, use the app_label + module_name.
108 if not self.db_table:
109 self.db_table = "%s_%s" % (self.app_label, self.module_name)
110 self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
112 def add_field(self, field):
113 # Insert the given field in the order in which it was created, using
114 # the "creation_counter" attribute of the field.
115 # Move many-to-many related fields from self.fields into
116 # self.many_to_many.
117 if field.rel and isinstance(field.rel, ManyToManyRel):
118 self.local_many_to_many.insert(bisect(self.local_many_to_many, field), field)
119 if hasattr(self, '_m2m_cache'):
120 del self._m2m_cache
121 else:
122 self.local_fields.insert(bisect(self.local_fields, field), field)
123 self.setup_pk(field)
124 if hasattr(self, '_field_cache'):
125 del self._field_cache
126 del self._field_name_cache
128 if hasattr(self, '_name_map'):
129 del self._name_map
131 def setup_pk(self, field):
132 if not self.pk and field.primary_key:
133 self.pk = field
134 field.serialize = False
136 def __repr__(self):
137 return '<Options for %s>' % self.object_name
139 def __str__(self):
140 return "%s.%s" % (smart_str(self.app_label), smart_str(self.module_name))
142 def verbose_name_raw(self):
144 There are a few places where the untranslated verbose name is needed
145 (so that we get the same value regardless of currently active
146 locale).
148 lang = get_language()
149 deactivate_all()
150 raw = force_unicode(self.verbose_name)
151 activate(lang)
152 return raw
153 verbose_name_raw = property(verbose_name_raw)
155 def _fields(self):
157 The getter for self.fields. This returns the list of field objects
158 available to this model (including through parent models).
160 Callers are not permitted to modify this list, since it's a reference
161 to this instance (not a copy).
163 try:
164 self._field_name_cache
165 except AttributeError:
166 self._fill_fields_cache()
167 return self._field_name_cache
168 fields = property(_fields)
170 def get_fields_with_model(self):
172 Returns a sequence of (field, model) pairs for all fields. The "model"
173 element is None for fields on the current model. Mostly of use when
174 constructing queries so that we know which model a field belongs to.
176 try:
177 self._field_cache
178 except AttributeError:
179 self._fill_fields_cache()
180 return self._field_cache
182 def _fill_fields_cache(self):
183 cache = []
184 for parent in self.parents:
185 for field, model in parent._meta.get_fields_with_model():
186 if model:
187 cache.append((field, model))
188 else:
189 cache.append((field, parent))
190 cache.extend([(f, None) for f in self.local_fields])
191 self._field_cache = tuple(cache)
192 self._field_name_cache = [x for x, _ in cache]
194 def _many_to_many(self):
195 try:
196 self._m2m_cache
197 except AttributeError:
198 self._fill_m2m_cache()
199 return self._m2m_cache.keys()
200 many_to_many = property(_many_to_many)
202 def get_m2m_with_model(self):
204 The many-to-many version of get_fields_with_model().
206 try:
207 self._m2m_cache
208 except AttributeError:
209 self._fill_m2m_cache()
210 return self._m2m_cache.items()
212 def _fill_m2m_cache(self):
213 cache = SortedDict()
214 for parent in self.parents:
215 for field, model in parent._meta.get_m2m_with_model():
216 if model:
217 cache[field] = model
218 else:
219 cache[field] = parent
220 for field in self.local_many_to_many:
221 cache[field] = None
222 self._m2m_cache = cache
224 def get_field(self, name, many_to_many=True):
226 Returns the requested field by name. Raises FieldDoesNotExist on error.
228 to_search = many_to_many and (self.fields + self.many_to_many) or self.fields
229 for f in to_search:
230 if f.name == name:
231 return f
232 raise FieldDoesNotExist, '%s has no field named %r' % (self.object_name, name)
234 def get_field_by_name(self, name):
236 Returns the (field_object, model, direct, m2m), where field_object is
237 the Field instance for the given name, model is the model containing
238 this field (None for local fields), direct is True if the field exists
239 on this model, and m2m is True for many-to-many relations. When
240 'direct' is False, 'field_object' is the corresponding RelatedObject
241 for this field (since the field doesn't have an instance associated
242 with it).
244 Uses a cache internally, so after the first access, this is very fast.
246 try:
247 try:
248 return self._name_map[name]
249 except AttributeError:
250 cache = self.init_name_map()
251 return self._name_map[name]
252 except KeyError:
253 raise FieldDoesNotExist('%s has no field named %r'
254 % (self.object_name, name))
256 def get_all_field_names(self):
258 Returns a list of all field names that are possible for this model
259 (including reverse relation names).
261 try:
262 cache = self._name_map
263 except AttributeError:
264 cache = self.init_name_map()
265 names = cache.keys()
266 names.sort()
267 return names
269 def init_name_map(self):
271 Initialises the field name -> field object mapping.
273 cache = dict([(f.name, (f, m, True, False)) for f, m in
274 self.get_fields_with_model()])
275 for f, model in self.get_m2m_with_model():
276 cache[f.name] = (f, model, True, True)
277 for f, model in self.get_all_related_m2m_objects_with_model():
278 cache[f.field.related_query_name()] = (f, model, False, True)
279 for f, model in self.get_all_related_objects_with_model():
280 cache[f.field.related_query_name()] = (f, model, False, False)
281 if self.order_with_respect_to:
282 cache['_order'] = OrderWrt(), None, True, False
283 if app_cache_ready():
284 self._name_map = cache
285 return cache
287 def get_add_permission(self):
288 return 'add_%s' % self.object_name.lower()
290 def get_change_permission(self):
291 return 'change_%s' % self.object_name.lower()
293 def get_delete_permission(self):
294 return 'delete_%s' % self.object_name.lower()
296 def get_all_related_objects(self, local_only=False):
297 try:
298 self._related_objects_cache
299 except AttributeError:
300 self._fill_related_objects_cache()
301 if local_only:
302 return [k for k, v in self._related_objects_cache.items() if not v]
303 return self._related_objects_cache.keys()
305 def get_all_related_objects_with_model(self):
307 Returns a list of (related-object, model) pairs. Similar to
308 get_fields_with_model().
310 try:
311 self._related_objects_cache
312 except AttributeError:
313 self._fill_related_objects_cache()
314 return self._related_objects_cache.items()
316 def _fill_related_objects_cache(self):
317 cache = SortedDict()
318 parent_list = self.get_parent_list()
319 for parent in self.parents:
320 for obj, model in parent._meta.get_all_related_objects_with_model():
321 if (obj.field.creation_counter < 0 or obj.field.rel.parent_link) and obj.model not in parent_list:
322 continue
323 if not model:
324 cache[obj] = parent
325 else:
326 cache[obj] = model
327 for klass in get_models():
328 for f in klass._meta.local_fields:
329 if f.rel and not isinstance(f.rel.to, str) and self == f.rel.to._meta:
330 cache[RelatedObject(f.rel.to, klass, f)] = None
331 self._related_objects_cache = cache
333 def get_all_related_many_to_many_objects(self, local_only=False):
334 try:
335 cache = self._related_many_to_many_cache
336 except AttributeError:
337 cache = self._fill_related_many_to_many_cache()
338 if local_only:
339 return [k for k, v in cache.items() if not v]
340 return cache.keys()
342 def get_all_related_m2m_objects_with_model(self):
344 Returns a list of (related-m2m-object, model) pairs. Similar to
345 get_fields_with_model().
347 try:
348 cache = self._related_many_to_many_cache
349 except AttributeError:
350 cache = self._fill_related_many_to_many_cache()
351 return cache.items()
353 def _fill_related_many_to_many_cache(self):
354 cache = SortedDict()
355 parent_list = self.get_parent_list()
356 for parent in self.parents:
357 for obj, model in parent._meta.get_all_related_m2m_objects_with_model():
358 if obj.field.creation_counter < 0 and obj.model not in parent_list:
359 continue
360 if not model:
361 cache[obj] = parent
362 else:
363 cache[obj] = model
364 for klass in get_models():
365 for f in klass._meta.local_many_to_many:
366 if f.rel and not isinstance(f.rel.to, str) and self == f.rel.to._meta:
367 cache[RelatedObject(f.rel.to, klass, f)] = None
368 if app_cache_ready():
369 self._related_many_to_many_cache = cache
370 return cache
372 def get_followed_related_objects(self, follow=None):
373 if follow == None:
374 follow = self.get_follow()
375 return [f for f in self.get_all_related_objects() if follow.get(f.name, None)]
377 def get_data_holders(self, follow=None):
378 if follow == None:
379 follow = self.get_follow()
380 return [f for f in self.fields + self.many_to_many + self.get_all_related_objects() if follow.get(f.name, None)]
382 def get_follow(self, override=None):
383 follow = {}
384 for f in self.fields + self.many_to_many + self.get_all_related_objects():
385 if override and f.name in override:
386 child_override = override[f.name]
387 else:
388 child_override = None
389 fol = f.get_follow(child_override)
390 if fol != None:
391 follow[f.name] = fol
392 return follow
394 def get_base_chain(self, model):
396 Returns a list of parent classes leading to 'model' (order from closet
397 to most distant ancestor). This has to handle the case were 'model' is
398 a granparent or even more distant relation.
400 if not self.parents:
401 return
402 if model in self.parents:
403 return [model]
404 for parent in self.parents:
405 res = parent._meta.get_base_chain(model)
406 if res:
407 res.insert(0, parent)
408 return res
409 raise TypeError('%r is not an ancestor of this model'
410 % model._meta.module_name)
412 def get_parent_list(self):
414 Returns a list of all the ancestor of this model as a list. Useful for
415 determining if something is an ancestor, regardless of lineage.
417 result = set()
418 for parent in self.parents:
419 result.add(parent)
420 result.update(parent._meta.get_parent_list())
421 return result
423 def get_ordered_objects(self):
424 "Returns a list of Options objects that are ordered with respect to this object."
425 if not hasattr(self, '_ordered_objects'):
426 objects = []
427 # TODO
428 #for klass in get_models(get_app(self.app_label)):
429 # opts = klass._meta
430 # if opts.order_with_respect_to and opts.order_with_respect_to.rel \
431 # and self == opts.order_with_respect_to.rel.to._meta:
432 # objects.append(opts)
433 self._ordered_objects = objects
434 return self._ordered_objects
436 def has_field_type(self, field_type, follow=None):
438 Returns True if this object's admin form has at least one of the given
439 field_type (e.g. FileField).
441 # TODO: follow
442 if not hasattr(self, '_field_types'):
443 self._field_types = {}
444 if field_type not in self._field_types:
445 try:
446 # First check self.fields.
447 for f in self.fields:
448 if isinstance(f, field_type):
449 raise StopIteration
450 # Failing that, check related fields.
451 for related in self.get_followed_related_objects(follow):
452 for f in related.opts.fields:
453 if isinstance(f, field_type):
454 raise StopIteration
455 except StopIteration:
456 self._field_types[field_type] = True
457 else:
458 self._field_types[field_type] = False
459 return self._field_types[field_type]
461 class AdminOptions(object):
462 def __init__(self, fields=None, js=None, list_display=None, list_display_links=None, list_filter=None,
463 date_hierarchy=None, save_as=False, ordering=None, search_fields=None,
464 save_on_top=False, list_select_related=False, manager=None, list_per_page=100):
465 self.fields = fields
466 self.js = js or []
467 self.list_display = list_display or ['__str__']
468 self.list_display_links = list_display_links or []
469 self.list_filter = list_filter or []
470 self.date_hierarchy = date_hierarchy
471 self.save_as, self.ordering = save_as, ordering
472 self.search_fields = search_fields or []
473 self.save_on_top = save_on_top
474 self.list_select_related = list_select_related
475 self.list_per_page = list_per_page
476 self.manager = manager or Manager()
478 def get_field_sets(self, opts):
479 "Returns a list of AdminFieldSet objects for this AdminOptions object."
480 if self.fields is None:
481 field_struct = ((None, {'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]}),)
482 else:
483 field_struct = self.fields
484 new_fieldset_list = []
485 for fieldset in field_struct:
486 fs_options = fieldset[1]
487 classes = fs_options.get('classes', ())
488 description = fs_options.get('description', '')
489 new_fieldset_list.append(AdminFieldSet(fieldset[0], classes,
490 opts.get_field, fs_options['fields'], description))
491 return new_fieldset_list
493 def contribute_to_class(self, cls, name):
494 cls._meta.admin = self
495 # Make sure the admin manager has access to the model
496 self.manager.model = cls
498 class AdminFieldSet(object):
499 def __init__(self, name, classes, field_locator_func, line_specs, description):
500 self.name = name
501 self.field_lines = [AdminFieldLine(field_locator_func, line_spec) for line_spec in line_specs]
502 self.classes = classes
503 self.description = description
505 def __repr__(self):
506 return "FieldSet: (%s, %s)" % (self.name, self.field_lines)
508 def bind(self, field_mapping, original, bound_field_set_class):
509 return bound_field_set_class(self, field_mapping, original)
511 def __iter__(self):
512 for field_line in self.field_lines:
513 yield field_line
515 def __len__(self):
516 return len(self.field_lines)
518 class AdminFieldLine(object):
519 def __init__(self, field_locator_func, linespec):
520 if isinstance(linespec, basestring):
521 self.fields = [field_locator_func(linespec)]
522 else:
523 self.fields = [field_locator_func(field_name) for field_name in linespec]
525 def bind(self, field_mapping, original, bound_field_line_class):
526 return bound_field_line_class(self, field_mapping, original)
528 def __iter__(self):
529 for field in self.fields:
530 yield field
532 def __len__(self):
533 return len(self.fields)