mirror of
https://github.com/django/django.git
synced 2025-06-09 05:29:13 +00:00
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1741 bcc190cf-cafb-0310-a4f2-bffc1f526a37
234 lines
9.4 KiB
Python
234 lines
9.4 KiB
Python
from django.db.models.related import RelatedObject
|
|
from django.db.models.fields.related import OneToOne, ManyToMany
|
|
from django.db.models.fields import AutoField
|
|
from django.db.models.loading import get_installed_model_modules
|
|
from django.db.models.query import orderlist2sql
|
|
from django.db.models.exceptions import FieldDoesNotExist
|
|
from bisect import bisect
|
|
import re
|
|
|
|
# Calculate the module_name using a poor-man's pluralization.
|
|
get_module_name = lambda class_name: class_name.lower() + 's'
|
|
|
|
# Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
|
|
get_verbose_name = lambda class_name: re.sub('([A-Z])', ' \\1', class_name).lower().strip()
|
|
|
|
DEFAULT_NAMES = ('module_name', 'verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
|
|
'unique_together', 'admin','where_constraints', 'exceptions', 'permissions',
|
|
'get_latest_by','order_with_respect_to', 'module_constants')
|
|
|
|
class Options:
|
|
def __init__(self, meta):
|
|
self.fields, self.many_to_many = [], []
|
|
self.module_name, self.verbose_name = None, None
|
|
self.verbose_name_plural = None
|
|
self.db_table = ''
|
|
self.ordering = []
|
|
self.unique_together = []
|
|
self.where_constraints = []
|
|
self.exceptions = []
|
|
self.permissions = []
|
|
self.object_name, self.app_label = None, None
|
|
self.get_latest_by = None
|
|
self.order_with_respect_to = None
|
|
self.module_constants = {}
|
|
self.admin = None
|
|
self.meta = meta
|
|
|
|
def merge_meta(self):
|
|
meta_attrs = self.meta.__dict__
|
|
del meta_attrs['__module__']
|
|
del meta_attrs['__doc__']
|
|
for attr_name in DEFAULT_NAMES:
|
|
setattr(self, attr_name, meta_attrs.pop(attr_name, getattr(self, attr_name)))
|
|
if meta_attrs != {}:
|
|
raise TypeError, "'class META' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
|
|
|
|
def contribute_to_class(self, cls, name):
|
|
# TODO: Remove this self.model reference. This is a circular reference.
|
|
self.model = cls
|
|
cls._meta = self
|
|
self.object_name = cls.__name__
|
|
self.module_name = get_module_name(self.object_name )
|
|
# If the verbose_name wasn't given, use the class name,
|
|
# converted from "InitialCaps" to "lowercase with spaces".
|
|
self.verbose_name = get_verbose_name(self.object_name)
|
|
self.verbose_name_plural = self.verbose_name + 's'
|
|
if self.meta:
|
|
self.merge_meta()
|
|
del self.meta
|
|
|
|
def _prepare(self):
|
|
if self.order_with_respect_to:
|
|
self.order_with_respect_to = self.get_field(self.order_with_respect_to)
|
|
self.ordering = ('_order',)
|
|
else:
|
|
self.order_with_respect_to = None
|
|
|
|
# Calculate one_to_one_field.
|
|
self.one_to_one_field = None
|
|
for f in self.fields:
|
|
if isinstance(f.rel, OneToOne):
|
|
self.one_to_one_field = f
|
|
break
|
|
# Cache the primary-key field.
|
|
self.pk = None
|
|
for f in self.fields:
|
|
if f.primary_key:
|
|
self.pk = f
|
|
break
|
|
# If a primary_key field hasn't been specified, add an
|
|
# auto-incrementing primary-key ID field automatically.
|
|
if self.pk is None:
|
|
auto = AutoField(verbose_name='ID', primary_key=True)
|
|
auto.creation_counter = -1
|
|
self.model.add_to_class('id', auto)
|
|
self.pk = self.fields[0]
|
|
# Cache whether this has an AutoField.
|
|
self.has_auto_field = False
|
|
for f in self.fields:
|
|
is_auto = isinstance(f, AutoField)
|
|
if is_auto and self.has_auto_field:
|
|
raise AssertionError, "A model can't have more than one AutoField."
|
|
elif is_auto:
|
|
self.has_auto_field = True
|
|
#HACK
|
|
self.limit_choices_to = {}
|
|
|
|
# If the db_table wasn't provided, use the app_label + module_name.
|
|
if not self.db_table:
|
|
self.db_table = "%s_%s" % (self.app_label, self.module_name)
|
|
|
|
def add_field(self, field):
|
|
# Insert the given field in the order in which it was created, using
|
|
# the "creation_counter" attribute of the field.
|
|
# Move many-to-many related fields from self.fields into self.many_to_many.
|
|
if field.rel and isinstance(field.rel, ManyToMany):
|
|
self.many_to_many.insert(bisect(self.many_to_many, field), field)
|
|
else:
|
|
self.fields.insert(bisect(self.fields, field), field)
|
|
|
|
def __repr__(self):
|
|
return '<Options for %s>' % self.module_name
|
|
|
|
# def get_model_module(self):
|
|
# return get_module(self.app_label, self.module_name)
|
|
|
|
def get_content_type_id(self):
|
|
"Returns the content-type ID for this object type."
|
|
if not hasattr(self, '_content_type_id'):
|
|
from django.models.core import ContentType
|
|
self._content_type_id = ContentType.objects.get_object(
|
|
python_module_name__exact=self.module_name,
|
|
package__label__exact=self.app_label).id
|
|
return self._content_type_id
|
|
|
|
def get_field(self, name, many_to_many=True):
|
|
"""
|
|
Returns the requested field by name. Raises FieldDoesNotExist on error.
|
|
"""
|
|
to_search = many_to_many and (self.fields + self.many_to_many) or self.fields
|
|
for f in to_search:
|
|
if f.name == name:
|
|
return f
|
|
raise FieldDoesNotExist, "name=%s" % name
|
|
|
|
def get_order_sql(self, table_prefix=''):
|
|
"Returns the full 'ORDER BY' clause for this object, according to self.ordering."
|
|
if not self.ordering: return ''
|
|
pre = table_prefix and (table_prefix + '.') or ''
|
|
return 'ORDER BY ' + orderlist2sql(self.ordering, self, pre)
|
|
|
|
def get_add_permission(self):
|
|
return 'add_%s' % self.object_name.lower()
|
|
|
|
def get_change_permission(self):
|
|
return 'change_%s' % self.object_name.lower()
|
|
|
|
def get_delete_permission(self):
|
|
return 'delete_%s' % self.object_name.lower()
|
|
|
|
def get_all_related_objects(self):
|
|
try: # Try the cache first.
|
|
return self._all_related_objects
|
|
except AttributeError:
|
|
module_list = get_installed_model_modules()
|
|
rel_objs = []
|
|
for mod in module_list:
|
|
for klass in mod._MODELS:
|
|
for f in klass._meta.fields:
|
|
if f.rel and self == f.rel.to._meta:
|
|
rel_objs.append(RelatedObject(self, klass, f))
|
|
self._all_related_objects = rel_objs
|
|
return rel_objs
|
|
|
|
def get_followed_related_objects(self, follow=None):
|
|
if follow == None:
|
|
follow = self.get_follow()
|
|
return [f for f in self.get_all_related_objects() if follow.get(f.name, None)]
|
|
|
|
def get_data_holders(self, follow=None):
|
|
if follow == None:
|
|
follow = self.get_follow()
|
|
return [f for f in self.fields + self.many_to_many + self.get_all_related_objects() if follow.get(f.name, None)]
|
|
|
|
def get_follow(self, override=None):
|
|
follow = {}
|
|
for f in self.fields + self.many_to_many + self.get_all_related_objects():
|
|
if override and override.has_key(f.name):
|
|
child_override = override[f.name]
|
|
else:
|
|
child_override = None
|
|
fol = f.get_follow(child_override)
|
|
if fol:
|
|
follow[f.name] = fol
|
|
return follow
|
|
|
|
def get_all_related_many_to_many_objects(self):
|
|
module_list = get_installed_model_modules()
|
|
rel_objs = []
|
|
for mod in module_list:
|
|
for klass in mod._MODELS:
|
|
for f in klass._meta.many_to_many:
|
|
if f.rel and self == f.rel.to._meta:
|
|
rel_objs.append(RelatedObject(self, klass, f))
|
|
return rel_objs
|
|
|
|
def get_ordered_objects(self):
|
|
"Returns a list of Options objects that are ordered with respect to this object."
|
|
if not hasattr(self, '_ordered_objects'):
|
|
objects = []
|
|
#HACK
|
|
#for klass in get_app(self.app_label)._MODELS:
|
|
# opts = klass._meta
|
|
# if opts.order_with_respect_to and opts.order_with_respect_to.rel \
|
|
# and self == opts.order_with_respect_to.rel.to._meta:
|
|
# objects.append(opts)
|
|
self._ordered_objects = objects
|
|
return self._ordered_objects
|
|
|
|
def has_field_type(self, field_type, follow=None):
|
|
"""
|
|
Returns True if this object's admin form has at least one of the given
|
|
field_type (e.g. FileField).
|
|
"""
|
|
# TODO: follow
|
|
if not hasattr(self, '_field_types'):
|
|
self._field_types = {}
|
|
if not self._field_types.has_key(field_type):
|
|
try:
|
|
# First check self.fields.
|
|
for f in self.fields:
|
|
if isinstance(f, field_type):
|
|
raise StopIteration
|
|
# Failing that, check related fields.
|
|
for related in self.get_followed_related_objects(follow):
|
|
for f in related.opts.fields:
|
|
if isinstance(f, field_type):
|
|
raise StopIteration
|
|
except StopIteration:
|
|
self._field_types[field_type] = True
|
|
else:
|
|
self._field_types[field_type] = False
|
|
return self._field_types[field_type]
|