2 from bisect
import bisect
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',
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
34 self
.unique_together
= []
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
43 self
.has_auto_field
, self
.auto_field
= False, None
44 self
.one_to_one_field
= None
46 self
.parents
= SortedDict()
48 def contribute_to_class(self
, cls
, name
):
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'.
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)):
73 setattr(self
, 'unique_together', ut
)
75 # verbose_name_plural is a special case because it uses a 's'
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.
81 raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs
.keys())
83 self
.verbose_name_plural
= string_concat(self
.verbose_name
, 's')
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',)
93 self
.order_with_respect_to
= None
97 # Promote the first parent link in lieu of adding yet another
99 field
= self
.parents
.value_for_index(0)
100 field
.primary_key
= True
103 auto
= AutoField(verbose_name
='ID', primary_key
=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
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'):
122 self
.local_fields
.insert(bisect(self
.local_fields
, field
), field
)
124 if hasattr(self
, '_field_cache'):
125 del self
._field
_cache
126 del self
._field
_name
_cache
128 if hasattr(self
, '_name_map'):
131 def setup_pk(self
, field
):
132 if not self
.pk
and field
.primary_key
:
134 field
.serialize
= False
137 return '<Options for %s>' % self
.object_name
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
148 lang
= get_language()
150 raw
= force_unicode(self
.verbose_name
)
153 verbose_name_raw
= property(verbose_name_raw
)
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).
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.
178 except AttributeError:
179 self
._fill
_fields
_cache
()
180 return self
._field
_cache
182 def _fill_fields_cache(self
):
184 for parent
in self
.parents
:
185 for field
, model
in parent
._meta
.get_fields_with_model():
187 cache
.append((field
, model
))
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
):
197 except AttributeError:
198 self
._fill
_m
2m
_cache
()
199 return self
._m
2m
_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().
208 except AttributeError:
209 self
._fill
_m
2m
_cache
()
210 return self
._m
2m
_cache
.items()
212 def _fill_m2m_cache(self
):
214 for parent
in self
.parents
:
215 for field
, model
in parent
._meta
.get_m2m_with_model():
219 cache
[field
] = parent
220 for field
in self
.local_many_to_many
:
222 self
._m
2m
_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
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
244 Uses a cache internally, so after the first access, this is very fast.
248 return self
._name
_map
[name
]
249 except AttributeError:
250 cache
= self
.init_name_map()
251 return self
._name
_map
[name
]
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).
262 cache
= self
._name
_map
263 except AttributeError:
264 cache
= self
.init_name_map()
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
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):
298 self
._related
_objects
_cache
299 except AttributeError:
300 self
._fill
_related
_objects
_cache
()
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().
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
):
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
:
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):
335 cache
= self
._related
_many
_to
_many
_cache
336 except AttributeError:
337 cache
= self
._fill
_related
_many
_to
_many
_cache
()
339 return [k
for k
, v
in cache
.items() if not v
]
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().
348 cache
= self
._related
_many
_to
_many
_cache
349 except AttributeError:
350 cache
= self
._fill
_related
_many
_to
_many
_cache
()
353 def _fill_related_many_to_many_cache(self
):
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
:
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
372 def get_followed_related_objects(self
, 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):
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):
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
]
388 child_override
= None
389 fol
= f
.get_follow(child_override
)
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.
402 if model
in self
.parents
:
404 for parent
in self
.parents
:
405 res
= parent
._meta
.get_base_chain(model
)
407 res
.insert(0, parent
)
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.
418 for parent
in self
.parents
:
420 result
.update(parent
._meta
.get_parent_list())
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'):
428 #for klass in get_models(get_app(self.app_label)):
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).
442 if not hasattr(self
, '_field_types'):
443 self
._field
_types
= {}
444 if field_type
not in self
._field
_types
:
446 # First check self.fields.
447 for f
in self
.fields
:
448 if isinstance(f
, field_type
):
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
):
455 except StopIteration:
456 self
._field
_types
[field_type
] = True
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):
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
)]}),)
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
):
501 self
.field_lines
= [AdminFieldLine(field_locator_func
, line_spec
) for line_spec
in line_specs
]
502 self
.classes
= classes
503 self
.description
= description
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
)
512 for field_line
in self
.field_lines
:
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
)]
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
)
529 for field
in self
.fields
:
533 return len(self
.fields
)