mirror of
				https://github.com/django/django.git
				synced 2025-10-26 07:06:08 +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.utils.functional import curry | ||||||
| from django.contrib.admin.views.main import AdminBoundField | from django.contrib.admin.views.main import AdminBoundField | ||||||
| from django.db.models.fields import BoundField, Field | 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.db import models | ||||||
| from django.conf.settings import ADMIN_MEDIA_PREFIX | from django.conf.settings import ADMIN_MEDIA_PREFIX | ||||||
| import re | import re | ||||||
|   | |||||||
| @@ -24,6 +24,8 @@ from django.utils.html import escape | |||||||
| import operator | import operator | ||||||
| from itertools import izip | 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 | # The system will display a "Show all" link only if the total result count | ||||||
| # is less than or equal to this setting. | # is less than or equal to this setting. | ||||||
| MAX_SHOW_ALL_ALLOWED = 200 | 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] |         ordering = lookup_opts.admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name] | ||||||
|  |  | ||||||
|         # Normalize it to new-style ordering. |         # Normalize it to new-style ordering. | ||||||
|         ordering = models.handle_legacy_orderlist(ordering) |         ordering = handle_legacy_orderlist(ordering) | ||||||
|  |  | ||||||
|         if ordering[0].startswith('-'): |         if ordering[0].startswith('-'): | ||||||
|             order_field, order_type = ordering[0][1:], 'desc' |             order_field, order_type = ordering[0][1:], 'desc' | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| from copy import copy | from copy import copy | ||||||
| from math import ceil | from math import ceil | ||||||
| from django.db.models import ModelBase |  | ||||||
|  |  | ||||||
| class InvalidPage(Exception): | class InvalidPage(Exception): | ||||||
|     pass |     pass | ||||||
|   | |||||||
| @@ -1,36 +1,24 @@ | |||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.core import formfields, validators | from django.core import formfields, validators | ||||||
| from django.core.exceptions import ObjectDoesNotExist |  | ||||||
| from django.db import backend, connection | from django.db import backend, connection | ||||||
| from django.db.models.fields import * |  | ||||||
| from django.utils.functional import curry | from django.utils.functional import curry | ||||||
| from django.utils.text import capfirst | 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.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 | ||||||
| from django.db.models.query import Q, orderlist2sql  |  | ||||||
| from django.db.models.manager import Manager | 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. | # Admin stages. | ||||||
| ADD, CHANGE, BOTH = 1, 2, 3 | 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): | #def get_module(app_label, module_name): | ||||||
| #    return __import__('%s.%s.%s' % (MODEL_PREFIX, 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), '', '', ['']) | #    return __import__('%s.%s' % (MODEL_PREFIX, app_label), '', '', ['']) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class LazyDate: | class LazyDate: | ||||||
|     """ |     """ | ||||||
|     Use in limit_choices_to to compare the field to dates calculated at run time |     Use in limit_choices_to to compare the field to dates calculated at run time | ||||||
| @@ -64,905 +51,10 @@ class LazyDate: | |||||||
| # MAIN CLASSES # | # 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 import backend, connection | ||||||
|  | from django.db.models.exceptions import * | ||||||
|  |  | ||||||
| LOOKUP_SEPARATOR = '__' | 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