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