mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	Split up of models/__init__.py complete, tests run and admin works to a degree.
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1696 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -4,7 +4,8 @@ from django.utils.text import capfirst | ||||
| from django.utils.functional import curry | ||||
| from django.contrib.admin.views.main import AdminBoundField | ||||
| from django.db.models.fields import BoundField, Field | ||||
| from django.db.models import BoundRelatedObject, TABULAR, STACKED | ||||
| from django.db.models.related import BoundRelatedObject | ||||
| from django.db.models.fields import TABULAR, STACKED | ||||
| from django.db import models | ||||
| from django.conf.settings import ADMIN_MEDIA_PREFIX | ||||
| import re | ||||
|   | ||||
| @@ -24,6 +24,8 @@ from django.utils.html import escape | ||||
| import operator | ||||
| from itertools import izip | ||||
|  | ||||
| from django.db.models.query import handle_legacy_orderlist | ||||
|  | ||||
| # The system will display a "Show all" link only if the total result count | ||||
| # is less than or equal to this setting. | ||||
| MAX_SHOW_ALL_ALLOWED = 200 | ||||
| @@ -231,7 +233,7 @@ class ChangeList(object): | ||||
|         ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name] | ||||
|  | ||||
|         # Normalize it to new-style ordering. | ||||
|         ordering = models.handle_legacy_orderlist(ordering) | ||||
|         ordering = handle_legacy_orderlist(ordering) | ||||
|  | ||||
|         if ordering[0].startswith('-'): | ||||
|             order_field, order_type = ordering[0][1:], 'desc' | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| from copy import copy | ||||
| from math import ceil | ||||
| from django.db.models import ModelBase | ||||
|  | ||||
| class InvalidPage(Exception): | ||||
|     pass | ||||
|   | ||||
| @@ -1,36 +1,24 @@ | ||||
| from django.conf import settings | ||||
| from django.core import formfields, validators | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
|  | ||||
| from django.db import backend, connection | ||||
| from django.db.models.fields import * | ||||
|  | ||||
| from django.utils.functional import curry | ||||
| from django.utils.text import capfirst | ||||
| import copy, datetime, os, re, sys, types | ||||
|  | ||||
| from django.db.models.loading import get_installed_models, get_installed_model_modules | ||||
| from django.db.models.manipulators import ManipulatorDescriptor, ModelAddManipulator, ModelChangeManipulator | ||||
| from django.db.models.query import Q, orderlist2sql  | ||||
| from django.db.models.query import Q | ||||
| from django.db.models.manager import Manager | ||||
| from django.db.models.base import Model | ||||
| from django.db.models.fields import * | ||||
|  | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.db.models.exceptions import FieldDoesNotExist, BadKeywordArguments | ||||
|  | ||||
| # Admin stages. | ||||
| ADD, CHANGE, BOTH = 1, 2, 3 | ||||
|  | ||||
|  | ||||
| # Prefix (in Python path style) to location of models. | ||||
| MODEL_PREFIX = 'django.models' | ||||
|  | ||||
| # Methods on models with the following prefix will be removed and | ||||
| # converted to module-level functions. | ||||
| MODEL_FUNCTIONS_PREFIX = '_module_' | ||||
|  | ||||
| # Methods on models with the following prefix will be removed and | ||||
| # converted to manipulator methods. | ||||
| MANIPULATOR_FUNCTIONS_PREFIX = '_manipulator_' | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| #def get_module(app_label, module_name): | ||||
| #    return __import__('%s.%s.%s' % (MODEL_PREFIX, app_label, module_name), '', '', ['']) | ||||
|  | ||||
| @@ -38,7 +26,6 @@ MANIPULATOR_FUNCTIONS_PREFIX = '_manipulator_' | ||||
| #    return __import__('%s.%s' % (MODEL_PREFIX, app_label), '', '', ['']) | ||||
|  | ||||
|  | ||||
|  | ||||
| class LazyDate: | ||||
|     """ | ||||
|     Use in limit_choices_to to compare the field to dates calculated at run time | ||||
| @@ -64,905 +51,10 @@ class LazyDate: | ||||
| # MAIN CLASSES # | ||||
| ################ | ||||
|  | ||||
| class FieldDoesNotExist(Exception): | ||||
|     pass | ||||
|  | ||||
| class BadKeywordArguments(Exception): | ||||
|     pass | ||||
|  | ||||
| class BoundRelatedObject(object): | ||||
|     def __init__(self, related_object, field_mapping, original): | ||||
|         self.relation = related_object | ||||
|         self.field_mappings = field_mapping[related_object.opts.module_name] | ||||
|  | ||||
|     def template_name(self): | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return repr(self.__dict__) | ||||
|  | ||||
| class RelatedObject(object): | ||||
|     def __init__(self, parent_opts, model, field): | ||||
|         self.parent_opts = parent_opts | ||||
|         self.model = model | ||||
|         self.opts = model._meta | ||||
|         self.field = field | ||||
|         self.edit_inline = field.rel.edit_inline | ||||
|         self.name = self.opts.module_name | ||||
|         self.var_name = self.opts.object_name.lower() | ||||
|  | ||||
|     def flatten_data(self, follow, obj=None): | ||||
|         new_data = {} | ||||
|         rel_instances = self.get_list(obj) | ||||
|         for i, rel_instance in enumerate(rel_instances): | ||||
|             instance_data = {} | ||||
|             for f in self.opts.fields + self.opts.many_to_many: | ||||
|                 # TODO: Fix for recursive manipulators. | ||||
|                 fol = follow.get(f.name, None) | ||||
|                 if fol: | ||||
|                     field_data = f.flatten_data(fol, rel_instance) | ||||
|                     for name, value in field_data.items(): | ||||
|                         instance_data['%s.%d.%s' % (self.var_name, i, name)] = value | ||||
|             new_data.update(instance_data) | ||||
|         return new_data | ||||
|  | ||||
|     def extract_data(self, data): | ||||
|         """ | ||||
|         Pull out the data meant for inline objects of this class, | ||||
|         i.e. anything starting with our module name. | ||||
|         """ | ||||
|         return data # TODO | ||||
|  | ||||
|     def get_list(self, parent_instance=None): | ||||
|         "Get the list of this type of object from an instance of the parent class." | ||||
|         if parent_instance != None: | ||||
|             func_name = 'get_%s_list' % self.get_method_name_part() | ||||
|             func = getattr(parent_instance, func_name) | ||||
|             list = func() | ||||
|  | ||||
|             count = len(list) + self.field.rel.num_extra_on_change | ||||
|             if self.field.rel.min_num_in_admin: | ||||
|                count = max(count, self.field.rel.min_num_in_admin) | ||||
|             if self.field.rel.max_num_in_admin: | ||||
|                count = min(count, self.field.rel.max_num_in_admin) | ||||
|  | ||||
|             change = count - len(list) | ||||
|             if change > 0: | ||||
|                 return list + [None for _ in range(change)] | ||||
|             if change < 0: | ||||
|                 return list[:change] | ||||
|             else: # Just right | ||||
|                 return list | ||||
|         else: | ||||
|             return [None for _ in range(self.field.rel.num_in_admin)] | ||||
|  | ||||
|  | ||||
|     def editable_fields(self): | ||||
|         "Get the fields in this class that should be edited inline." | ||||
|         return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field] | ||||
|  | ||||
|     def get_follow(self, override=None): | ||||
|         if isinstance(override, bool): | ||||
|             if override: | ||||
|                 over = {} | ||||
|             else: | ||||
|                 return None | ||||
|         else: | ||||
|             if override: | ||||
|                 over = override.copy() | ||||
|             elif self.edit_inline: | ||||
|                 over = {} | ||||
|             else: | ||||
|                 return None | ||||
|  | ||||
|         over[self.field.name] = False | ||||
|         return self.opts.get_follow(over) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "<RelatedObject: %s related to %s>" % (self.name, self.field.name) | ||||
|  | ||||
|     def get_manipulator_fields(self, opts, manipulator, change, follow): | ||||
|         # TODO: Remove core fields stuff. | ||||
|          | ||||
|         if manipulator.original_object: | ||||
|             meth_name = 'get_%s_count' % self.get_method_name_part() | ||||
|             count = getattr(manipulator.original_object, meth_name)() | ||||
|              | ||||
|             count += self.field.rel.num_extra_on_change | ||||
|             if self.field.rel.min_num_in_admin: | ||||
|                 count = max(count, self.field.rel.min_num_in_admin) | ||||
|             if self.field.rel.max_num_in_admin: | ||||
|                 count = min(count, self.field.rel.max_num_in_admin) | ||||
|         else: | ||||
|             count = self.field.rel.num_in_admin | ||||
|         fields = [] | ||||
|         for i in range(count): | ||||
|             for f in self.opts.fields + self.opts.many_to_many: | ||||
|                 if follow.get(f.name, False): | ||||
|                     prefix = '%s.%d.' % (self.var_name, i) | ||||
|                     fields.extend(f.get_manipulator_fields(self.opts, manipulator, change, name_prefix=prefix, rel=True)) | ||||
|         return fields | ||||
|  | ||||
|     def bind(self, field_mapping, original, bound_related_object_class=BoundRelatedObject): | ||||
|         return bound_related_object_class(self, field_mapping, original) | ||||
|  | ||||
|     def get_method_name_part(self): | ||||
|         # This method encapsulates the logic that decides what name to give a | ||||
|         # method that retrieves related many-to-one or many-to-many objects. | ||||
|         # Usually it just uses the lower-cased object_name, but if the related | ||||
|         # object is in another app, the related object's app_label is appended. | ||||
|         # | ||||
|         # Examples: | ||||
|         # | ||||
|         #   # Normal case -- a related object in the same app. | ||||
|         #   # This method returns "choice". | ||||
|         #   Poll.get_choice_list() | ||||
|         # | ||||
|         #   # A related object in a different app. | ||||
|         #   # This method returns "lcom_bestofaward". | ||||
|         #   Place.get_lcom_bestofaward_list() # "lcom_bestofaward" | ||||
|         rel_obj_name = self.field.rel.related_name or self.opts.object_name.lower() | ||||
|         if self.parent_opts.app_label != self.opts.app_label: | ||||
|             rel_obj_name = '%s_%s' % (self.opts.app_label, rel_obj_name) | ||||
|         return rel_obj_name | ||||
|  | ||||
|  | ||||
| class Options: | ||||
|     def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', | ||||
|         fields=None, ordering=None, unique_together=None, admin=None, | ||||
|         where_constraints=None, object_name=None, app_label=None, | ||||
|         exceptions=None, permissions=None, get_latest_by=None, | ||||
|         order_with_respect_to=None, module_constants=None): | ||||
|         # Move many-to-many related fields from self.fields into self.many_to_many. | ||||
|         self.fields, self.many_to_many = [], [] | ||||
|         for field in (fields or []): | ||||
|             if field.rel and isinstance(field.rel, ManyToMany): | ||||
|                 self.many_to_many.append(field) | ||||
|             else: | ||||
|                 self.fields.append(field) | ||||
|         self.module_name, self.verbose_name = module_name, verbose_name | ||||
|         self.verbose_name_plural = verbose_name_plural or verbose_name + 's' | ||||
|         self.db_table = db_table | ||||
|         self.ordering = ordering or [] | ||||
|         self.unique_together = unique_together or [] | ||||
|         self.where_constraints = where_constraints or [] | ||||
|         self.exceptions = exceptions or [] | ||||
|         self.permissions = permissions or [] | ||||
|         self.object_name, self.app_label = object_name, app_label | ||||
|         self.get_latest_by = get_latest_by | ||||
|         if order_with_respect_to: | ||||
|             self.order_with_respect_to = self.get_field(order_with_respect_to) | ||||
|             self.ordering = ('_order',) | ||||
|         else: | ||||
|             self.order_with_respect_to = None | ||||
|         self.module_constants = module_constants or {} | ||||
|         self.admin = admin | ||||
|  | ||||
|         # 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: | ||||
|             self.fields.insert(0, AutoField(name='id', verbose_name='ID', primary_key=True)) | ||||
|             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 = {} | ||||
|  | ||||
|  | ||||
|     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'): | ||||
|             import django.models.core | ||||
|             manager = django.models.core.ContentType.objects | ||||
|             self._content_type_id = \ | ||||
|                 manager.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] | ||||
|  | ||||
| # 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() | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class ModelBase(type): | ||||
|     "Metaclass for all models" | ||||
|     def __new__(cls, name, bases, attrs): | ||||
|         # If this isn't a subclass of Model, don't do anything special. | ||||
|         if not bases or bases == (object,): | ||||
|             return type.__new__(cls, name, bases, attrs) | ||||
|  | ||||
|         try: | ||||
|             meta_attrs = attrs.pop('META').__dict__ | ||||
|             del meta_attrs['__module__'] | ||||
|             del meta_attrs['__doc__'] | ||||
|         except KeyError: | ||||
|             meta_attrs = {} | ||||
|  | ||||
|         # Gather all attributes that are Field or Manager instances. | ||||
|         fields, managers = [], [] | ||||
|         for obj_name, obj in attrs.items(): | ||||
|             if isinstance(obj, Field): | ||||
|                 obj.set_name(obj_name) | ||||
|                 fields.append(obj) | ||||
|                 del attrs[obj_name] | ||||
|             elif isinstance(obj, Manager): | ||||
|                 managers.append((obj_name, obj)) | ||||
|                 del attrs[obj_name] | ||||
|  | ||||
|         # Sort the fields and managers in the order that they were created. The | ||||
|         # "creation_counter" is needed because metaclasses don't preserve the | ||||
|         # attribute order. | ||||
|         fields.sort(lambda x, y: x.creation_counter - y.creation_counter) | ||||
|         managers.sort(lambda x, y: x[1].creation_counter - y[1].creation_counter) | ||||
|  | ||||
|         opts = Options( | ||||
|             module_name = meta_attrs.pop('module_name', get_module_name(name)), | ||||
|             # If the verbose_name wasn't given, use the class name, | ||||
|             # converted from InitialCaps to "lowercase with spaces". | ||||
|             verbose_name = meta_attrs.pop('verbose_name', get_verbose_name(name)), | ||||
|             verbose_name_plural = meta_attrs.pop('verbose_name_plural', ''), | ||||
|             db_table = meta_attrs.pop('db_table', ''), | ||||
|             fields = fields, | ||||
|             ordering = meta_attrs.pop('ordering', None), | ||||
|             unique_together = meta_attrs.pop('unique_together', None), | ||||
|             admin = meta_attrs.pop('admin', None), | ||||
|             where_constraints = meta_attrs.pop('where_constraints', None), | ||||
|             object_name = name, | ||||
|             app_label = meta_attrs.pop('app_label', None), | ||||
|             exceptions = meta_attrs.pop('exceptions', None), | ||||
|             permissions = meta_attrs.pop('permissions', None), | ||||
|             get_latest_by = meta_attrs.pop('get_latest_by', None), | ||||
|             order_with_respect_to = meta_attrs.pop('order_with_respect_to', None), | ||||
|             module_constants = meta_attrs.pop('module_constants', None), | ||||
|         ) | ||||
|  | ||||
|         if meta_attrs != {}: | ||||
|             raise TypeError, "'class META' got invalid attribute(s): %s" % ','.join(meta_attrs.keys()) | ||||
|  | ||||
|         # Create the DoesNotExist exception. | ||||
|         attrs['DoesNotExist'] = types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}) | ||||
|  | ||||
|         # Create the class, because we need it to use in currying. | ||||
|         new_class = type.__new__(cls, name, bases, attrs) | ||||
|  | ||||
|         # Give the class a docstring -- its definition. | ||||
|         if new_class.__doc__ is None: | ||||
|             new_class.__doc__ = "%s.%s(%s)" % (opts.module_name, name, ", ".join([f.name for f in opts.fields])) | ||||
|  | ||||
|         if hasattr(new_class, 'get_absolute_url'): | ||||
|             new_class.get_absolute_url = curry(get_absolute_url, opts, new_class.get_absolute_url) | ||||
|  | ||||
|         # Figure out the app_label by looking one level up. | ||||
|         app_package = sys.modules.get(new_class.__module__) | ||||
|         app_label = app_package.__name__.replace('.models', '') | ||||
|         app_label = app_label[app_label.rfind('.')+1:] | ||||
|  | ||||
|         # Populate the _MODELS member on the module the class is in. | ||||
|         app_package.__dict__.setdefault('_MODELS', []).append(new_class) | ||||
|  | ||||
|         # Cache the app label. | ||||
|         opts.app_label = app_label | ||||
|  | ||||
|         # If the db_table wasn't provided, use the app_label + module_name. | ||||
|         if not opts.db_table: | ||||
|             opts.db_table = "%s_%s" % (app_label, opts.module_name) | ||||
|         new_class._meta = opts | ||||
|  | ||||
|         # Create the default manager, if needed. | ||||
|         # TODO: Use weakref because of possible memory leak / circular reference. | ||||
|         if managers: | ||||
|             for m_name, m in managers: | ||||
|                 m._prepare(new_class) | ||||
|                 setattr(new_class, m_name, m) | ||||
|             new_class._default_manager = managers[0][1] | ||||
|         else: | ||||
|             if hasattr(new_class, 'objects'): | ||||
|                 raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % name | ||||
|             m = Manager() | ||||
|             m._prepare(new_class) | ||||
|             new_class.objects = m | ||||
|             new_class._default_manager = m | ||||
|  | ||||
|         new_class._prepare() | ||||
|  | ||||
|         for field in fields: | ||||
|             if field.rel: | ||||
|                 other = field.rel.to | ||||
|                 if isinstance(other, basestring): | ||||
|                     print "string lookup" | ||||
|                 else: | ||||
|                     related = RelatedObject(other._meta, new_class, field) | ||||
|                     field.contribute_to_related_class(other, related) | ||||
|  | ||||
|          | ||||
|         return new_class | ||||
|  | ||||
|  | ||||
| class Model(object): | ||||
|     __metaclass__ = ModelBase | ||||
|      | ||||
|     AddManipulator = ManipulatorDescriptor('AddManipulator', ModelAddManipulator) | ||||
|     ChangeManipulator = ManipulatorDescriptor('ChangeManipulator', ModelChangeManipulator)     | ||||
|      | ||||
|     def __repr__(self): | ||||
|         return '<%s object>' % self.__class__.__name__ | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         return isinstance(other, self.__class__) and getattr(self, self._meta.pk.attname) == getattr(other, self._meta.pk.attname) | ||||
|  | ||||
|     def __ne__(self, other): | ||||
|         return not self.__eq__(other) | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         if kwargs: | ||||
|             for f in self._meta.fields: | ||||
|                 if isinstance(f.rel, ManyToOne): | ||||
|                     try: | ||||
|                         # Assume object instance was passed in. | ||||
|                         rel_obj = kwargs.pop(f.name) | ||||
|                     except KeyError: | ||||
|                         try: | ||||
|                             # Object instance wasn't passed in -- must be an ID. | ||||
|                             val = kwargs.pop(f.attname) | ||||
|                         except KeyError: | ||||
|                             val = f.get_default() | ||||
|                     else: | ||||
|                         # Object instance was passed in. | ||||
|                         # Special case: You can pass in "None" for related objects if it's allowed. | ||||
|                         if rel_obj is None and f.null: | ||||
|                             val = None | ||||
|                         else: | ||||
|                             try: | ||||
|                                 val = getattr(rel_obj, f.rel.get_related_field().attname) | ||||
|                             except AttributeError: | ||||
|                                 raise TypeError, "Invalid value: %r should be a %s instance, not a %s" % (f.name, f.rel.to, type(rel_obj)) | ||||
|                     setattr(self, f.attname, val) | ||||
|                 else: | ||||
|                     val = kwargs.pop(f.attname, f.get_default()) | ||||
|                     setattr(self, f.attname, val) | ||||
|             if kwargs: | ||||
|                 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] | ||||
|         for i, arg in enumerate(args): | ||||
|             setattr(self, self._meta.fields[i].attname, arg) | ||||
|  | ||||
|     def _prepare(cls): | ||||
|         # Creates some methods once self._meta has been populated. | ||||
|         for f in cls._meta.fields: | ||||
|             if f.choices: | ||||
|                 setattr(cls, 'get_%s_display' % f.name, curry(cls.__get_FIELD_display, field=f)) | ||||
|             if isinstance(f, DateField): | ||||
|                 if not f.null: | ||||
|                     setattr(cls, 'get_next_by_%s' % f.name, curry(cls.__get_next_or_previous_by_FIELD, field=f, is_next=True)) | ||||
|                     setattr(cls, 'get_previous_by_%s' % f.name, curry(cls.__get_next_or_previous_by_FIELD, field=f, is_next=False)) | ||||
|             elif isinstance(f, FileField): | ||||
|                 setattr(cls, 'get_%s_filename' % f.name, curry(cls.__get_FIELD_filename, field=f)) | ||||
|                 setattr(cls, 'get_%s_url' % f.name, curry(cls.__get_FIELD_url, field=f)) | ||||
|                 setattr(cls, 'get_%s_size' % f.name, curry(cls.__get_FIELD_size, field=f)) | ||||
|                 setattr(cls, 'save_%s_file' % f.name, curry(cls.__save_FIELD_file, field=f)) | ||||
|                 if isinstance(f, ImageField): | ||||
|                     # Add get_BLAH_width and get_BLAH_height methods, but only | ||||
|                     # if the image field doesn't have width and height cache | ||||
|                     # fields. | ||||
|                     if not f.width_field: | ||||
|                         setattr(cls, 'get_%s_width' % f.name, curry(cls.__get_FIELD_width, field=f)) | ||||
|                     if not f.height_field: | ||||
|                         setattr(cls, 'get_%s_height' % f.name, curry(cls.__get_FIELD_height, field=f)) | ||||
|  | ||||
|             # If the object has a relationship to itself, as designated by | ||||
|             # RECURSIVE_RELATIONSHIP_CONSTANT, create that relationship formally. | ||||
|             if f.rel and f.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT: | ||||
|                 f.rel.to = cls | ||||
|                 f.name = f.name or (f.rel.to._meta.object_name.lower() + '_' + f.rel.to._meta.pk.name) | ||||
|                 f.verbose_name = f.verbose_name or f.rel.to._meta.verbose_name | ||||
|                 f.rel.field_name = f.rel.field_name or f.rel.to._meta.pk.name | ||||
|  | ||||
|             # Add methods for many-to-one related objects. | ||||
|             # EXAMPLES: Choice.get_poll(), Story.get_dateline() | ||||
|             if isinstance(f.rel, ManyToOne): | ||||
|                 setattr(cls, 'get_%s' % f.name, curry(cls.__get_foreign_key_object, field_with_rel=f)) | ||||
|  | ||||
|         # Create the default class methods. | ||||
|         for f in cls._meta.many_to_many: | ||||
|             # Add "get_thingie" methods for many-to-many related objects. | ||||
|             # EXAMPLES: Poll.get_site_list(), Story.get_byline_list() | ||||
|             setattr(cls, 'get_%s_list' % f.rel.singular, curry(cls.__get_many_to_many_objects, field_with_rel=f)) | ||||
|  | ||||
|             # Add "set_thingie" methods for many-to-many related objects. | ||||
|             # EXAMPLES: Poll.set_sites(), Story.set_bylines() | ||||
|             setattr(cls, 'set_%s' % f.name, curry(cls.__set_many_to_many_objects, field_with_rel=f)) | ||||
|  | ||||
|         if cls._meta.order_with_respect_to: | ||||
|             cls.get_next_in_order = curry(cls.__get_next_or_previous_in_order, is_next=True) | ||||
|             cls.get_previous_in_order = curry(cls.__get_next_or_previous_in_order, is_next=False) | ||||
|  | ||||
|     _prepare = classmethod(_prepare) | ||||
|  | ||||
|     def save(self): | ||||
|         # Run any pre-save hooks. | ||||
|         if hasattr(self, '_pre_save'): | ||||
|             self._pre_save() | ||||
|  | ||||
|         non_pks = [f for f in self._meta.fields if not f.primary_key] | ||||
|         cursor = connection.cursor() | ||||
|  | ||||
|         # First, try an UPDATE. If that doesn't update anything, do an INSERT. | ||||
|         pk_val = getattr(self, self._meta.pk.attname) | ||||
|         pk_set = bool(pk_val) | ||||
|         record_exists = True | ||||
|         if pk_set: | ||||
|             # Determine whether a record with the primary key already exists. | ||||
|             cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \ | ||||
|                 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) | ||||
|             # If it does already exist, do an UPDATE. | ||||
|             if cursor.fetchone(): | ||||
|                 db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), False)) for f in non_pks] | ||||
|                 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ | ||||
|                     (backend.quote_name(self._meta.db_table), | ||||
|                     ','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]), | ||||
|                     backend.quote_name(self._meta.pk.attname)), | ||||
|                     db_values + [pk_val]) | ||||
|             else: | ||||
|                 record_exists = False | ||||
|         if not pk_set or not record_exists: | ||||
|             field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)] | ||||
|             db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), True)) for f in self._meta.fields if not isinstance(f, AutoField)] | ||||
|             # If the PK has been manually set, respect that. | ||||
|             if pk_set: | ||||
|                 field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)] | ||||
|                 db_values += [f.get_db_prep_save(f.pre_save(getattr(self, f.column), True)) for f in self._meta.fields if isinstance(f, AutoField)] | ||||
|             placeholders = ['%s'] * len(field_names) | ||||
|             if self._meta.order_with_respect_to: | ||||
|                 field_names.append(backend.quote_name('_order')) | ||||
|                 # TODO: This assumes the database supports subqueries. | ||||
|                 placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \ | ||||
|                     (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.order_with_respect_to.column))) | ||||
|                 db_values.append(getattr(self, self._meta.order_with_respect_to.attname)) | ||||
|             cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \ | ||||
|                 (backend.quote_name(self._meta.db_table), ','.join(field_names), | ||||
|                 ','.join(placeholders)), db_values) | ||||
|             if self._meta.has_auto_field and not pk_set: | ||||
|                 setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column)) | ||||
|         connection.commit() | ||||
|  | ||||
|         # Run any post-save hooks. | ||||
|         if hasattr(self, '_post_save'): | ||||
|             self._post_save() | ||||
|  | ||||
|     save.alters_data = True | ||||
|  | ||||
|     def delete(self): | ||||
|         assert getattr(self, self._meta.pk.attname) is not None, "%r can't be deleted because it doesn't have an ID." | ||||
|  | ||||
|         # Run any pre-delete hooks. | ||||
|         if hasattr(self, '_pre_delete'): | ||||
|             self._pre_delete() | ||||
|  | ||||
|         cursor = connection.cursor() | ||||
|         for related in self._meta.get_all_related_objects(): | ||||
|             rel_opts_name = related.get_method_name_part() | ||||
|             if isinstance(related.field.rel, OneToOne): | ||||
|                 try: | ||||
|                     sub_obj = getattr(self, 'get_%s' % rel_opts_name)() | ||||
|                 except ObjectDoesNotExist: | ||||
|                     pass | ||||
|                 else: | ||||
|                     sub_obj.delete() | ||||
|             else: | ||||
|                 for sub_obj in getattr(self, 'get_%s_list' % rel_opts_name)(): | ||||
|                     sub_obj.delete() | ||||
|         for related in self._meta.get_all_related_many_to_many_objects(): | ||||
|             cursor.execute("DELETE FROM %s WHERE %s=%%s" % \ | ||||
|                 (backend.quote_name(related.field.get_m2m_db_table(related.opts)), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id')), [getattr(self, self._meta.pk.attname)]) | ||||
|         for f in self._meta.many_to_many: | ||||
|             cursor.execute("DELETE FROM %s WHERE %s=%%s" % \ | ||||
|                 (backend.quote_name(f.get_m2m_db_table(self._meta)), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id')), | ||||
|                 [getattr(self, self._meta.pk.attname)]) | ||||
|  | ||||
|         cursor.execute("DELETE FROM %s WHERE %s=%%s" % \ | ||||
|             (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), | ||||
|             [getattr(self, self._meta.pk.attname)]) | ||||
|  | ||||
|         connection.commit() | ||||
|         setattr(self, self._meta.pk.attname, None) | ||||
|         for f in self._meta.fields: | ||||
|             if isinstance(f, FileField) and getattr(self, f.attname): | ||||
|                 file_name = getattr(self, 'get_%s_filename' % f.name)() | ||||
|                 # If the file exists and no other object of this type references it, | ||||
|                 # delete it from the filesystem. | ||||
|                 if os.path.exists(file_name) and not self._default_manager.get_list(**{'%s__exact' % f.name: getattr(self, f.name)}): | ||||
|                     os.remove(file_name) | ||||
|  | ||||
|         # Run any post-delete hooks. | ||||
|         if hasattr(self, '_post_delete'): | ||||
|             self._post_delete() | ||||
|  | ||||
|     delete.alters_data = True | ||||
|  | ||||
|      | ||||
|     def __get_FIELD_display(self, field): | ||||
|         value = getattr(self, field.attname) | ||||
|         return dict(field.choices).get(value, value) | ||||
|  | ||||
|     def __get_next_or_previous_by_FIELD(self, field, is_next, **kwargs): | ||||
|         op = is_next and '>' or '<' | ||||
|         kwargs.setdefault('where', []).append('(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \ | ||||
|             (backend.quote_name(field.column), op, backend.quote_name(field.column), | ||||
|             backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column), op)) | ||||
|         param = str(getattr(self, field.attname)) | ||||
|         kwargs.setdefault('params', []).extend([param, param, getattr(self, self._meta.pk.attname)]) | ||||
|         kwargs['order_by'] = [(not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name] | ||||
|         kwargs['limit'] = 1 | ||||
|         return self.__class__._default_manager.get_object(**kwargs) | ||||
|  | ||||
|     def __get_next_or_previous_in_order(self, is_next): | ||||
|         cachename = "__%s_order_cache" % is_next | ||||
|         if not hasattr(self, cachename): | ||||
|             op = is_next and '>' or '<' | ||||
|             order_field = self.order_with_respect_to | ||||
|             obj = self._default_manager.get_object(order_by=('_order',), | ||||
|                 where=['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \ | ||||
|                     (backend.quote_name('_order'), op, backend.quote_name('_order'), | ||||
|                     backend.quote_name(opts.db_table), backend.quote_name(opts.pk.column)), | ||||
|                     '%s=%%s' % backend.quote_name(order_field.column)], | ||||
|                 limit=1, | ||||
|                 params=[getattr(self, opts.pk.attname), getattr(self, order_field.attname)]) | ||||
|             setattr(self, cachename, obj) | ||||
|         return getattr(self, cachename) | ||||
|  | ||||
|     def __get_FIELD_filename(self, field): | ||||
|         return os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname)) | ||||
|  | ||||
|     def __get_FIELD_url(self, field): | ||||
|         if getattr(self, field.attname): # value is not blank | ||||
|             import urlparse | ||||
|             return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/') | ||||
|         return '' | ||||
|  | ||||
|     def __get_FIELD_size(self, field): | ||||
|         return os.path.getsize(self.__get_FIELD_filename(field)) | ||||
|  | ||||
|     def __save_FIELD_file(self, field, filename, raw_contents): | ||||
|         directory = field.get_directory_name() | ||||
|         try: # Create the date-based directory if it doesn't exist. | ||||
|             os.makedirs(os.path.join(settings.MEDIA_ROOT, directory)) | ||||
|         except OSError: # Directory probably already exists. | ||||
|             pass | ||||
|         filename = field.get_filename(filename) | ||||
|  | ||||
|         # If the filename already exists, keep adding an underscore to the name of | ||||
|         # the file until the filename doesn't exist. | ||||
|         while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)): | ||||
|             try: | ||||
|                 dot_index = filename.rindex('.') | ||||
|             except ValueError: # filename has no dot | ||||
|                 filename += '_' | ||||
|             else: | ||||
|                 filename = filename[:dot_index] + '_' + filename[dot_index:] | ||||
|  | ||||
|         # Write the file to disk. | ||||
|         setattr(self, field.attname, filename) | ||||
|  | ||||
|         full_filename = self.__get_FIELD_filename(field) | ||||
|         fp = open(full_filename, 'wb') | ||||
|         fp.write(raw_contents) | ||||
|         fp.close() | ||||
|  | ||||
|         # Save the width and/or height, if applicable. | ||||
|         if isinstance(field, ImageField) and (field.width_field or field.height_field): | ||||
|             from django.utils.images import get_image_dimensions | ||||
|             width, height = get_image_dimensions(full_filename) | ||||
|             if field.width_field: | ||||
|                 setattr(self, field.width_field, width) | ||||
|             if field.height_field: | ||||
|                 setattr(self, field.height_field, height) | ||||
|  | ||||
|         # Save the object, because it has changed. | ||||
|         self.save() | ||||
|  | ||||
|     __save_FIELD_file.alters_data = True | ||||
|  | ||||
|     def __get_FIELD_width(self, field): | ||||
|         return self.__get_image_dimensions(field)[0] | ||||
|  | ||||
|     def __get_FIELD_height(self, field): | ||||
|         return self.__get_image_dimensions(field)[1] | ||||
|  | ||||
|     def __get_image_dimensions(self, field): | ||||
|         cachename = "__%s_dimensions_cache" % field.name | ||||
|         if not hasattr(self, cachename): | ||||
|             from django.utils.images import get_image_dimensions | ||||
|             filename = self.__get_FIELD_filename(field)() | ||||
|             setattr(self, cachename, get_image_dimensions(filename)) | ||||
|         return getattr(self, cachename) | ||||
|  | ||||
|     def __get_foreign_key_object(self, field_with_rel): | ||||
|         cache_var = field_with_rel.get_cache_name() | ||||
|         if not hasattr(self, cache_var): | ||||
|             val = getattr(self, field_with_rel.attname) | ||||
|             if val is None: | ||||
|                 raise field_with_rel.rel.to.DoesNotExist | ||||
|             other_field = field_with_rel.rel.get_related_field() | ||||
|             if other_field.rel: | ||||
|                 params = {'%s__%s__exact' % (field_with_rel.rel.field_name, other_field.rel.field_name): val} | ||||
|             else: | ||||
|                 params = {'%s__exact' % field_with_rel.rel.field_name: val} | ||||
|             retrieved_obj = field_with_rel.rel.to._default_manager.get_object(**params) | ||||
|             setattr(self, cache_var, retrieved_obj) | ||||
|         return getattr(self, cache_var) | ||||
|  | ||||
|     def __get_many_to_many_objects(self, field_with_rel): | ||||
|         cache_var = '_%s_cache' % field_with_rel.name | ||||
|         if not hasattr(self, cache_var): | ||||
|             rel_opts = field_with_rel.rel.to._meta | ||||
|             sql = "SELECT %s FROM %s a, %s b WHERE a.%s = b.%s AND b.%s = %%s %s" % \ | ||||
|                 (','.join(['a.%s' % backend.quote_name(f.column) for f in rel_opts.fields]), | ||||
|                 backend.quote_name(rel_opts.db_table), | ||||
|                 backend.quote_name(field_with_rel.get_m2m_db_table(self._meta)), | ||||
|                 backend.quote_name(rel_opts.pk.column), | ||||
|                 backend.quote_name(rel_opts.object_name.lower() + '_id'), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id'), rel_opts.get_order_sql('a')) | ||||
|             cursor = connection.cursor() | ||||
|             cursor.execute(sql, [getattr(self, self._meta.pk.attname)]) | ||||
|             setattr(self, cache_var, [field_with_rel.rel.to(*row) for row in cursor.fetchall()]) | ||||
|         return getattr(self, cache_var) | ||||
|  | ||||
|     def __set_many_to_many_objects(self, id_list, field_with_rel): | ||||
|         current_ids = [obj.id for obj in self.__get_many_to_many_objects(field_with_rel)] | ||||
|         ids_to_add, ids_to_delete = dict([(i, 1) for i in id_list]), [] | ||||
|         for current_id in current_ids: | ||||
|             if current_id in id_list: | ||||
|                 del ids_to_add[current_id] | ||||
|             else: | ||||
|                 ids_to_delete.append(current_id) | ||||
|         ids_to_add = ids_to_add.keys() | ||||
|         # Now ids_to_add is a list of IDs to add, and ids_to_delete is a list of IDs to delete. | ||||
|         if not ids_to_delete and not ids_to_add: | ||||
|             return False # No change | ||||
|         rel = field_with_rel.rel.to._meta | ||||
|         m2m_table = field_with_rel.get_m2m_db_table(self._meta) | ||||
|         cursor = connection.cursor() | ||||
|         this_id = getattr(self, self._meta.pk.attname) | ||||
|         if ids_to_delete: | ||||
|             sql = "DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \ | ||||
|                 (backend.quote_name(m2m_table), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id'), | ||||
|                 backend.quote_name(rel.object_name.lower() + '_id'), ','.join(map(str, ids_to_delete))) | ||||
|             cursor.execute(sql, [this_id]) | ||||
|         if ids_to_add: | ||||
|             sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ | ||||
|                 (backend.quote_name(m2m_table), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id'), | ||||
|                 backend.quote_name(rel.object_name.lower() + '_id')) | ||||
|             cursor.executemany(sql, [(this_id, i) for i in ids_to_add]) | ||||
|         connection.commit() | ||||
|         try: | ||||
|             delattr(self, '_%s_cache' % field_with_rel.name) # clear cache, if it exists | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         return True | ||||
|  | ||||
|     __set_many_to_many_objects.alters_data = True | ||||
|  | ||||
|     def _get_related(self, method_name, rel_class, rel_field, **kwargs): | ||||
|         kwargs['%s__%s__exact' % (rel_field.name, rel_field.rel.to._meta.pk.name)] = getattr(self, rel_field.rel.get_related_field().attname) | ||||
|         kwargs.update(rel_field.rel.lookup_overrides) | ||||
|         return getattr(rel_class._default_manager, method_name)(**kwargs) | ||||
|  | ||||
|     def _add_related(self, rel_class, rel_field, *args, **kwargs): | ||||
|         init_kwargs = dict(zip([f.attname for f in rel_class._meta.fields if f != rel_field and not isinstance(f, AutoField)], args)) | ||||
|         init_kwargs.update(kwargs) | ||||
|         for f in rel_class._meta.fields: | ||||
|             if isinstance(f, AutoField): | ||||
|                 init_kwargs[f.attname] = None | ||||
|         init_kwargs[rel_field.name] = self | ||||
|         obj = rel_class(**init_kwargs) | ||||
|         obj.save() | ||||
|         return obj | ||||
|  | ||||
|     _add_related.alters_data = True | ||||
|  | ||||
|  | ||||
|     # Handles related many-to-many object retrieval. | ||||
|     # Examples: Album.get_song(), Album.get_song_list(), Album.get_song_count() | ||||
|     def _get_related_many_to_many(self, method_name, rel_class, rel_field, **kwargs): | ||||
|         kwargs['%s__%s__exact' % (rel_field.name, self._meta.pk.name)] = getattr(self, self._meta.pk.attname) | ||||
|         return getattr(rel_class._default_manager, method_name)(**kwargs) | ||||
|  | ||||
|     # Handles setting many-to-many related objects. | ||||
|     # Example: Album.set_songs() | ||||
|     def _set_related_many_to_many(self, rel_class, rel_field, id_list): | ||||
|         id_list = map(int, id_list) # normalize to integers | ||||
|         rel = rel_field.rel.to | ||||
|         m2m_table = rel_field.get_m2m_db_table(rel_opts) | ||||
|         this_id = getattr(self, self._meta.pk.attname) | ||||
|         cursor = connection.cursor() | ||||
|         cursor.execute("DELETE FROM %s WHERE %s = %%s" % \ | ||||
|             (backend.quote_name(m2m_table), | ||||
|             backend.quote_name(rel.object_name.lower() + '_id')), [this_id]) | ||||
|         sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ | ||||
|             (backend.quote_name(m2m_table), | ||||
|             backend.quote_name(rel.object_name.lower() + '_id'), | ||||
|             backend.quote_name(rel_opts.object_name.lower() + '_id')) | ||||
|         cursor.executemany(sql, [(this_id, i) for i in id_list]) | ||||
|         connection.commit() | ||||
|  | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
| ############################################ | ||||
| # HELPER FUNCTIONS (CURRIED MODEL METHODS) # | ||||
| ############################################ | ||||
|  | ||||
| # ORDERING METHODS ######################### | ||||
|  | ||||
| def method_set_order(ordered_obj, self, id_list): | ||||
|     cursor = connection.cursor() | ||||
|     # Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s" | ||||
|     sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \ | ||||
|         (backend.quote_name(ordered_obj.db_table), backend.quote_name('_order'), | ||||
|         backend.quote_name(ordered_obj.order_with_respect_to.column), | ||||
|         backend.quote_name(ordered_obj.pk.column)) | ||||
|     rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name) | ||||
|     cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)]) | ||||
|     connection.commit() | ||||
|  | ||||
| def method_get_order(ordered_obj, self): | ||||
|     cursor = connection.cursor() | ||||
|     # Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order" | ||||
|     sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \ | ||||
|         (backend.quote_name(ordered_obj.pk.column), | ||||
|         backend.quote_name(ordered_obj.db_table), | ||||
|         backend.quote_name(ordered_obj.order_with_respect_to.column), | ||||
|         backend.quote_name('_order')) | ||||
|     rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name) | ||||
|     cursor.execute(sql, [rel_val]) | ||||
|     return [r[0] for r in cursor.fetchall()] | ||||
|  | ||||
| ############################################## | ||||
| # HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) # | ||||
| ############################################## | ||||
|  | ||||
| def get_absolute_url(opts, func, self): | ||||
|     return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self) | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										589
									
								
								django/db/models/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										589
									
								
								django/db/models/base.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,589 @@ | ||||
| from django.db.models.manipulators import ManipulatorDescriptor, ModelAddManipulator, ModelChangeManipulator | ||||
| from django.db.models.fields import Field, DateField, FileField, ImageField, AutoField | ||||
| from django.db.models.fields import OneToOne, ManyToOne, ManyToMany, RECURSIVE_RELATIONSHIP_CONSTANT | ||||
| from django.db.models.related import RelatedObject | ||||
| from django.db.models.manager import Manager | ||||
| from django.db.models.query import orderlist2sql  | ||||
| from django.db.models.options import Options | ||||
| from django.db import connection, backend | ||||
|  | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| from django.utils.functional import curry | ||||
|  | ||||
| import re | ||||
| import types | ||||
| import sys | ||||
|  | ||||
|  | ||||
| # 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() | ||||
|  | ||||
|  | ||||
| class ModelBase(type): | ||||
|     "Metaclass for all models" | ||||
|     def __new__(cls, name, bases, attrs): | ||||
|         # If this isn't a subclass of Model, don't do anything special. | ||||
|         if not bases or bases == (object,): | ||||
|             return type.__new__(cls, name, bases, attrs) | ||||
|  | ||||
|         try: | ||||
|             meta_attrs = attrs.pop('META').__dict__ | ||||
|             del meta_attrs['__module__'] | ||||
|             del meta_attrs['__doc__'] | ||||
|         except KeyError: | ||||
|             meta_attrs = {} | ||||
|  | ||||
|         # Gather all attributes that are Field or Manager instances. | ||||
|         fields, managers = [], [] | ||||
|         for obj_name, obj in attrs.items(): | ||||
|             if isinstance(obj, Field): | ||||
|                 obj.set_name(obj_name) | ||||
|                 fields.append(obj) | ||||
|                 del attrs[obj_name] | ||||
|             elif isinstance(obj, Manager): | ||||
|                 managers.append((obj_name, obj)) | ||||
|                 del attrs[obj_name] | ||||
|  | ||||
|         # Sort the fields and managers in the order that they were created. The | ||||
|         # "creation_counter" is needed because metaclasses don't preserve the | ||||
|         # attribute order. | ||||
|         fields.sort(lambda x, y: x.creation_counter - y.creation_counter) | ||||
|         managers.sort(lambda x, y: x[1].creation_counter - y[1].creation_counter) | ||||
|  | ||||
|         opts = Options( | ||||
|             module_name = meta_attrs.pop('module_name', get_module_name(name)), | ||||
|             # If the verbose_name wasn't given, use the class name, | ||||
|             # converted from InitialCaps to "lowercase with spaces". | ||||
|             verbose_name = meta_attrs.pop('verbose_name', get_verbose_name(name)), | ||||
|             verbose_name_plural = meta_attrs.pop('verbose_name_plural', ''), | ||||
|             db_table = meta_attrs.pop('db_table', ''), | ||||
|             fields = fields, | ||||
|             ordering = meta_attrs.pop('ordering', None), | ||||
|             unique_together = meta_attrs.pop('unique_together', None), | ||||
|             admin = meta_attrs.pop('admin', None), | ||||
|             where_constraints = meta_attrs.pop('where_constraints', None), | ||||
|             object_name = name, | ||||
|             app_label = meta_attrs.pop('app_label', None), | ||||
|             exceptions = meta_attrs.pop('exceptions', None), | ||||
|             permissions = meta_attrs.pop('permissions', None), | ||||
|             get_latest_by = meta_attrs.pop('get_latest_by', None), | ||||
|             order_with_respect_to = meta_attrs.pop('order_with_respect_to', None), | ||||
|             module_constants = meta_attrs.pop('module_constants', None), | ||||
|         ) | ||||
|  | ||||
|         if meta_attrs != {}: | ||||
|             raise TypeError, "'class META' got invalid attribute(s): %s" % ','.join(meta_attrs.keys()) | ||||
|  | ||||
|         # Create the DoesNotExist exception. | ||||
|         attrs['DoesNotExist'] = types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {}) | ||||
|  | ||||
|         # Create the class, because we need it to use in currying. | ||||
|         new_class = type.__new__(cls, name, bases, attrs) | ||||
|  | ||||
|         # Give the class a docstring -- its definition. | ||||
|         if new_class.__doc__ is None: | ||||
|             new_class.__doc__ = "%s.%s(%s)" % (opts.module_name, name, ", ".join([f.name for f in opts.fields])) | ||||
|  | ||||
|         if hasattr(new_class, 'get_absolute_url'): | ||||
|             new_class.get_absolute_url = curry(get_absolute_url, opts, new_class.get_absolute_url) | ||||
|  | ||||
|         # Figure out the app_label by looking one level up. | ||||
|         app_package = sys.modules.get(new_class.__module__) | ||||
|         app_label = app_package.__name__.replace('.models', '') | ||||
|         app_label = app_label[app_label.rfind('.')+1:] | ||||
|  | ||||
|         # Populate the _MODELS member on the module the class is in. | ||||
|         app_package.__dict__.setdefault('_MODELS', []).append(new_class) | ||||
|  | ||||
|         # Cache the app label. | ||||
|         opts.app_label = app_label | ||||
|  | ||||
|         # If the db_table wasn't provided, use the app_label + module_name. | ||||
|         if not opts.db_table: | ||||
|             opts.db_table = "%s_%s" % (app_label, opts.module_name) | ||||
|         new_class._meta = opts | ||||
|  | ||||
|         # Create the default manager, if needed. | ||||
|         # TODO: Use weakref because of possible memory leak / circular reference. | ||||
|         if managers: | ||||
|             for m_name, m in managers: | ||||
|                 m._prepare(new_class) | ||||
|                 setattr(new_class, m_name, m) | ||||
|             new_class._default_manager = managers[0][1] | ||||
|         else: | ||||
|             if hasattr(new_class, 'objects'): | ||||
|                 raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % name | ||||
|             m = Manager() | ||||
|             m._prepare(new_class) | ||||
|             new_class.objects = m | ||||
|             new_class._default_manager = m | ||||
|  | ||||
|         new_class._prepare() | ||||
|  | ||||
|         for field in fields: | ||||
|             if field.rel: | ||||
|                 other = field.rel.to | ||||
|                 if isinstance(other, basestring): | ||||
|                     print "string lookup" | ||||
|                 else: | ||||
|                     related = RelatedObject(other._meta, new_class, field) | ||||
|                     field.contribute_to_related_class(other, related) | ||||
|  | ||||
|          | ||||
|         return new_class | ||||
|  | ||||
|  | ||||
| class Model(object): | ||||
|     __metaclass__ = ModelBase | ||||
|      | ||||
|     AddManipulator = ManipulatorDescriptor('AddManipulator', ModelAddManipulator) | ||||
|     ChangeManipulator = ManipulatorDescriptor('ChangeManipulator', ModelChangeManipulator)     | ||||
|      | ||||
|     def __repr__(self): | ||||
|         return '<%s object>' % self.__class__.__name__ | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         return isinstance(other, self.__class__) and getattr(self, self._meta.pk.attname) == getattr(other, self._meta.pk.attname) | ||||
|  | ||||
|     def __ne__(self, other): | ||||
|         return not self.__eq__(other) | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         if kwargs: | ||||
|             for f in self._meta.fields: | ||||
|                 if isinstance(f.rel, ManyToOne): | ||||
|                     try: | ||||
|                         # Assume object instance was passed in. | ||||
|                         rel_obj = kwargs.pop(f.name) | ||||
|                     except KeyError: | ||||
|                         try: | ||||
|                             # Object instance wasn't passed in -- must be an ID. | ||||
|                             val = kwargs.pop(f.attname) | ||||
|                         except KeyError: | ||||
|                             val = f.get_default() | ||||
|                     else: | ||||
|                         # Object instance was passed in. | ||||
|                         # Special case: You can pass in "None" for related objects if it's allowed. | ||||
|                         if rel_obj is None and f.null: | ||||
|                             val = None | ||||
|                         else: | ||||
|                             try: | ||||
|                                 val = getattr(rel_obj, f.rel.get_related_field().attname) | ||||
|                             except AttributeError: | ||||
|                                 raise TypeError, "Invalid value: %r should be a %s instance, not a %s" % (f.name, f.rel.to, type(rel_obj)) | ||||
|                     setattr(self, f.attname, val) | ||||
|                 else: | ||||
|                     val = kwargs.pop(f.attname, f.get_default()) | ||||
|                     setattr(self, f.attname, val) | ||||
|             if kwargs: | ||||
|                 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] | ||||
|         for i, arg in enumerate(args): | ||||
|             setattr(self, self._meta.fields[i].attname, arg) | ||||
|  | ||||
|     def _prepare(cls): | ||||
|         # Creates some methods once self._meta has been populated. | ||||
|         for f in cls._meta.fields: | ||||
|             if f.choices: | ||||
|                 setattr(cls, 'get_%s_display' % f.name, curry(cls.__get_FIELD_display, field=f)) | ||||
|             if isinstance(f, DateField): | ||||
|                 if not f.null: | ||||
|                     setattr(cls, 'get_next_by_%s' % f.name, curry(cls.__get_next_or_previous_by_FIELD, field=f, is_next=True)) | ||||
|                     setattr(cls, 'get_previous_by_%s' % f.name, curry(cls.__get_next_or_previous_by_FIELD, field=f, is_next=False)) | ||||
|             elif isinstance(f, FileField): | ||||
|                 setattr(cls, 'get_%s_filename' % f.name, curry(cls.__get_FIELD_filename, field=f)) | ||||
|                 setattr(cls, 'get_%s_url' % f.name, curry(cls.__get_FIELD_url, field=f)) | ||||
|                 setattr(cls, 'get_%s_size' % f.name, curry(cls.__get_FIELD_size, field=f)) | ||||
|                 setattr(cls, 'save_%s_file' % f.name, curry(cls.__save_FIELD_file, field=f)) | ||||
|                 if isinstance(f, ImageField): | ||||
|                     # Add get_BLAH_width and get_BLAH_height methods, but only | ||||
|                     # if the image field doesn't have width and height cache | ||||
|                     # fields. | ||||
|                     if not f.width_field: | ||||
|                         setattr(cls, 'get_%s_width' % f.name, curry(cls.__get_FIELD_width, field=f)) | ||||
|                     if not f.height_field: | ||||
|                         setattr(cls, 'get_%s_height' % f.name, curry(cls.__get_FIELD_height, field=f)) | ||||
|  | ||||
|             # If the object has a relationship to itself, as designated by | ||||
|             # RECURSIVE_RELATIONSHIP_CONSTANT, create that relationship formally. | ||||
|             if f.rel and f.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT: | ||||
|                 f.rel.to = cls | ||||
|                 f.name = f.name or (f.rel.to._meta.object_name.lower() + '_' + f.rel.to._meta.pk.name) | ||||
|                 f.verbose_name = f.verbose_name or f.rel.to._meta.verbose_name | ||||
|                 f.rel.field_name = f.rel.field_name or f.rel.to._meta.pk.name | ||||
|  | ||||
|             # Add methods for many-to-one related objects. | ||||
|             # EXAMPLES: Choice.get_poll(), Story.get_dateline() | ||||
|             if isinstance(f.rel, ManyToOne): | ||||
|                 setattr(cls, 'get_%s' % f.name, curry(cls.__get_foreign_key_object, field_with_rel=f)) | ||||
|  | ||||
|         # Create the default class methods. | ||||
|         for f in cls._meta.many_to_many: | ||||
|             # Add "get_thingie" methods for many-to-many related objects. | ||||
|             # EXAMPLES: Poll.get_site_list(), Story.get_byline_list() | ||||
|             setattr(cls, 'get_%s_list' % f.rel.singular, curry(cls.__get_many_to_many_objects, field_with_rel=f)) | ||||
|  | ||||
|             # Add "set_thingie" methods for many-to-many related objects. | ||||
|             # EXAMPLES: Poll.set_sites(), Story.set_bylines() | ||||
|             setattr(cls, 'set_%s' % f.name, curry(cls.__set_many_to_many_objects, field_with_rel=f)) | ||||
|  | ||||
|         if cls._meta.order_with_respect_to: | ||||
|             cls.get_next_in_order = curry(cls.__get_next_or_previous_in_order, is_next=True) | ||||
|             cls.get_previous_in_order = curry(cls.__get_next_or_previous_in_order, is_next=False) | ||||
|  | ||||
|     _prepare = classmethod(_prepare) | ||||
|  | ||||
|     def save(self): | ||||
|         # Run any pre-save hooks. | ||||
|         if hasattr(self, '_pre_save'): | ||||
|             self._pre_save() | ||||
|  | ||||
|         non_pks = [f for f in self._meta.fields if not f.primary_key] | ||||
|         cursor = connection.cursor() | ||||
|  | ||||
|         # First, try an UPDATE. If that doesn't update anything, do an INSERT. | ||||
|         pk_val = getattr(self, self._meta.pk.attname) | ||||
|         pk_set = bool(pk_val) | ||||
|         record_exists = True | ||||
|         if pk_set: | ||||
|             # Determine whether a record with the primary key already exists. | ||||
|             cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \ | ||||
|                 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) | ||||
|             # If it does already exist, do an UPDATE. | ||||
|             if cursor.fetchone(): | ||||
|                 db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), False)) for f in non_pks] | ||||
|                 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ | ||||
|                     (backend.quote_name(self._meta.db_table), | ||||
|                     ','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]), | ||||
|                     backend.quote_name(self._meta.pk.attname)), | ||||
|                     db_values + [pk_val]) | ||||
|             else: | ||||
|                 record_exists = False | ||||
|         if not pk_set or not record_exists: | ||||
|             field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)] | ||||
|             db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.attname), True)) for f in self._meta.fields if not isinstance(f, AutoField)] | ||||
|             # If the PK has been manually set, respect that. | ||||
|             if pk_set: | ||||
|                 field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)] | ||||
|                 db_values += [f.get_db_prep_save(f.pre_save(getattr(self, f.column), True)) for f in self._meta.fields if isinstance(f, AutoField)] | ||||
|             placeholders = ['%s'] * len(field_names) | ||||
|             if self._meta.order_with_respect_to: | ||||
|                 field_names.append(backend.quote_name('_order')) | ||||
|                 # TODO: This assumes the database supports subqueries. | ||||
|                 placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \ | ||||
|                     (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.order_with_respect_to.column))) | ||||
|                 db_values.append(getattr(self, self._meta.order_with_respect_to.attname)) | ||||
|             cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \ | ||||
|                 (backend.quote_name(self._meta.db_table), ','.join(field_names), | ||||
|                 ','.join(placeholders)), db_values) | ||||
|             if self._meta.has_auto_field and not pk_set: | ||||
|                 setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column)) | ||||
|         connection.commit() | ||||
|  | ||||
|         # Run any post-save hooks. | ||||
|         if hasattr(self, '_post_save'): | ||||
|             self._post_save() | ||||
|  | ||||
|     save.alters_data = True | ||||
|  | ||||
|     def delete(self): | ||||
|         assert getattr(self, self._meta.pk.attname) is not None, "%r can't be deleted because it doesn't have an ID." | ||||
|  | ||||
|         # Run any pre-delete hooks. | ||||
|         if hasattr(self, '_pre_delete'): | ||||
|             self._pre_delete() | ||||
|  | ||||
|         cursor = connection.cursor() | ||||
|         for related in self._meta.get_all_related_objects(): | ||||
|             rel_opts_name = related.get_method_name_part() | ||||
|             if isinstance(related.field.rel, OneToOne): | ||||
|                 try: | ||||
|                     sub_obj = getattr(self, 'get_%s' % rel_opts_name)() | ||||
|                 except ObjectDoesNotExist: | ||||
|                     pass | ||||
|                 else: | ||||
|                     sub_obj.delete() | ||||
|             else: | ||||
|                 for sub_obj in getattr(self, 'get_%s_list' % rel_opts_name)(): | ||||
|                     sub_obj.delete() | ||||
|         for related in self._meta.get_all_related_many_to_many_objects(): | ||||
|             cursor.execute("DELETE FROM %s WHERE %s=%%s" % \ | ||||
|                 (backend.quote_name(related.field.get_m2m_db_table(related.opts)), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id')), [getattr(self, self._meta.pk.attname)]) | ||||
|         for f in self._meta.many_to_many: | ||||
|             cursor.execute("DELETE FROM %s WHERE %s=%%s" % \ | ||||
|                 (backend.quote_name(f.get_m2m_db_table(self._meta)), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id')), | ||||
|                 [getattr(self, self._meta.pk.attname)]) | ||||
|  | ||||
|         cursor.execute("DELETE FROM %s WHERE %s=%%s" % \ | ||||
|             (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), | ||||
|             [getattr(self, self._meta.pk.attname)]) | ||||
|  | ||||
|         connection.commit() | ||||
|         setattr(self, self._meta.pk.attname, None) | ||||
|         for f in self._meta.fields: | ||||
|             if isinstance(f, FileField) and getattr(self, f.attname): | ||||
|                 file_name = getattr(self, 'get_%s_filename' % f.name)() | ||||
|                 # If the file exists and no other object of this type references it, | ||||
|                 # delete it from the filesystem. | ||||
|                 if os.path.exists(file_name) and not self._default_manager.get_list(**{'%s__exact' % f.name: getattr(self, f.name)}): | ||||
|                     os.remove(file_name) | ||||
|  | ||||
|         # Run any post-delete hooks. | ||||
|         if hasattr(self, '_post_delete'): | ||||
|             self._post_delete() | ||||
|  | ||||
|     delete.alters_data = True | ||||
|  | ||||
|      | ||||
|     def __get_FIELD_display(self, field): | ||||
|         value = getattr(self, field.attname) | ||||
|         return dict(field.choices).get(value, value) | ||||
|  | ||||
|     def __get_next_or_previous_by_FIELD(self, field, is_next, **kwargs): | ||||
|         op = is_next and '>' or '<' | ||||
|         kwargs.setdefault('where', []).append('(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \ | ||||
|             (backend.quote_name(field.column), op, backend.quote_name(field.column), | ||||
|             backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column), op)) | ||||
|         param = str(getattr(self, field.attname)) | ||||
|         kwargs.setdefault('params', []).extend([param, param, getattr(self, self._meta.pk.attname)]) | ||||
|         kwargs['order_by'] = [(not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name] | ||||
|         kwargs['limit'] = 1 | ||||
|         return self.__class__._default_manager.get_object(**kwargs) | ||||
|  | ||||
|     def __get_next_or_previous_in_order(self, is_next): | ||||
|         cachename = "__%s_order_cache" % is_next | ||||
|         if not hasattr(self, cachename): | ||||
|             op = is_next and '>' or '<' | ||||
|             order_field = self.order_with_respect_to | ||||
|             obj = self._default_manager.get_object(order_by=('_order',), | ||||
|                 where=['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \ | ||||
|                     (backend.quote_name('_order'), op, backend.quote_name('_order'), | ||||
|                     backend.quote_name(opts.db_table), backend.quote_name(opts.pk.column)), | ||||
|                     '%s=%%s' % backend.quote_name(order_field.column)], | ||||
|                 limit=1, | ||||
|                 params=[getattr(self, opts.pk.attname), getattr(self, order_field.attname)]) | ||||
|             setattr(self, cachename, obj) | ||||
|         return getattr(self, cachename) | ||||
|  | ||||
|     def __get_FIELD_filename(self, field): | ||||
|         return os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname)) | ||||
|  | ||||
|     def __get_FIELD_url(self, field): | ||||
|         if getattr(self, field.attname): # value is not blank | ||||
|             import urlparse | ||||
|             return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/') | ||||
|         return '' | ||||
|  | ||||
|     def __get_FIELD_size(self, field): | ||||
|         return os.path.getsize(self.__get_FIELD_filename(field)) | ||||
|  | ||||
|     def __save_FIELD_file(self, field, filename, raw_contents): | ||||
|         directory = field.get_directory_name() | ||||
|         try: # Create the date-based directory if it doesn't exist. | ||||
|             os.makedirs(os.path.join(settings.MEDIA_ROOT, directory)) | ||||
|         except OSError: # Directory probably already exists. | ||||
|             pass | ||||
|         filename = field.get_filename(filename) | ||||
|  | ||||
|         # If the filename already exists, keep adding an underscore to the name of | ||||
|         # the file until the filename doesn't exist. | ||||
|         while os.path.exists(os.path.join(settings.MEDIA_ROOT, filename)): | ||||
|             try: | ||||
|                 dot_index = filename.rindex('.') | ||||
|             except ValueError: # filename has no dot | ||||
|                 filename += '_' | ||||
|             else: | ||||
|                 filename = filename[:dot_index] + '_' + filename[dot_index:] | ||||
|  | ||||
|         # Write the file to disk. | ||||
|         setattr(self, field.attname, filename) | ||||
|  | ||||
|         full_filename = self.__get_FIELD_filename(field) | ||||
|         fp = open(full_filename, 'wb') | ||||
|         fp.write(raw_contents) | ||||
|         fp.close() | ||||
|  | ||||
|         # Save the width and/or height, if applicable. | ||||
|         if isinstance(field, ImageField) and (field.width_field or field.height_field): | ||||
|             from django.utils.images import get_image_dimensions | ||||
|             width, height = get_image_dimensions(full_filename) | ||||
|             if field.width_field: | ||||
|                 setattr(self, field.width_field, width) | ||||
|             if field.height_field: | ||||
|                 setattr(self, field.height_field, height) | ||||
|  | ||||
|         # Save the object, because it has changed. | ||||
|         self.save() | ||||
|  | ||||
|     __save_FIELD_file.alters_data = True | ||||
|  | ||||
|     def __get_FIELD_width(self, field): | ||||
|         return self.__get_image_dimensions(field)[0] | ||||
|  | ||||
|     def __get_FIELD_height(self, field): | ||||
|         return self.__get_image_dimensions(field)[1] | ||||
|  | ||||
|     def __get_image_dimensions(self, field): | ||||
|         cachename = "__%s_dimensions_cache" % field.name | ||||
|         if not hasattr(self, cachename): | ||||
|             from django.utils.images import get_image_dimensions | ||||
|             filename = self.__get_FIELD_filename(field)() | ||||
|             setattr(self, cachename, get_image_dimensions(filename)) | ||||
|         return getattr(self, cachename) | ||||
|  | ||||
|     def __get_foreign_key_object(self, field_with_rel): | ||||
|         cache_var = field_with_rel.get_cache_name() | ||||
|         if not hasattr(self, cache_var): | ||||
|             val = getattr(self, field_with_rel.attname) | ||||
|             if val is None: | ||||
|                 raise field_with_rel.rel.to.DoesNotExist | ||||
|             other_field = field_with_rel.rel.get_related_field() | ||||
|             if other_field.rel: | ||||
|                 params = {'%s__%s__exact' % (field_with_rel.rel.field_name, other_field.rel.field_name): val} | ||||
|             else: | ||||
|                 params = {'%s__exact' % field_with_rel.rel.field_name: val} | ||||
|             retrieved_obj = field_with_rel.rel.to._default_manager.get_object(**params) | ||||
|             setattr(self, cache_var, retrieved_obj) | ||||
|         return getattr(self, cache_var) | ||||
|  | ||||
|     def __get_many_to_many_objects(self, field_with_rel): | ||||
|         cache_var = '_%s_cache' % field_with_rel.name | ||||
|         if not hasattr(self, cache_var): | ||||
|             rel_opts = field_with_rel.rel.to._meta | ||||
|             sql = "SELECT %s FROM %s a, %s b WHERE a.%s = b.%s AND b.%s = %%s %s" % \ | ||||
|                 (','.join(['a.%s' % backend.quote_name(f.column) for f in rel_opts.fields]), | ||||
|                 backend.quote_name(rel_opts.db_table), | ||||
|                 backend.quote_name(field_with_rel.get_m2m_db_table(self._meta)), | ||||
|                 backend.quote_name(rel_opts.pk.column), | ||||
|                 backend.quote_name(rel_opts.object_name.lower() + '_id'), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id'), rel_opts.get_order_sql('a')) | ||||
|             cursor = connection.cursor() | ||||
|             cursor.execute(sql, [getattr(self, self._meta.pk.attname)]) | ||||
|             setattr(self, cache_var, [field_with_rel.rel.to(*row) for row in cursor.fetchall()]) | ||||
|         return getattr(self, cache_var) | ||||
|  | ||||
|     def __set_many_to_many_objects(self, id_list, field_with_rel): | ||||
|         current_ids = [obj.id for obj in self.__get_many_to_many_objects(field_with_rel)] | ||||
|         ids_to_add, ids_to_delete = dict([(i, 1) for i in id_list]), [] | ||||
|         for current_id in current_ids: | ||||
|             if current_id in id_list: | ||||
|                 del ids_to_add[current_id] | ||||
|             else: | ||||
|                 ids_to_delete.append(current_id) | ||||
|         ids_to_add = ids_to_add.keys() | ||||
|         # Now ids_to_add is a list of IDs to add, and ids_to_delete is a list of IDs to delete. | ||||
|         if not ids_to_delete and not ids_to_add: | ||||
|             return False # No change | ||||
|         rel = field_with_rel.rel.to._meta | ||||
|         m2m_table = field_with_rel.get_m2m_db_table(self._meta) | ||||
|         cursor = connection.cursor() | ||||
|         this_id = getattr(self, self._meta.pk.attname) | ||||
|         if ids_to_delete: | ||||
|             sql = "DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \ | ||||
|                 (backend.quote_name(m2m_table), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id'), | ||||
|                 backend.quote_name(rel.object_name.lower() + '_id'), ','.join(map(str, ids_to_delete))) | ||||
|             cursor.execute(sql, [this_id]) | ||||
|         if ids_to_add: | ||||
|             sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ | ||||
|                 (backend.quote_name(m2m_table), | ||||
|                 backend.quote_name(self._meta.object_name.lower() + '_id'), | ||||
|                 backend.quote_name(rel.object_name.lower() + '_id')) | ||||
|             cursor.executemany(sql, [(this_id, i) for i in ids_to_add]) | ||||
|         connection.commit() | ||||
|         try: | ||||
|             delattr(self, '_%s_cache' % field_with_rel.name) # clear cache, if it exists | ||||
|         except AttributeError: | ||||
|             pass | ||||
|         return True | ||||
|  | ||||
|     __set_many_to_many_objects.alters_data = True | ||||
|  | ||||
|     def _get_related(self, method_name, rel_class, rel_field, **kwargs): | ||||
|         kwargs['%s__%s__exact' % (rel_field.name, rel_field.rel.to._meta.pk.name)] = getattr(self, rel_field.rel.get_related_field().attname) | ||||
|         kwargs.update(rel_field.rel.lookup_overrides) | ||||
|         return getattr(rel_class._default_manager, method_name)(**kwargs) | ||||
|  | ||||
|     def _add_related(self, rel_class, rel_field, *args, **kwargs): | ||||
|         init_kwargs = dict(zip([f.attname for f in rel_class._meta.fields if f != rel_field and not isinstance(f, AutoField)], args)) | ||||
|         init_kwargs.update(kwargs) | ||||
|         for f in rel_class._meta.fields: | ||||
|             if isinstance(f, AutoField): | ||||
|                 init_kwargs[f.attname] = None | ||||
|         init_kwargs[rel_field.name] = self | ||||
|         obj = rel_class(**init_kwargs) | ||||
|         obj.save() | ||||
|         return obj | ||||
|  | ||||
|     _add_related.alters_data = True | ||||
|  | ||||
|  | ||||
|     # Handles related many-to-many object retrieval. | ||||
|     # Examples: Album.get_song(), Album.get_song_list(), Album.get_song_count() | ||||
|     def _get_related_many_to_many(self, method_name, rel_class, rel_field, **kwargs): | ||||
|         kwargs['%s__%s__exact' % (rel_field.name, self._meta.pk.name)] = getattr(self, self._meta.pk.attname) | ||||
|         return getattr(rel_class._default_manager, method_name)(**kwargs) | ||||
|  | ||||
|     # Handles setting many-to-many related objects. | ||||
|     # Example: Album.set_songs() | ||||
|     def _set_related_many_to_many(self, rel_class, rel_field, id_list): | ||||
|         id_list = map(int, id_list) # normalize to integers | ||||
|         rel = rel_field.rel.to | ||||
|         m2m_table = rel_field.get_m2m_db_table(rel_opts) | ||||
|         this_id = getattr(self, self._meta.pk.attname) | ||||
|         cursor = connection.cursor() | ||||
|         cursor.execute("DELETE FROM %s WHERE %s = %%s" % \ | ||||
|             (backend.quote_name(m2m_table), | ||||
|             backend.quote_name(rel.object_name.lower() + '_id')), [this_id]) | ||||
|         sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ | ||||
|             (backend.quote_name(m2m_table), | ||||
|             backend.quote_name(rel.object_name.lower() + '_id'), | ||||
|             backend.quote_name(rel_opts.object_name.lower() + '_id')) | ||||
|         cursor.executemany(sql, [(this_id, i) for i in id_list]) | ||||
|         connection.commit() | ||||
|  | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
| ############################################ | ||||
| # HELPER FUNCTIONS (CURRIED MODEL METHODS) # | ||||
| ############################################ | ||||
|  | ||||
| # ORDERING METHODS ######################### | ||||
|  | ||||
| def method_set_order(ordered_obj, self, id_list): | ||||
|     cursor = connection.cursor() | ||||
|     # Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s" | ||||
|     sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \ | ||||
|         (backend.quote_name(ordered_obj.db_table), backend.quote_name('_order'), | ||||
|         backend.quote_name(ordered_obj.order_with_respect_to.column), | ||||
|         backend.quote_name(ordered_obj.pk.column)) | ||||
|     rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name) | ||||
|     cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)]) | ||||
|     connection.commit() | ||||
|  | ||||
| def method_get_order(ordered_obj, self): | ||||
|     cursor = connection.cursor() | ||||
|     # Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order" | ||||
|     sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \ | ||||
|         (backend.quote_name(ordered_obj.pk.column), | ||||
|         backend.quote_name(ordered_obj.db_table), | ||||
|         backend.quote_name(ordered_obj.order_with_respect_to.column), | ||||
|         backend.quote_name('_order')) | ||||
|     rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name) | ||||
|     cursor.execute(sql, [rel_val]) | ||||
|     return [r[0] for r in cursor.fetchall()] | ||||
|  | ||||
| ############################################## | ||||
| # HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) # | ||||
| ############################################## | ||||
|  | ||||
| def get_absolute_url(opts, func, self): | ||||
|     return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self) | ||||
|  | ||||
|  | ||||
							
								
								
									
										5
									
								
								django/db/models/exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								django/db/models/exceptions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| class FieldDoesNotExist(Exception): | ||||
|     pass | ||||
|  | ||||
| class BadKeywordArguments(Exception): | ||||
|     pass | ||||
							
								
								
									
										191
									
								
								django/db/models/options.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								django/db/models/options.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| from django.db.models.related import RelatedObject | ||||
| from django.db.models.fields 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 | ||||
|  | ||||
| class Options: | ||||
|     def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', | ||||
|         fields=None, ordering=None, unique_together=None, admin=None, | ||||
|         where_constraints=None, object_name=None, app_label=None, | ||||
|         exceptions=None, permissions=None, get_latest_by=None, | ||||
|         order_with_respect_to=None, module_constants=None): | ||||
|         # Move many-to-many related fields from self.fields into self.many_to_many. | ||||
|         self.fields, self.many_to_many = [], [] | ||||
|         for field in (fields or []): | ||||
|             if field.rel and isinstance(field.rel, ManyToMany): | ||||
|                 self.many_to_many.append(field) | ||||
|             else: | ||||
|                 self.fields.append(field) | ||||
|         self.module_name, self.verbose_name = module_name, verbose_name | ||||
|         self.verbose_name_plural = verbose_name_plural or verbose_name + 's' | ||||
|         self.db_table = db_table | ||||
|         self.ordering = ordering or [] | ||||
|         self.unique_together = unique_together or [] | ||||
|         self.where_constraints = where_constraints or [] | ||||
|         self.exceptions = exceptions or [] | ||||
|         self.permissions = permissions or [] | ||||
|         self.object_name, self.app_label = object_name, app_label | ||||
|         self.get_latest_by = get_latest_by | ||||
|         if order_with_respect_to: | ||||
|             self.order_with_respect_to = self.get_field(order_with_respect_to) | ||||
|             self.ordering = ('_order',) | ||||
|         else: | ||||
|             self.order_with_respect_to = None | ||||
|         self.module_constants = module_constants or {} | ||||
|         self.admin = admin | ||||
|  | ||||
|         # 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: | ||||
|             self.fields.insert(0, AutoField(name='id', verbose_name='ID', primary_key=True)) | ||||
|             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 = {} | ||||
|  | ||||
|  | ||||
|     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'): | ||||
|             import django.models.core | ||||
|             manager = django.models.core.ContentType.objects | ||||
|             self._content_type_id = \ | ||||
|                 manager.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] | ||||
| @@ -1,4 +1,5 @@ | ||||
| from django.db import backend, connection | ||||
| from django.db.models.exceptions import * | ||||
|  | ||||
| LOOKUP_SEPARATOR = '__' | ||||
| #################### | ||||
|   | ||||
							
								
								
									
										135
									
								
								django/db/models/related.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								django/db/models/related.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| class BoundRelatedObject(object): | ||||
|     def __init__(self, related_object, field_mapping, original): | ||||
|         self.relation = related_object | ||||
|         self.field_mappings = field_mapping[related_object.opts.module_name] | ||||
|  | ||||
|     def template_name(self): | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return repr(self.__dict__) | ||||
|  | ||||
| class RelatedObject(object): | ||||
|     def __init__(self, parent_opts, model, field): | ||||
|         self.parent_opts = parent_opts | ||||
|         self.model = model | ||||
|         self.opts = model._meta | ||||
|         self.field = field | ||||
|         self.edit_inline = field.rel.edit_inline | ||||
|         self.name = self.opts.module_name | ||||
|         self.var_name = self.opts.object_name.lower() | ||||
|  | ||||
|     def flatten_data(self, follow, obj=None): | ||||
|         new_data = {} | ||||
|         rel_instances = self.get_list(obj) | ||||
|         for i, rel_instance in enumerate(rel_instances): | ||||
|             instance_data = {} | ||||
|             for f in self.opts.fields + self.opts.many_to_many: | ||||
|                 # TODO: Fix for recursive manipulators. | ||||
|                 fol = follow.get(f.name, None) | ||||
|                 if fol: | ||||
|                     field_data = f.flatten_data(fol, rel_instance) | ||||
|                     for name, value in field_data.items(): | ||||
|                         instance_data['%s.%d.%s' % (self.var_name, i, name)] = value | ||||
|             new_data.update(instance_data) | ||||
|         return new_data | ||||
|  | ||||
|     def extract_data(self, data): | ||||
|         """ | ||||
|         Pull out the data meant for inline objects of this class, | ||||
|         i.e. anything starting with our module name. | ||||
|         """ | ||||
|         return data # TODO | ||||
|  | ||||
|     def get_list(self, parent_instance=None): | ||||
|         "Get the list of this type of object from an instance of the parent class." | ||||
|         if parent_instance != None: | ||||
|             func_name = 'get_%s_list' % self.get_method_name_part() | ||||
|             func = getattr(parent_instance, func_name) | ||||
|             list = func() | ||||
|  | ||||
|             count = len(list) + self.field.rel.num_extra_on_change | ||||
|             if self.field.rel.min_num_in_admin: | ||||
|                count = max(count, self.field.rel.min_num_in_admin) | ||||
|             if self.field.rel.max_num_in_admin: | ||||
|                count = min(count, self.field.rel.max_num_in_admin) | ||||
|  | ||||
|             change = count - len(list) | ||||
|             if change > 0: | ||||
|                 return list + [None for _ in range(change)] | ||||
|             if change < 0: | ||||
|                 return list[:change] | ||||
|             else: # Just right | ||||
|                 return list | ||||
|         else: | ||||
|             return [None for _ in range(self.field.rel.num_in_admin)] | ||||
|  | ||||
|  | ||||
|     def editable_fields(self): | ||||
|         "Get the fields in this class that should be edited inline." | ||||
|         return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field] | ||||
|  | ||||
|     def get_follow(self, override=None): | ||||
|         if isinstance(override, bool): | ||||
|             if override: | ||||
|                 over = {} | ||||
|             else: | ||||
|                 return None | ||||
|         else: | ||||
|             if override: | ||||
|                 over = override.copy() | ||||
|             elif self.edit_inline: | ||||
|                 over = {} | ||||
|             else: | ||||
|                 return None | ||||
|  | ||||
|         over[self.field.name] = False | ||||
|         return self.opts.get_follow(over) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "<RelatedObject: %s related to %s>" % (self.name, self.field.name) | ||||
|  | ||||
|     def get_manipulator_fields(self, opts, manipulator, change, follow): | ||||
|         # TODO: Remove core fields stuff. | ||||
|          | ||||
|         if manipulator.original_object: | ||||
|             meth_name = 'get_%s_count' % self.get_method_name_part() | ||||
|             count = getattr(manipulator.original_object, meth_name)() | ||||
|              | ||||
|             count += self.field.rel.num_extra_on_change | ||||
|             if self.field.rel.min_num_in_admin: | ||||
|                 count = max(count, self.field.rel.min_num_in_admin) | ||||
|             if self.field.rel.max_num_in_admin: | ||||
|                 count = min(count, self.field.rel.max_num_in_admin) | ||||
|         else: | ||||
|             count = self.field.rel.num_in_admin | ||||
|         fields = [] | ||||
|         for i in range(count): | ||||
|             for f in self.opts.fields + self.opts.many_to_many: | ||||
|                 if follow.get(f.name, False): | ||||
|                     prefix = '%s.%d.' % (self.var_name, i) | ||||
|                     fields.extend(f.get_manipulator_fields(self.opts, manipulator, change, name_prefix=prefix, rel=True)) | ||||
|         return fields | ||||
|  | ||||
|     def bind(self, field_mapping, original, bound_related_object_class=BoundRelatedObject): | ||||
|         return bound_related_object_class(self, field_mapping, original) | ||||
|  | ||||
|     def get_method_name_part(self): | ||||
|         # This method encapsulates the logic that decides what name to give a | ||||
|         # method that retrieves related many-to-one or many-to-many objects. | ||||
|         # Usually it just uses the lower-cased object_name, but if the related | ||||
|         # object is in another app, the related object's app_label is appended. | ||||
|         # | ||||
|         # Examples: | ||||
|         # | ||||
|         #   # Normal case -- a related object in the same app. | ||||
|         #   # This method returns "choice". | ||||
|         #   Poll.get_choice_list() | ||||
|         # | ||||
|         #   # A related object in a different app. | ||||
|         #   # This method returns "lcom_bestofaward". | ||||
|         #   Place.get_lcom_bestofaward_list() # "lcom_bestofaward" | ||||
|         rel_obj_name = self.field.rel.related_name or self.opts.object_name.lower() | ||||
|         if self.parent_opts.app_label != self.opts.app_label: | ||||
|             rel_obj_name = '%s_%s' % (self.opts.app_label, rel_obj_name) | ||||
|         return rel_obj_name | ||||
		Reference in New Issue
	
	Block a user