Django

Code

Ticket #87: __init__.2.py

File __init__.2.py, 77.0 kB (added by Jason Huggins, 3 years ago)

django\core\meta\init.py

Line 
1 from django.conf import settings
2 from django.core import formfields, validators
3 from django.core import db
4 from django.core.exceptions import ObjectDoesNotExist
5 from django.core.meta.fields import *
6 from django.utils.functional import curry
7 from django.utils.text import capfirst
8 import copy, datetime, os, re, sys, types
9
10 # Admin stages.
11 ADD, CHANGE, BOTH = 1, 2, 3
12
13 # Size of each "chunk" for get_iterator calls.
14 # Larger values are slightly faster at the expense of more storage space.
15 GET_ITERATOR_CHUNK_SIZE = 100
16
17 # Prefix (in Python path style) to location of models.
18 MODEL_PREFIX = 'django.models'
19
20 # Methods on models with the following prefix will be removed and
21 # converted to module-level functions.
22 MODEL_FUNCTIONS_PREFIX = '_module_'
23
24 # Methods on models with the following prefix will be removed and
25 # converted to manipulator methods.
26 MANIPULATOR_FUNCTIONS_PREFIX = '_manipulator_'
27
28 LOOKUP_SEPARATOR = '__'
29
30 ####################
31 # HELPER FUNCTIONS #
32 ####################
33
34 # Django currently supports two forms of ordering.
35 # Form 1 (deprecated) example:
36 #     order_by=(('pub_date', 'DESC'), ('headline', 'ASC'), (None, 'RANDOM'))
37 # Form 2 (new-style) example:
38 #     order_by=('-pub_date', 'headline', '?')
39 # Form 1 is deprecated and will no longer be supported for Django's first
40 # official release. The following code converts from Form 1 to Form 2.
41
42 LEGACY_ORDERING_MAPPING = {'ASC': '_', 'DESC': '-_', 'RANDOM': '?'}
43
44 def handle_legacy_orderlist(order_list):
45     if not order_list or isinstance(order_list[0], basestring):
46         return order_list
47     else:
48         import warnings
49         new_order_list = [LEGACY_ORDERING_MAPPING[j.upper()].replace('_', str(i)) for i, j in order_list]
50         warnings.warn("%r ordering syntax is deprecated. Use %r instead." % (order_list, new_order_list), DeprecationWarning)
51         return new_order_list
52
53 def orderlist2sql(order_list, prefix=''):
54     output = []
55     for f in handle_legacy_orderlist(order_list):
56         if f.startswith('-'):
57             output.append('%s%s DESC' % (prefix, f[1:]))
58         elif f == '?':
59             output.append('RANDOM()')
60         else:
61             output.append('%s%s ASC' % (prefix, f))
62     return ', '.join(output)
63
64 def get_module(app_label, module_name):
65     return __import__('%s.%s.%s' % (MODEL_PREFIX, app_label, module_name), '', '', [''])
66
67 def get_app(app_label):
68     return __import__('%s.%s' % (MODEL_PREFIX, app_label), '', '', [''])
69
70 _installed_models_cache = None
71 def get_installed_models():
72     """
73     Returns a list of installed "models" packages, such as foo.models,
74     ellington.news.models, etc. This does NOT include django.models.
75     """
76     global _installed_models_cache
77     if _installed_models_cache is not None:
78         return _installed_models_cache
79     _installed_models_cache = []
80     for a in settings.INSTALLED_APPS:
81         try:
82             _installed_models_cache.append(__import__(a + '.models', '', '', ['']))
83         except ImportError:
84             pass
85     return _installed_models_cache
86
87 _installed_modules_cache = None
88 def get_installed_model_modules(core_models=None):
89     """
90     Returns a list of installed models, such as django.models.core,
91     ellington.news.models.news, foo.models.bar, etc.
92     """
93     global _installed_modules_cache
94     if _installed_modules_cache is not None:
95         return _installed_modules_cache
96     _installed_modules_cache = []
97
98     # django.models is a special case.
99     for submodule in (core_models or []):
100         _installed_modules_cache.append(__import__('django.models.%s' % submodule, '', '', ['']))
101     for m in get_installed_models():
102         for submodule in getattr(m, '__all__', []):
103             mod = __import__('django.models.%s' % submodule, '', '', [''])
104             try:
105                 mod._MODELS
106             except AttributeError:
107                 pass # Skip model modules that don't actually have models in them.
108             else:
109                 _installed_modules_cache.append(mod)
110     return _installed_modules_cache
111
112 class LazyDate:
113     """
114     Use in limit_choices_to to compare the field to dates calculated at run time
115     instead of when the model is loaded.  For example::
116
117         ... limit_choices_to = {'date__gt' : meta.LazyDate(days=-3)} ...
118
119     which will limit the choices to dates greater than three days ago.
120     """
121     def __init__(self, **kwargs):
122         self.delta = datetime.timedelta(**kwargs)
123
124     def __str__(self):
125         return str(self.__get_value__())
126
127     def __repr__(self):
128         return "<LazyDate: %s>" % self.delta
129
130     def __get_value__(self):
131         return datetime.datetime.now() + self.delta
132
133 ################
134 # MAIN CLASSES #
135 ################
136
137 class FieldDoesNotExist(Exception):
138     pass
139
140 class BadKeywordArguments(Exception):
141     pass
142
143 class Options:
144     def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='',
145         fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False,
146         where_constraints=None, object_name=None, app_label=None,
147         exceptions=None, permissions=None, get_latest_by=None,
148         order_with_respect_to=None, module_constants=None):
149
150         # Save the original function args, for use by copy(). Note that we're
151         # NOT using copy.deepcopy(), because that would create a new copy of
152         # everything in memory, and it's better to conserve memory. Of course,
153         # this comes with the important gotcha that changing any attribute of
154         # this object will change its value in self._orig_init_args, so we
155         # need to be careful not to do that. In practice, we can pull this off
156         # because Options are generally read-only objects, and __init__() is
157         # the only place where its attributes are manipulated.
158
159         # locals() is used purely for convenience, so we don't have to do
160         # something verbose like this:
161         #    self._orig_init_args = {
162         #       'module_name': module_name,
163         #       'verbose_name': verbose_name,
164         #       ...
165         #    }
166         self._orig_init_args = locals()
167         del self._orig_init_args['self'] # because we don't care about it.
168
169         # Move many-to-many related fields from self.fields into self.many_to_many.
170         self.fields, self.many_to_many = [], []
171         for field in (fields or []):
172             if field.rel and isinstance(field.rel, ManyToMany):
173                 self.many_to_many.append(field)
174             else:
175                 self.fields.append(field)
176         self.module_name, self.verbose_name = module_name, verbose_name
177         self.verbose_name_plural = verbose_name_plural or verbose_name + 's'
178         self.db_table, self.has_related_links = db_table, has_related_links
179         self.ordering = ordering or []
180         self.unique_together = unique_together or []
181         self.where_constraints = where_constraints or []
182         self.exceptions = exceptions or []
183         self.permissions = permissions or []
184         self.object_name, self.app_label = object_name, app_label
185         self.get_latest_by = get_latest_by
186         if order_with_respect_to:
187             self.order_with_respect_to = self.get_field(order_with_respect_to)
188             self.ordering = ('_order',)
189         else:
190             self.order_with_respect_to = None
191         self.module_constants = module_constants or {}
192         self.admin = admin
193
194         # Calculate one_to_one_field.
195         self.one_to_one_field = None
196         for f in self.fields:
197             if isinstance(f.rel, OneToOne):
198                 self.one_to_one_field = f
199                 break
200         # Cache the primary-key field.
201         self.pk = None
202         for f in self.fields:
203             if f.primary_key:
204                 self.pk = f
205                 break
206         # If a primary_key field hasn't been specified, add an
207         # auto-incrementing primary-key ID field automatically.
208         if self.pk is None:
209             self.fields.insert(0, AutoField('id', 'ID', primary_key=True))
210             self.pk = self.fields[0]
211         # Cache whether this has an AutoField.
212         self.has_auto_field = False
213         for f in self.fields:
214             is_auto = isinstance(f, AutoField)
215             if is_auto and self.has_auto_field:
216                 raise AssertionError, "A model can't have more than one AutoField."
217             elif is_auto:
218                 self.has_auto_field = True
219
220     def __repr__(self):
221         return '<Options for %s>' % self.module_name
222
223     def copy(self, **kwargs):
224         args = self._orig_init_args.copy()
225         args.update(kwargs)
226         return self.__class__(**args)
227
228     def get_model_module(self):
229         return get_module(self.app_label, self.module_name)
230
231     def get_content_type_id(self):
232         "Returns the content-type ID for this object type."
233         if not hasattr(self, '_content_type_id'):
234             mod = get_module('core', 'contenttypes')
235             self._content_type_id = mod.get_object(python_module_name__exact=self.module_name, package__label__exact=self.app_label).id
236         return self._content_type_id
237
238     def get_field(self, name, many_to_many=True):
239         """
240         Returns the requested field by name. Raises FieldDoesNotExist on error.
241         """
242         to_search = many_to_many and (self.fields + self.many_to_many) or self.fields
243         for f in to_search:
244             if f.name == name:
245                 return f
246         raise FieldDoesNotExist, "name=%s" % name
247
248     def get_order_sql(self, table_prefix=''):
249         "Returns the full 'ORDER BY' clause for this object, according to self.ordering."
250         if not self.ordering: return ''
251         pre = table_prefix and (table_prefix + '.') or ''
252         return 'ORDER BY ' + orderlist2sql(self.ordering, pre)
253
254     def get_add_permission(self):
255         return 'add_%s' % self.object_name.lower()
256
257     def get_change_permission(self):
258         return 'change_%s' % self.object_name.lower()
259
260     def get_delete_permission(self):
261         return 'delete_%s' % self.object_name.lower()
262
263     def get_rel_object_method_name(self, rel_opts, rel_field):
264         # This method encapsulates the logic that decides what name to give a
265         # method that retrieves related many-to-one objects. Usually it just
266         # uses the lower-cased object_name, but if the related object is in
267         # another app, its app_label is appended.
268         #
269         # Examples:
270         #
271         #   # Normal case -- a related object in the same app.
272         #   # This method returns "choice".
273         #   Poll.get_choice_list()
274         #
275         #   # A related object in a different app.
276         #   # This method returns "lcom_bestofaward".
277         #   Place.get_lcom_bestofaward_list() # "lcom_bestofaward"
278         rel_obj_name = rel_field.rel.related_name or rel_opts.object_name.lower()
279         if self.app_label != rel_opts.app_label:
280             rel_obj_name = '%s_%s' % (rel_opts.app_label, rel_obj_name)
281         return rel_obj_name
282
283     def get_all_related_objects(self):
284         try: # Try the cache first.
285             return self._all_related_objects
286         except AttributeError:
287             module_list = get_installed_model_modules()
288             rel_objs = []
289             for mod in module_list:
290                 for klass in mod._MODELS:
291                     for f in klass._meta.fields:
292                         if f.rel and self == f.rel.to:
293                             rel_objs.append((klass._meta, f))
294             if self.has_related_links:
295                 # Manually add RelatedLink objects, which are a special case.
296                 core = get_module('relatedlinks', 'relatedlinks')
297                 # Note that the copy() is very important -- otherwise any
298                 # subsequently loaded object with related links will override this
299                 # relationship we're adding.
300                 link_field = copy.copy(core.RelatedLink._meta.get_field('object_id'))
301                 link_field.rel = ManyToOne(self.get_model_module().Klass, 'related_links', 'id',
302                     num_in_admin=3, min_num_in_admin=3, edit_inline=TABULAR,
303                     lookup_overrides={
304                         'content_type__package__label__exact': self.app_label,
305                         'content_type__python_module_name__exact': self.module_name
306                     })
307                 rel_objs.append((core.RelatedLink._meta, link_field))
308             self._all_related_objects = rel_objs
309             return rel_objs
310
311     def get_inline_related_objects(self):
312         return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline]
313
314     def get_all_related_many_to_many_objects(self):
315         module_list = get_installed_model_modules()
316         rel_objs = []
317         for mod in module_list:
318             for klass in mod._MODELS:
319                 try:
320                     for f in klass._meta.many_to_many:
321                         if f.rel and self == f.rel.to:
322                             rel_objs.append((klass._meta, f))
323                             raise StopIteration
324                 except StopIteration:
325                     continue
326         return rel_objs
327
328     def get_ordered_objects(self):
329         "Returns a list of Options objects that are ordered with respect to this object."
330         if not hasattr(self, '_ordered_objects'):
331             objects = []
332             for klass in get_app(self.app_label)._MODELS:
333                 opts = klass._meta
334                 if opts.order_with_respect_to and opts.order_with_respect_to.rel \
335                     and self == opts.order_with_respect_to.rel.to:
336                     objects.append(opts)
337             self._ordered_objects = objects
338         return self._ordered_objects
339
340     def has_field_type(self, field_type):
341         """
342         Returns True if this object's admin form has at least one of the given
343         field_type (e.g. FileField).
344         """
345         if not hasattr(self, '_field_types'):
346             self._field_types = {}
347         if not self._field_types.has_key(field_type):
348             try:
349                 # First check self.fields.
350                 for f in self.fields:
351                     if isinstance(f, field_type):
352                         raise StopIteration
353                 # Failing that, check related fields.
354                 for rel_obj, rel_field in self.get_inline_related_objects():
355                     for f in rel_obj.fields:
356                         if isinstance(f, field_type):
357                             raise StopIteration
358             except StopIteration:
359                 self._field_types[field_type] = True
360             else:
361                 self._field_types[field_type] = False
362         return self._field_types[field_type]
363
364 def _reassign_globals(function_dict, extra_globals, namespace):
365     new_functions = {}
366     for k, v in function_dict.items():
367         # Get the code object.
368         code = v.func_code
369         # Recreate the function, but give it access to extra_globals and the
370         # given namespace's globals, too.
371         new_globals = {'__builtins__': __builtins__, 'db': db.db, 'datetime': datetime}
372         new_globals.update(extra_globals.__dict__)
373         func = types.FunctionType(code, globals=new_globals, name=k, argdefs=v.func_defaults)
374         func.__dict__.update(v.__dict__)
375         setattr(namespace, k, func)
376         # For all of the custom functions that have been added so far, give
377         # them access to the new function we've just created.
378         for new_k, new_v in new_functions.items():
379             new_v.func_globals[k] = func
380         new_functions[k] = func
381
382 class ModelBase(type):
383     "Metaclass for all models"
384     def __new__(cls, name, bases, attrs):
385         # If this isn't a subclass of Model, don't do anything special.
386         if not bases:
387             return type.__new__(cls, name, bases, attrs)
388
389         # If this model is a subclass of another Model, create an Options
390         # object by first copying the base class's _meta and then updating it
391         # with the overrides from this class.
392         replaces_module = None
393         if bases[0] != Model:
394             if not attrs.has_key('fields'):
395                 attrs['fields'] = list(bases[0]._meta._orig_init_args['fields'][:])
396             if attrs.has_key('ignore_fields'):
397                 ignore_fields = attrs.pop('ignore_fields')
398                 new_fields = []
399                 for i, f in enumerate(attrs['fields']):
400                     if f.name not in ignore_fields:
401                         new_fields.append(f)
402                 attrs['fields'] = new_fields
403             if attrs.has_key('add_fields'):
404                 attrs['fields'].extend(attrs.pop('add_fields'))
405             if attrs.has_key('replaces_module'):
406                 # Set the replaces_module variable for now. We can't actually
407                 # do anything with it yet, because the module hasn't yet been
408                 # created.
409                 replaces_module = attrs.pop('replaces_module').split('.')
410             # Pass any Options overrides to the base's Options instance, and
411             # simultaneously remove them from attrs. When this is done, attrs
412             # will be a dictionary of custom methods, plus __module__.
413             meta_overrides = {}
414             for k, v in attrs.items():
415                 if not callable(v) and k != '__module__':
416                     meta_overrides[k] = attrs.pop(k)
417             opts = bases[0]._meta.copy(**meta_overrides)
418             opts.object_name = name
419             del meta_overrides
420         else:
421             opts = Options(
422                 # If the module_name wasn't given, use the class name
423                 # in lowercase, plus a trailing "s" -- a poor-man's
424                 # pluralization.
425                 module_name = attrs.pop('module_name', name.lower() + 's'),
426                 # If the verbose_name wasn't given, use the class name,
427                 # converted from InitialCaps to "lowercase with spaces".
428                 verbose_name = attrs.pop('verbose_name',
429                     re.sub('([A-Z])', ' \\1', name).lower().strip()),
430                 verbose_name_plural = attrs.pop('verbose_name_plural', ''),
431                 db_table = attrs.pop('db_table', ''),
432                 fields = attrs.pop('fields'),
433                 ordering = attrs.pop('ordering', None),
434                 unique_together = attrs.pop('unique_together', None),
435                 admin = attrs.pop('admin', None),
436                 has_related_links = attrs.pop('has_related_links', False),
437                 where_constraints = attrs.pop('where_constraints', None),
438                 object_name = name,
439                 app_label = attrs.pop('app_label', None),
440                 exceptions = attrs.pop('exceptions', None),
441                 permissions = attrs.pop('permissions', None),
442                 get_latest_by = attrs.pop('get_latest_by', None),
443                 order_with_respect_to = attrs.pop('order_with_respect_to', None),
444                 module_constants = attrs.pop('module_constants', None),
445             )
446
447         # Dynamically create the module that will contain this class and its
448         # associated helper functions.
449         if replaces_module is not None:
450             new_mod = get_module(*replaces_module)
451         else:
452             new_mod = types.ModuleType(opts.module_name)
453
454         # Collect any/all custom class methods and module functions, and move
455         # them to a temporary holding variable. We'll deal with them later.
456         if replaces_module is not None:
457             # Initialize these values to the base class' custom_methods and
458             # custom_functions.
459             custom_methods = dict([(k, v) for k, v in new_mod.Klass.__dict__.items() if hasattr(v, 'custom')])
460             custom_functions = dict([(k, v) for k, v in new_mod.__dict__.items() if hasattr(v, 'custom')])
461         else:
462             custom_methods, custom_functions = {}, {}
463         manipulator_methods = {}
464         for k, v in attrs.items():
465             if k in ('__module__', '__init__', '_overrides', '__doc__'):
466                 continue # Skip the important stuff.
467             # Give the function a function attribute "custom" to designate that
468             # it's a custom function/method.
469             v.custom = True
470             if k.startswith(MODEL_FUNCTIONS_PREFIX):
471                 custom_functions[k[len(MODEL_FUNCTIONS_PREFIX):]] = v
472             elif k.startswith(MANIPULATOR_FUNCTIONS_PREFIX):
473                 manipulator_methods[k[len(MANIPULATOR_FUNCTIONS_PREFIX):]] = v
474             else:
475                 custom_methods[k] = v
476             del attrs[k]
477
478         # Create the module-level ObjectDoesNotExist exception.
479         dne_exc_name = '%sDoesNotExist' % name
480         does_not_exist_exception = types.ClassType(dne_exc_name, (ObjectDoesNotExist,), {})
481         # Explicitly set its __module__ because it will initially (incorrectly)
482         # be set to the module the code is being executed in.
483         does_not_exist_exception.__module__ = MODEL_PREFIX + '.' + opts.module_name
484         setattr(new_mod, dne_exc_name, does_not_exist_exception)
485
486         # Create other exceptions.
487         for exception_name in opts.exceptions:
488             exc = types.ClassType(exception_name, (Exception,), {})
489             exc.__module__ = MODEL_PREFIX + '.' + opts.module_name # Set this explicitly, as above.
490             setattr(new_mod, exception_name, exc)
491
492         # Create any module-level constants, if applicable.
493         for k, v in opts.module_constants.items():
494             setattr(new_mod, k, v)
495
496         # Create the default class methods.
497         attrs['__init__'] = curry(method_init, opts)
498         attrs['__eq__'] = curry(method_eq, opts