Django

Code

root/django/trunk/django/db/models/options.py

Revision 8855, 17.6 kB (checked in by jacob, 3 months ago)

Fixed #8309: subclasses now inherit GenericForeignKey correctly. There's also now an internal API so that other "virtual fields" like GFK can be inherited as well. Thanks, msaelices.

  • Property svn:eol-style set to native
Line 
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
7
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.utils.translation import activate, deactivate_all, get_language, string_concat
15 from django.utils.encoding import force_unicode, smart_str
16 from django.utils.datastructures import SortedDict
17
18 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
19 get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip()
20
21 DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
22                  'unique_together', 'permissions', 'get_latest_by',
23                  'order_with_respect_to', 'app_label', 'db_tablespace',
24                  'abstract')
25
26 class Options(object):
27     def __init__(self, meta, app_label=None):
28         self.local_fields, self.local_many_to_many = [], []
29         self.virtual_fields = []
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, app_label
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()
47         self.duplicate_targets = {}
48         # Managers that have been inherited from abstract base classes. These
49         # are passed onto any children.
50         self.abstract_managers = []
51
52     def contribute_to_class(self, cls, name):
53         from django.db import connection
54         from django.db.backends.util import truncate_name
55
56         cls._meta = self
57         self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
58         # First, construct the default values for these options.
59         self.object_name = cls.__name__
60         self.module_name = self.object_name.lower()
61         self.verbose_name = get_verbose_name(self.object_name)
62
63         # Next, apply any overridden values from 'class Meta'.
64         if self.meta:
65             meta_attrs = self.meta.__dict__.copy()
66             for name in self.meta.__dict__:
67                 # Ignore any private attributes that Django doesn't care about.
68                 # NOTE: We can't modify a dictionary's contents while looping
69                 # over it, so we loop over the *original* dictionary instead.
70                 if name.startswith('_'):
71                     del meta_attrs[name]
72             for attr_name in DEFAULT_NAMES:
73                 if attr_name in meta_attrs:
74                     setattr(self, attr_name, meta_attrs.pop(attr_name))
75                 elif hasattr(self.meta, attr_name):
76                     setattr(self, attr_name, getattr(self.meta, attr_name))
77
78             # unique_together can be either a tuple of tuples, or a single
79             # tuple of two strings. Normalize it to a tuple of tuples, so that
80             # calling code can uniformly expect that.
81             ut = meta_attrs.pop('unique_together', getattr(self, 'unique_together'))
82             if ut and not isinstance(ut[0], (tuple, list)):
83                 ut = (ut,)
84             setattr(self, 'unique_together', ut)
85
86             # verbose_name_plural is a special case because it uses a 's'
87             # by default.
88             setattr(self, 'verbose_name_plural', meta_attrs.pop('verbose_name_plural', string_concat(self.verbose_name, 's')))
89
90             # Any leftover attributes must be invalid.
91             if meta_attrs != {}:
92                 raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
93         else:
94             self.verbose_name_plural = string_concat(self.verbose_name, 's')
95         del self.meta
96
97         # If the db_table wasn't provided, use the app_label + module_name.
98         if not self.db_table:
99             self.db_table = "%s_%s" % (self.app_label, self.module_name)
100             self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
101
102
103     def _prepare(self, model):
104         if self.order_with_respect_to:
105             self.order_with_respect_to = self.get_field(self.order_with_respect_to)
106             self.ordering = ('_order',)
107         else:
108             self.order_with_respect_to = None
109
110         if self.pk is None:
111             if self.parents:
112                 # Promote the first parent link in lieu of adding yet another
113                 # field.
114                 field = self.parents.value_for_index(0)
115                 field.primary_key = True
116                 self.setup_pk(field)
117             else:
118                 auto = AutoField(verbose_name='ID', primary_key=True,
119                         auto_created=True)
120                 model.add_to_class('id', auto)
121
122         # Determine any sets of fields that are pointing to the same targets
123         # (e.g. two ForeignKeys to the same remote model). The query
124         # construction code needs to know this. At the end of this,
125         # self.duplicate_targets will map each duplicate field column to the
126         # columns it duplicates.
127         collections = {}
128         for column, target in self.duplicate_targets.iteritems():
129             try:
130                 collections[target].add(column)
131             except KeyError:
132                 collections[target] = set([column])
133         self.duplicate_targets = {}
134         for elt in collections.itervalues():
135             if len(elt) == 1:
136                 continue
137             for column in elt:
138                 self.duplicate_targets[column] = elt.difference(set([column]))
139
140     def add_field(self, field):
141         # Insert the given field in the order in which it was created, using
142         # the "creation_counter" attribute of the field.
143         # Move many-to-many related fields from self.fields into
144         # self.many_to_many.
145         if field.rel and isinstance(field.rel, ManyToManyRel):
146             self.local_many_to_many.insert(bisect(self.local_many_to_many, field), field)
147             if hasattr(self, '_m2m_cache'):
148                 del self._m2m_cache
149         else:
150             self.local_fields.insert(bisect(self.local_fields, field), field)
151             self.setup_pk(field)
152             if hasattr(self, '_field_cache'):
153                 del self._field_cache
154                 del self._field_name_cache
155
156         if hasattr(self, '_name_map'):
157             del self._name_map
158
159     def add_virtual_field(self, field):
160         self.virtual_fields.append(field)
161
162     def setup_pk(self, field):
163         if not self.pk and field.primary_key:
164             self.pk = field
165             field.serialize = False
166
167     def __repr__(self):
168         return '<Options for %s>' % self.object_name
169
170     def __str__(self):
171         return "%s.%s" % (smart_str(self.app_label), smart_str(self.module_name))
172
173     def verbose_name_raw(self):
174         """
175         There are a few places where the untranslated verbose name is needed
176         (so that we get the same value regardless of currently active
177         locale).
178         """
179         lang = get_language()
180         deactivate_all()
181         raw = force_unicode(self.verbose_name)
182         activate(lang)
183         return raw
184     verbose_name_raw = property(verbose_name_raw)
185
186     def _fields(self):
187         """
188         The getter for self.fields. This returns the list of field objects
189         available to this model (including through parent models).
190
191         Callers are not permitted to modify this list, since it's a reference
192         to this instance (not a copy).
193         """
194         try:
195             self._field_name_cache
196         except AttributeError:
197             self._fill_fields_cache()
198         return self._field_name_cache
199     fields = property(_fields)
200
201     def get_fields_with_model(self):
202         """
203         Returns a sequence of (field, model) pairs for all fields. The "model"
204         element is None for fields on the current model. Mostly of use when
205         constructing queries so that we know which model a field belongs to.
206         """
207         try:
208             self._field_cache
209         except AttributeError:
210             self._fill_fields_cache()
211         return self._field_cache
212
213     def _fill_fields_cache(self):
214         cache = []
215         for parent in self.parents:
216             for field, model in parent._meta.get_fields_with_model():
217                 if model:
218                     cache.append((field, model))
219                 else:
220                     cache.append((field, parent))
221         cache.extend([(f, None) for f in self.local_fields])
222         self._field_cache = tuple(cache)
223         self._field_name_cache = [x for x, _ in cache]
224
225     def _many_to_many(self):
226         try:
227             self._m2m_cache
228         except AttributeError:
229             self._fill_m2m_cache()
230         return self._m2m_cache.keys()
231     many_to_many = property(_many_to_many)
232
233     def get_m2m_with_model(self):
234         """
235         The many-to-many version of get_fields_with_model().
236         """
237         try:
238             self._m2m_cache
239         except AttributeError:
240             self._fill_m2m_cache()
241         return self._m2m_cache.items()
242
243     def _fill_m2m_cache(self):
244         cache = SortedDict()
245         for parent in self.parents:
246             for field, model in parent._meta.get_m2m_with_model():
247                 if model:
248                     cache[field] = model
249                 else:
250                     cache[field] = parent
251         for field in self.local_many_to_many:
252             cache[field] = None
253         self._m2m_cache = cache
254
255     def get_field(self, name, many_to_many=True):
256         """
257         Returns the requested field by name. Raises FieldDoesNotExist on error.
258         """
259         to_search = many_to_many and (self.fields + self.many_to_many) or self.fields
260         for f in to_search:
261             if f.name == name:
262                 return f
263         raise FieldDoesNotExist, '%s has no field named %r' % (self.object_name, name)
264
265     def get_field_by_name(self, name):
266         """
267         Returns the (field_object, model, direct, m2m), where field_object is
268         the Field instance for the given name, model is the model containing
269         this field (None for local fields), direct is True if the field exists
270         on this model, and m2m is True for many-to-many relations. When
271         'direct' is False, 'field_object' is the corresponding RelatedObject
272         for this field (since the field doesn't have an instance associated
273         with it).
274
275         Uses a cache internally, so after the first access, this is very fast.
276         """
277         try:
278             try:
279                 return self._name_map[name]
280             except AttributeError:
281                 cache = self.init_name_map()
282                 return cache[name]
283         except KeyError:
284             raise FieldDoesNotExist('%s has no field named %r'
285                     % (self.object_name, name))
286
287     def get_all_field_names(self):
288         """
289         Returns a list of all field names that are possible for this model
290         (including reverse relation names). This is used for pretty printing
291         debugging output (a list of choices), so any internal-only field names
292         are not included.
293         """
294         try:
295             cache = self._name_map
296         except AttributeError:
297             cache = self.init_name_map()
298         names = cache.keys()
299         names.sort()
300         # Internal-only names end with "+" (symmetrical m2m related names being
301         # the main example). Trim them.
302         return [val for val in names if not val.endswith('+')]
303
304     def init_name_map(self):
305         """
306         Initialises the field name -> field object mapping.
307         """
308         cache = {}
309         # We intentionally handle related m2m objects first so that symmetrical
310         # m2m accessor names can be overridden, if necessary.
311         for f, model in self.get_all_related_m2m_objects_with_model():
312             cache[f.field.related_query_name()] = (f, model, False, True)
313         for f, model in self.get_all_related_objects_with_model():
314             cache[f.field.related_query_name()] = (f, model, False, False)
315         for f, model in self.get_m2m_with_model():
316             cache[f.name] = (f, model, True, True)
317         for f, model in self.get_fields_with_model():
318             cache[f.name] = (f, model, True, False)
319         if self.order_with_respect_to:
320             cache['_order'] = OrderWrt(), None, True, False
321         if app_cache_ready():
322             self._name_map = cache
323         return cache
324
325     def get_add_permission(self):
326         return 'add_%s' % self.object_name.lower()
327
328     def get_change_permission(self):
329         return 'change_%s' % self.object_name.lower()
330
331     def get_delete_permission(self):
332         return 'delete_%s' % self.object_name.lower()
333
334     def get_all_related_objects(self, local_only=False):
335         try:
336             self._related_objects_cache
337         except AttributeError:
338             self._fill_related_objects_cache()
339         if local_only:
340             return [k for k, v in self._related_objects_cache.items() if not v]
341         return self._related_objects_cache.keys()
342
343     def get_all_related_objects_with_model(self):
344         """
345         Returns a list of (related-object, model) pairs. Similar to
346         get_fields_with_model().
347         """
348         try:
349             self._related_objects_cache
350         except AttributeError:
351             self._fill_related_objects_cache()
352         return self._related_objects_cache.items()
353
354     def _fill_related_objects_cache(self):
355         cache = SortedDict()
356         parent_list = self.get_parent_list()
357         for parent in self.parents:
358             for obj, model in parent._meta.get_all_related_objects_with_model():
359                 if (obj.field.creation_counter < 0 or obj.field.rel.parent_link) and obj.model not in parent_list:
360                     continue
361                 if not model:
362                     cache[obj] = parent
363                 else:
364                     cache[obj] = model
365         for klass in get_models():
366             for f in klass._meta.local_fields:
367                 if f.rel and not isinstance(f.rel.to, str) and self == f.rel.to._meta:
368                     cache[RelatedObject(f.rel.to, klass, f)] = None
369         self._related_objects_cache = cache
370
371     def get_all_related_many_to_many_objects(self, local_only=False):
372         try:
373             cache = self._related_many_to_many_cache
374         except AttributeError:
375             cache = self._fill_related_many_to_many_cache()
376         if local_only:
377             return [k for k, v in cache.items() if not v]
378         return cache.keys()
379
380     def get_all_related_m2m_objects_with_model(self):
381         """
382         Returns a list of (related-m2m-object, model) pairs. Similar to
383         get_fields_with_model().
384         """
385         try:
386             cache = self._related_many_to_many_cache
387         except AttributeError:
388             cache = self._fill_related_many_to_many_cache()
389         return cache.items()
390
391     def _fill_related_many_to_many_cache(self):
392         cache = SortedDict()
393         parent_list = self.get_parent_list()
394         for parent in self.parents:
395             for obj, model in parent._meta.get_all_related_m2m_objects_with_model():
396                 if obj.field.creation_counter < 0 and obj.model not in parent_list:
397                     continue
398                 if not model:
399                     cache[obj] = parent
400                 else:
401                     cache[obj] = model
402         for klass in get_models():
403             for f in klass._meta.local_many_to_many:
404                 if f.rel and not isinstance(f.rel.to, str) and self == f.rel.to._meta:
405                     cache[RelatedObject(f.rel.to, klass, f)] = None
406         if app_cache_ready():
407             self._related_many_to_many_cache = cache
408         return cache
409
410     def get_base_chain(self, model):
411         """
412         Returns a list of parent classes leading to 'model' (order from closet
413         to most distant ancestor). This has to handle the case were 'model' is
414         a granparent or even more distant relation.
415         """
416         if not self.parents:
417             return
418         if model in self.parents:
419             return [model]
420         for parent in self.parents:
421             res = parent._meta.get_base_chain(model)
422             if res:
423                 res.insert(0, parent)
424                 return res
425         raise TypeError('%r is not an ancestor of this model'
426                 % model._meta.module_name)
427
428     def get_parent_list(self):
429         """
430         Returns a list of all the ancestor of this model as a list. Useful for
431         determining if something is an ancestor, regardless of lineage.
432         """
433         result = set()
434         for parent in self.parents:
435             result.add(parent)
436             result.update(parent._meta.get_parent_list())
437         return result
438
439     def get_ordered_objects(self):
440         "Returns a list of Options objects that are ordered with respect to this object."
441         if not hasattr(self, '_ordered_objects'):
442             objects = []
443             # TODO
444             #for klass in get_models(get_app(self.app_label)):
445             #    opts = klass._meta
446             #    if opts.order_with_respect_to and opts.order_with_respect_to.rel \
447             #        and self == opts.order_with_respect_to.rel.to._meta:
448             #        objects.append(opts)
449             self._ordered_objects = objects
450         return self._ordered_objects
Note: See TracBrowser for help on using the browser.