app/django/db/models/options.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
     9 from django.db.models.related import RelatedObject
     9 from django.db.models.related import RelatedObject
    10 from django.db.models.fields.related import ManyToManyRel
    10 from django.db.models.fields.related import ManyToManyRel
    11 from django.db.models.fields import AutoField, FieldDoesNotExist
    11 from django.db.models.fields import AutoField, FieldDoesNotExist
    12 from django.db.models.fields.proxy import OrderWrt
    12 from django.db.models.fields.proxy import OrderWrt
    13 from django.db.models.loading import get_models, app_cache_ready
    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
    14 from django.utils.translation import activate, deactivate_all, get_language, string_concat
    16 from django.utils.encoding import force_unicode, smart_str
    15 from django.utils.encoding import force_unicode, smart_str
    17 from django.utils.datastructures import SortedDict
    16 from django.utils.datastructures import SortedDict
    18 
    17 
    19 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
    18 # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
    23                  'unique_together', 'permissions', 'get_latest_by',
    22                  'unique_together', 'permissions', 'get_latest_by',
    24                  'order_with_respect_to', 'app_label', 'db_tablespace',
    23                  'order_with_respect_to', 'app_label', 'db_tablespace',
    25                  'abstract')
    24                  'abstract')
    26 
    25 
    27 class Options(object):
    26 class Options(object):
    28     def __init__(self, meta):
    27     def __init__(self, meta, app_label=None):
    29         self.local_fields, self.local_many_to_many = [], []
    28         self.local_fields, self.local_many_to_many = [], []
       
    29         self.virtual_fields = []
    30         self.module_name, self.verbose_name = None, None
    30         self.module_name, self.verbose_name = None, None
    31         self.verbose_name_plural = None
    31         self.verbose_name_plural = None
    32         self.db_table = ''
    32         self.db_table = ''
    33         self.ordering = []
    33         self.ordering = []
    34         self.unique_together =  []
    34         self.unique_together =  []
    35         self.permissions =  []
    35         self.permissions =  []
    36         self.object_name, self.app_label = None, None
    36         self.object_name, self.app_label = None, app_label
    37         self.get_latest_by = None
    37         self.get_latest_by = None
    38         self.order_with_respect_to = None
    38         self.order_with_respect_to = None
    39         self.db_tablespace = settings.DEFAULT_TABLESPACE
    39         self.db_tablespace = settings.DEFAULT_TABLESPACE
    40         self.admin = None
    40         self.admin = None
    41         self.meta = meta
    41         self.meta = meta
    42         self.pk = None
    42         self.pk = None
    43         self.has_auto_field, self.auto_field = False, None
    43         self.has_auto_field, self.auto_field = False, None
    44         self.one_to_one_field = None
    44         self.one_to_one_field = None
    45         self.abstract = False
    45         self.abstract = False
    46         self.parents = SortedDict()
    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 = []
    47 
    51 
    48     def contribute_to_class(self, cls, name):
    52     def contribute_to_class(self, cls, name):
       
    53         from django.db import connection
       
    54         from django.db.backends.util import truncate_name
       
    55 
    49         cls._meta = self
    56         cls._meta = self
    50         self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
    57         self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
    51         # First, construct the default values for these options.
    58         # First, construct the default values for these options.
    52         self.object_name = cls.__name__
    59         self.object_name = cls.__name__
    53         self.module_name = self.object_name.lower()
    60         self.module_name = self.object_name.lower()
    54         self.verbose_name = get_verbose_name(self.object_name)
    61         self.verbose_name = get_verbose_name(self.object_name)
    55 
    62 
    56         # Next, apply any overridden values from 'class Meta'.
    63         # Next, apply any overridden values from 'class Meta'.
    57         if self.meta:
    64         if self.meta:
    58             meta_attrs = self.meta.__dict__.copy()
    65             meta_attrs = self.meta.__dict__.copy()
    59             del meta_attrs['__module__']
    66             for name in self.meta.__dict__:
    60             del meta_attrs['__doc__']
    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]
    61             for attr_name in DEFAULT_NAMES:
    72             for attr_name in DEFAULT_NAMES:
    62                 if attr_name in meta_attrs:
    73                 if attr_name in meta_attrs:
    63                     setattr(self, attr_name, meta_attrs.pop(attr_name))
    74                     setattr(self, attr_name, meta_attrs.pop(attr_name))
    64                 elif hasattr(self.meta, attr_name):
    75                 elif hasattr(self.meta, attr_name):
    65                     setattr(self, attr_name, getattr(self.meta, attr_name))
    76                     setattr(self, attr_name, getattr(self.meta, attr_name))
    81                 raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
    92                 raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
    82         else:
    93         else:
    83             self.verbose_name_plural = string_concat(self.verbose_name, 's')
    94             self.verbose_name_plural = string_concat(self.verbose_name, 's')
    84         del self.meta
    95         del self.meta
    85 
    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 
    86     def _prepare(self, model):
   103     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:
   104         if self.order_with_respect_to:
    90             self.order_with_respect_to = self.get_field(self.order_with_respect_to)
   105             self.order_with_respect_to = self.get_field(self.order_with_respect_to)
    91             self.ordering = ('_order',)
   106             self.ordering = ('_order',)
    92         else:
   107         else:
    93             self.order_with_respect_to = None
   108             self.order_with_respect_to = None
    96             if self.parents:
   111             if self.parents:
    97                 # Promote the first parent link in lieu of adding yet another
   112                 # Promote the first parent link in lieu of adding yet another
    98                 # field.
   113                 # field.
    99                 field = self.parents.value_for_index(0)
   114                 field = self.parents.value_for_index(0)
   100                 field.primary_key = True
   115                 field.primary_key = True
   101                 self.pk = field
   116                 self.setup_pk(field)
   102             else:
   117             else:
   103                 auto = AutoField(verbose_name='ID', primary_key=True,
   118                 auto = AutoField(verbose_name='ID', primary_key=True,
   104                         auto_created=True)
   119                         auto_created=True)
   105                 model.add_to_class('id', auto)
   120                 model.add_to_class('id', auto)
   106 
   121 
   107         # If the db_table wasn't provided, use the app_label + module_name.
   122         # Determine any sets of fields that are pointing to the same targets
   108         if not self.db_table:
   123         # (e.g. two ForeignKeys to the same remote model). The query
   109             self.db_table = "%s_%s" % (self.app_label, self.module_name)
   124         # construction code needs to know this. At the end of this,
   110             self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
   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]))
   111 
   139 
   112     def add_field(self, field):
   140     def add_field(self, field):
   113         # Insert the given field in the order in which it was created, using
   141         # Insert the given field in the order in which it was created, using
   114         # the "creation_counter" attribute of the field.
   142         # the "creation_counter" attribute of the field.
   115         # Move many-to-many related fields from self.fields into
   143         # Move many-to-many related fields from self.fields into
   126                 del self._field_name_cache
   154                 del self._field_name_cache
   127 
   155 
   128         if hasattr(self, '_name_map'):
   156         if hasattr(self, '_name_map'):
   129             del self._name_map
   157             del self._name_map
   130 
   158 
       
   159     def add_virtual_field(self, field):
       
   160         self.virtual_fields.append(field)
       
   161 
   131     def setup_pk(self, field):
   162     def setup_pk(self, field):
   132         if not self.pk and field.primary_key:
   163         if not self.pk and field.primary_key:
   133             self.pk = field
   164             self.pk = field
   134             field.serialize = False
   165             field.serialize = False
   135 
   166 
   254                     % (self.object_name, name))
   285                     % (self.object_name, name))
   255 
   286 
   256     def get_all_field_names(self):
   287     def get_all_field_names(self):
   257         """
   288         """
   258         Returns a list of all field names that are possible for this model
   289         Returns a list of all field names that are possible for this model
   259         (including reverse relation names).
   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.
   260         """
   293         """
   261         try:
   294         try:
   262             cache = self._name_map
   295             cache = self._name_map
   263         except AttributeError:
   296         except AttributeError:
   264             cache = self.init_name_map()
   297             cache = self.init_name_map()
   265         names = cache.keys()
   298         names = cache.keys()
   266         names.sort()
   299         names.sort()
   267         return names
   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('+')]
   268 
   303 
   269     def init_name_map(self):
   304     def init_name_map(self):
   270         """
   305         """
   271         Initialises the field name -> field object mapping.
   306         Initialises the field name -> field object mapping.
   272         """
   307         """
   273         cache = dict([(f.name, (f, m, True, False)) for f, m in
   308         cache = {}
   274                 self.get_fields_with_model()])
   309         # We intentionally handle related m2m objects first so that symmetrical
   275         for f, model in self.get_m2m_with_model():
   310         # m2m accessor names can be overridden, if necessary.
   276             cache[f.name] = (f, model, True, True)
       
   277         for f, model in self.get_all_related_m2m_objects_with_model():
   311         for f, model in self.get_all_related_m2m_objects_with_model():
   278             cache[f.field.related_query_name()] = (f, model, False, True)
   312             cache[f.field.related_query_name()] = (f, model, False, True)
   279         for f, model in self.get_all_related_objects_with_model():
   313         for f, model in self.get_all_related_objects_with_model():
   280             cache[f.field.related_query_name()] = (f, model, False, False)
   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)
   281         if self.order_with_respect_to:
   319         if self.order_with_respect_to:
   282             cache['_order'] = OrderWrt(), None, True, False
   320             cache['_order'] = OrderWrt(), None, True, False
   283         if app_cache_ready():
   321         if app_cache_ready():
   284             self._name_map = cache
   322             self._name_map = cache
   285         return cache
   323         return cache
   367                     cache[RelatedObject(f.rel.to, klass, f)] = None
   405                     cache[RelatedObject(f.rel.to, klass, f)] = None
   368         if app_cache_ready():
   406         if app_cache_ready():
   369             self._related_many_to_many_cache = cache
   407             self._related_many_to_many_cache = cache
   370         return cache
   408         return cache
   371 
   409 
   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)]
       
   376 
       
   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)]
       
   381 
       
   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
       
   393 
       
   394     def get_base_chain(self, model):
   410     def get_base_chain(self, model):
   395         """
   411         """
   396         Returns a list of parent classes leading to 'model' (order from closet
   412         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
   413         to most distant ancestor). This has to handle the case were 'model' is
   398         a granparent or even more distant relation.
   414         a granparent or even more distant relation.
   430             #    if opts.order_with_respect_to and opts.order_with_respect_to.rel \
   446             #    if opts.order_with_respect_to and opts.order_with_respect_to.rel \
   431             #        and self == opts.order_with_respect_to.rel.to._meta:
   447             #        and self == opts.order_with_respect_to.rel.to._meta:
   432             #        objects.append(opts)
   448             #        objects.append(opts)
   433             self._ordered_objects = objects
   449             self._ordered_objects = objects
   434         return self._ordered_objects
   450         return self._ordered_objects
   435 
       
   436     def has_field_type(self, field_type, follow=None):
       
   437         """
       
   438         Returns True if this object's admin form has at least one of the given
       
   439         field_type (e.g. FileField).
       
   440         """
       
   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]
       
   460 
       
   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()
       
   477 
       
   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
       
   492 
       
   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
       
   497 
       
   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
       
   504 
       
   505     def __repr__(self):
       
   506         return "FieldSet: (%s, %s)" % (self.name, self.field_lines)
       
   507 
       
   508     def bind(self, field_mapping, original, bound_field_set_class):
       
   509         return bound_field_set_class(self, field_mapping, original)
       
   510 
       
   511     def __iter__(self):
       
   512         for field_line in self.field_lines:
       
   513             yield field_line
       
   514 
       
   515     def __len__(self):
       
   516         return len(self.field_lines)
       
   517 
       
   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]
       
   524 
       
   525     def bind(self, field_mapping, original, bound_field_line_class):
       
   526         return bound_field_line_class(self, field_mapping, original)
       
   527 
       
   528     def __iter__(self):
       
   529         for field in self.fields:
       
   530             yield field
       
   531 
       
   532     def __len__(self):
       
   533         return len(self.fields)