1
0
mirror of https://github.com/django/django.git synced 2025-06-29 15:29:13 +00:00

magic-removal: Start of core fields removal. edit-inline functionality does not offer a way to add or delete items right now.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1767 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-12-22 19:23:34 +00:00
parent 79ad41be03
commit a422575d4b
8 changed files with 253 additions and 199 deletions

View File

@ -11,6 +11,8 @@ try:
except ImportError: except ImportError:
raise ImproperlyConfigured, "You don't have 'django.contrib.admin' in INSTALLED_APPS." raise ImproperlyConfigured, "You don't have 'django.contrib.admin' in INSTALLED_APPS."
from django.core.exceptions import Http404, ImproperlyConfigured, ObjectDoesNotExist
def log_change_message(user, opts, manipulator, new_object): def log_change_message(user, opts, manipulator, new_object):
pk_value = getattr(new_object, opts.pk.column) pk_value = getattr(new_object, opts.pk.column)
# Construct the change message. # Construct the change message.

View File

@ -727,6 +727,10 @@ def get_validation_errors(outfile):
# Do field-specific validation. # Do field-specific validation.
for f in opts.fields: for f in opts.fields:
# Check for deprecated args
dep_args = getattr(f, 'deprecated_args', None)
if dep_args:
e.add(opts, "'%s' field: Initialised with deprecated args:%s" % (f.name, ",".join(dep_args)))
if isinstance(f, models.CharField) and f.maxlength in (None, 0): if isinstance(f, models.CharField) and f.maxlength in (None, 0):
e.add(opts, '"%s" field: CharFields require a "maxlength" attribute.' % f.name) e.add(opts, '"%s" field: CharFields require a "maxlength" attribute.' % f.name)
if isinstance(f, models.FloatField): if isinstance(f, models.FloatField):
@ -799,16 +803,6 @@ def get_validation_errors(outfile):
except models.FieldDoesNotExist: except models.FieldDoesNotExist:
e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name) e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
# Check core=True, if needed.
for related in opts.get_followed_related_objects():
try:
for f in related.opts.fields:
if f.core:
raise StopIteration
e.add(related.opts, "At least one field in %s should have core=True, because it's being edited inline by %s.%s." % (related.opts.object_name, opts.module_name, opts.object_name))
except StopIteration:
pass
# Check unique_together. # Check unique_together.
for ut in opts.unique_together: for ut in opts.unique_together:
for field_name in ut: for field_name in ut:
@ -819,6 +813,7 @@ def get_validation_errors(outfile):
else: else:
if isinstance(f.rel, models.ManyToMany): if isinstance(f.rel, models.ManyToMany):
e.add(opts, '"unique_together" refers to %s. ManyToManyFields are not supported in unique_together.' % f.name) e.add(opts, '"unique_together" refers to %s. ManyToManyFields are not supported in unique_together.' % f.name)
return len(e.errors) return len(e.errors)
def validate(outfile=sys.stdout): def validate(outfile=sys.stdout):

View File

@ -423,8 +423,11 @@ def phone2numeric(value):
def pprint(value): def pprint(value):
"A wrapper around pprint.pprint -- for debugging, really" "A wrapper around pprint.pprint -- for debugging, really"
from pprint import pformat from pprint import pformat
return pformat(value) try:
return pformat(value)
except Exception, e:
return "Error in formatting:%s" % e
# Syntax: register.filter(name of filter, callback) # Syntax: register.filter(name of filter, callback)
register.filter(add) register.filter(add)
register.filter(addslashes) register.filter(addslashes)

View File

@ -1,6 +1,6 @@
import django.db.models.manipulators import django.db.models.manipulators
import django.db.models.manager import django.db.models.manager
from django.db.models.fields import AutoField from django.db.models.fields import AutoField, ImageField
from django.db.models.fields.related import OneToOne, ManyToOne from django.db.models.fields.related import OneToOne, ManyToOne
from django.db.models.related import RelatedObject from django.db.models.related import RelatedObject
from django.db.models.query import orderlist2sql from django.db.models.query import orderlist2sql
@ -14,6 +14,7 @@ from django.conf import settings
import re import re
import types import types
import sys import sys
import os
# For Python 2.3 # For Python 2.3
if not hasattr(__builtins__, 'set'): if not hasattr(__builtins__, 'set'):
@ -347,7 +348,7 @@ class Model(object):
# Write the file to disk. # Write the file to disk.
setattr(self, field.attname, filename) setattr(self, field.attname, filename)
full_filename = self.__get_FIELD_filename(field) full_filename = self._get_FIELD_filename(field)
fp = open(full_filename, 'wb') fp = open(full_filename, 'wb')
fp.write(raw_contents) fp.write(raw_contents)
fp.close() fp.close()

View File

@ -97,7 +97,7 @@ class Field(object):
self.primary_key = primary_key self.primary_key = primary_key
self.maxlength, self.unique = maxlength, unique self.maxlength, self.unique = maxlength, unique
self.blank, self.null = blank, null self.blank, self.null = blank, null
self.core, self.rel, self.default = core, rel, default self.rel, self.default = rel, default
self.editable = editable self.editable = editable
self.validator_list = validator_list or [] self.validator_list = validator_list or []
self.prepopulate_from = prepopulate_from self.prepopulate_from = prepopulate_from
@ -110,6 +110,11 @@ class Field(object):
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index. # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
self.db_index = db_index self.db_index = db_index
self.deprecated_args = []
if core:
self.deprecated_args.append('core')
# Increase the creation counter, and save our local copy. # Increase the creation counter, and save our local copy.
self.creation_counter = Field.creation_counter self.creation_counter = Field.creation_counter
Field.creation_counter += 1 Field.creation_counter += 1
@ -245,23 +250,21 @@ class Field(object):
params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator)) params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
# Only add is_required=True if the field cannot be blank. Primary keys # Only add is_required=True if the field cannot be blank. Primary keys
# are a special case, and fields in a related context should set this # are a special case.
# as False, because they'll be caught by a separate validator -- params['is_required'] = not self.blank and not self.primary_key
# RequiredIfOtherFieldGiven.
params['is_required'] = not self.blank and not self.primary_key and not rel
# If this field is in a related context, check whether any other fields # If this field is in a related context, check whether any other fields
# in the related object have core=True. If so, add a validator -- # in the related object have core=True. If so, add a validator --
# RequiredIfOtherFieldsGiven -- to this FormField. # RequiredIfOtherFieldsGiven -- to this FormField.
if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField): #if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
# First, get the core fields, if any. # First, get the core fields, if any.
core_field_names = [] # core_field_names = []
for f in opts.fields: # for f in opts.fields:
if f.core and f != self: # if f.core and f != self:
core_field_names.extend(f.get_manipulator_field_names(name_prefix)) # core_field_names.extend(f.get_manipulator_field_names(name_prefix))
# Now, if there are any, add the validator to this FormField. # Now, if there are any, add the validator to this FormField.
if core_field_names: # if core_field_names:
params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, gettext_lazy("This field is required."))) # params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, gettext_lazy("This field is required.")))
# BooleanFields (CheckboxFields) are a special case. They don't take # BooleanFields (CheckboxFields) are a special case. They don't take
# is_required or validator_list. # is_required or validator_list.
@ -280,13 +283,13 @@ class Field(object):
Given the full new_data dictionary (from the manipulator), returns this Given the full new_data dictionary (from the manipulator), returns this
field's data. field's data.
""" """
if rel: #if rel:
return new_data.get(self.name, [self.get_default()])[0] # return new_data.get(self.name, [self.get_default()])[0]
else: #else:
val = new_data.get(self.name, self.get_default()) val = new_data.get(self.name, self.get_default())
if not self.empty_strings_allowed and val == '' and self.null: if not self.empty_strings_allowed and val == '' and self.null:
val = None val = None
return val return val
def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
"Returns a list of tuples used as SelectField choices for this field." "Returns a list of tuples used as SelectField choices for this field."
@ -341,6 +344,9 @@ class AutoField(Field):
return [formfields.HiddenField] return [formfields.HiddenField]
def get_manipulator_new_data(self, new_data, rel=False): def get_manipulator_new_data(self, new_data, rel=False):
# Never going to be called
# Not in main change pages
# ignored in related context
if not rel: if not rel:
return None return None
return Field.get_manipulator_new_data(self, new_data, rel) return Field.get_manipulator_new_data(self, new_data, rel)
@ -436,12 +442,12 @@ class DateTimeField(DateField):
def get_manipulator_new_data(self, new_data, rel=False): def get_manipulator_new_data(self, new_data, rel=False):
date_field, time_field = self.get_manipulator_field_names('') date_field, time_field = self.get_manipulator_field_names('')
if rel: #if rel:
d = new_data.get(date_field, [None])[0] # d = new_data.get(date_field, [None])[0]
t = new_data.get(time_field, [None])[0] # t = new_data.get(time_field, [None])[0]
else: #else:
d = new_data.get(date_field, None) d = new_data.get(date_field, None)
t = new_data.get(time_field, None) t = new_data.get(time_field, None)
if d is not None and t is not None: if d is not None and t is not None:
return datetime.datetime.combine(d, t) return datetime.datetime.combine(d, t)
return self.get_default() return self.get_default()
@ -472,30 +478,30 @@ class FileField(Field):
field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow) field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
if not self.blank: if not self.blank:
if rel: #if rel:
# This validator makes sure FileFields work in a related context. # This validator makes sure FileFields work in a related context.
class RequiredFileField: # class RequiredFileField:
def __init__(self, other_field_names, other_file_field_name): # def __init__(self, other_field_names, other_file_field_name):
self.other_field_names = other_field_names # self.other_field_names = other_field_names
self.other_file_field_name = other_file_field_name # self.other_file_field_name = other_file_field_name
self.always_test = True # self.always_test = True
def __call__(self, field_data, all_data): # def __call__(self, field_data, all_data):
if not all_data.get(self.other_file_field_name, False): # if not all_data.get(self.other_file_field_name, False):
c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, gettext_lazy("This field is required.")) # c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, gettext_lazy("This field is required."))
c(field_data, all_data) # c(field_data, all_data)
# First, get the core fields, if any. # # First, get the core fields, if any.
core_field_names = [] # core_field_names = []
for f in opts.fields: # for f in opts.fields:
if f.core and f != self: # if f.core and f != self:
core_field_names.extend(f.get_manipulator_field_names(name_prefix)) # core_field_names.extend(f.get_manipulator_field_names(name_prefix))
# Now, if there are any, add the validator to this FormField. # # Now, if there are any, add the validator to this FormField.
if core_field_names: # if core_field_names:
field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name)) # field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
else: # else:
v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, gettext_lazy("This field is required.")) v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, gettext_lazy("This field is required."))
v.always_test = True v.always_test = True
field_list[0].validator_list.append(v) field_list[0].validator_list.append(v)
field_list[0].is_required = field_list[1].is_required = False field_list[0].is_required = field_list[1].is_required = False
# If the raw path is passed in, validate it's under the MEDIA_ROOT. # If the raw path is passed in, validate it's under the MEDIA_ROOT.
def isWithinMediaRoot(field_data, all_data): def isWithinMediaRoot(field_data, all_data):
@ -510,7 +516,10 @@ class FileField(Field):
setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self)) setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
setattr(cls, 'save_%s_file' % self.name, curry(cls._save_FIELD_file, field=self)) setattr(cls, 'save_%s_file' % self.name,
lambda instance, filename, raw_contents:
instance._save_FIELD_file(self,filename, raw_contents)
)
dispatcher.connect( dispatcher.connect(
self.delete_file, self.delete_file,
signal = signals.post_delete, signal = signals.post_delete,
@ -536,10 +545,7 @@ class FileField(Field):
upload_field_name = self.get_manipulator_field_names('')[0] upload_field_name = self.get_manipulator_field_names('')[0]
if new_data.get(upload_field_name, False): if new_data.get(upload_field_name, False):
func = getattr(new_object, 'save_%s_file' % self.name) func = getattr(new_object, 'save_%s_file' % self.name)
if rel: func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
else:
func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
def get_directory_name(self): def get_directory_name(self):
return os.path.normpath(datetime.datetime.now().strftime(self.upload_to)) return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
@ -575,7 +581,7 @@ class ImageField(FileField):
return [formfields.ImageUploadField, formfields.HiddenField] return [formfields.ImageUploadField, formfields.HiddenField]
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):
super(FileField, self).contribute_to_class(cls, name) super(ImageField, self).contribute_to_class(cls, name)
# Add get_BLAH_width and get_BLAH_height methods, but only if the # Add get_BLAH_width and get_BLAH_height methods, but only if the
# image field doesn't have width and height cache fields. # image field doesn't have width and height cache fields.
if not self.width_field: if not self.width_field:

View File

@ -88,17 +88,17 @@ class ForeignKey(SharedMethods, Field):
kwargs['edit_inline'] = kwargs.pop('edit_inline_type') kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
kwargs['rel'] = ManyToOne(to, to_field, kwargs['rel'] = ManyToOne(to, to_field,
num_in_admin=kwargs.pop('num_in_admin', 3),
min_num_in_admin=kwargs.pop('min_num_in_admin', None),
max_num_in_admin=kwargs.pop('max_num_in_admin', None),
num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
edit_inline=kwargs.pop('edit_inline', False), edit_inline=kwargs.pop('edit_inline', False),
related_name=kwargs.pop('related_name', None), related_name=kwargs.pop('related_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None), limit_choices_to=kwargs.pop('limit_choices_to', None),
lookup_overrides=kwargs.pop('lookup_overrides', None), lookup_overrides=kwargs.pop('lookup_overrides', None),
raw_id_admin=kwargs.pop('raw_id_admin', False)) raw_id_admin=kwargs.pop('raw_id_admin', False))
Field.__init__(self, **kwargs) Field.__init__(self, **kwargs)
for name in ('num_in_admin', 'min_num_in_admin', 'max_num_in_admin', 'num_extra_on_change'):
if name in kwargs:
self.deprecated_args.append(name)
if not self.db_index: if not self.db_index:
self.db_index = True self.db_index = True
@ -179,14 +179,18 @@ class OneToOneField(SharedMethods, IntegerField):
kwargs['edit_inline'] = kwargs.pop('edit_inline_type') kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
kwargs['rel'] = OneToOne(to, to_field, kwargs['rel'] = OneToOne(to, to_field,
num_in_admin=kwargs.pop('num_in_admin', 0),
edit_inline=kwargs.pop('edit_inline', False), edit_inline=kwargs.pop('edit_inline', False),
related_name=kwargs.pop('related_name', None), related_name=kwargs.pop('related_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None), limit_choices_to=kwargs.pop('limit_choices_to', None),
lookup_overrides=kwargs.pop('lookup_overrides', None), lookup_overrides=kwargs.pop('lookup_overrides', None),
raw_id_admin=kwargs.pop('raw_id_admin', False)) raw_id_admin=kwargs.pop('raw_id_admin', False))
kwargs['primary_key'] = True kwargs['primary_key'] = True
IntegerField.__init__(self, **kwargs) IntegerField.__init__(self, **kwargs)
for name in ('num_in_admin'):
if name in kwargs:
self.deprecated_args.append(name)
if not self.db_index: if not self.db_index:
self.db_index = True self.db_index = True
@ -204,7 +208,6 @@ class ManyToManyField(RelatedField, Field):
def __init__(self, to, **kwargs): def __init__(self, to, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', None) kwargs['verbose_name'] = kwargs.get('verbose_name', None)
kwargs['rel'] = ManyToMany(to, kwargs.pop('singular', None), kwargs['rel'] = ManyToMany(to, kwargs.pop('singular', None),
num_in_admin=kwargs.pop('num_in_admin', 0),
related_name=kwargs.pop('related_name', None), related_name=kwargs.pop('related_name', None),
filter_interface=kwargs.pop('filter_interface', None), filter_interface=kwargs.pop('filter_interface', None),
limit_choices_to=kwargs.pop('limit_choices_to', None), limit_choices_to=kwargs.pop('limit_choices_to', None),
@ -212,6 +215,11 @@ class ManyToManyField(RelatedField, Field):
if kwargs["rel"].raw_id_admin: if kwargs["rel"].raw_id_admin:
kwargs.setdefault("validator_list", []).append(self.isValidIDList) kwargs.setdefault("validator_list", []).append(self.isValidIDList)
Field.__init__(self, **kwargs) Field.__init__(self, **kwargs)
for name in ('num_in_admin'):
if name in kwargs:
self.deprecated_args.append(name)
if self.rel.raw_id_admin: if self.rel.raw_id_admin:
msg = gettext_lazy(' Separate multiple IDs with commas.') msg = gettext_lazy(' Separate multiple IDs with commas.')
else: else:
@ -335,17 +343,15 @@ class ManyToManyFieldNew(RelatedField):
M2M.__name__ = "M2M_%s_%s_%s" % (self.name, self.from_.__name__, self.to.__name__) M2M.__name__ = "M2M_%s_%s_%s" % (self.name, self.from_.__name__, self.to.__name__)
class ManyToOne: class ManyToOne:
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None, def __init__(self, to, field_name, edit_inline=False,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False): related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
try: try:
to._meta to._meta
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT except AttributeError:
assert isinstance(to, basestring) , "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT assert isinstance(to, basestring) , "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
self.to, self.field_name = to, field_name self.to, self.field_name = to, field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline self.edit_inline = edit_inline
self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin self.related_name = related_name
self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
self.limit_choices_to = limit_choices_to or {} self.limit_choices_to = limit_choices_to or {}
self.lookup_overrides = lookup_overrides or {} self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin self.raw_id_admin = raw_id_admin
@ -355,22 +361,21 @@ class ManyToOne:
return self.to._meta.get_field(self.field_name) return self.to._meta.get_field(self.field_name)
class OneToOne(ManyToOne): class OneToOne(ManyToOne):
def __init__(self, to, field_name, num_in_admin=0, edit_inline=False, def __init__(self, to, field_name, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None, related_name=None, limit_choices_to=None, lookup_overrides=None,
raw_id_admin=False): raw_id_admin=False):
self.to, self.field_name = to, field_name self.to, self.field_name = to, field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline self.edit_inline = edit_inline
self.related_name = related_name self.related_name = related_name
self.limit_choices_to = limit_choices_to or {} self.limit_choices_to = limit_choices_to or {}
self.lookup_overrides = lookup_overrides or {} self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin self.raw_id_admin = raw_id_admin
class ManyToMany: class ManyToMany:
def __init__(self, to, singular=None, num_in_admin=0, related_name=None, def __init__(self, to, singular=None, related_name=None,
filter_interface=None, limit_choices_to=None, raw_id_admin=False): filter_interface=None, limit_choices_to=None, raw_id_admin=False):
self.to = to self.to = to
self.singular = singular or None self.singular = singular or None
self.num_in_admin = num_in_admin
self.related_name = related_name self.related_name = related_name
self.filter_interface = filter_interface self.filter_interface = filter_interface
self.limit_choices_to = limit_choices_to or {} self.limit_choices_to = limit_choices_to or {}

View File

@ -64,7 +64,7 @@ class AutomaticManipulator(Manipulator):
self.follow = self.model._meta.get_follow(follow) self.follow = self.model._meta.get_follow(follow)
else: else:
self.follow = follow self.follow = follow
self.fields_, self.children = [], [] self.fields_, self.children = [], {}
self.original_object = original_object self.original_object = original_object
self.name_prefix = name_prefix self.name_prefix = name_prefix
for f in self.opts.get_data_holders(self.follow): for f in self.opts.get_data_holders(self.follow):
@ -72,38 +72,45 @@ class AutomaticManipulator(Manipulator):
fields,manipulators = f.get_fields_and_manipulators(self.opts, self, follow=fol) fields,manipulators = f.get_fields_and_manipulators(self.opts, self, follow=fol)
#fields = f.get_manipulator_fields(self.opts, self, self.change, follow=fol) #fields = f.get_manipulator_fields(self.opts, self, self.change, follow=fol)
self.fields_.extend(fields) self.fields_.extend(fields)
self.children.append(manipulators) if manipulators:
#print self.fields_ self.children[f.var_name] = manipulators
def get_fields(self): def get_fields(self):
l = list(self.fields_) l = list(self.fields_)
for child in self.children: for child_manips in self.children.values():
for manip in child: for manip in child_manips:
l.extend(manip.fields) l.extend(manip.fields)
#print l
return l return l
fields = property(get_fields) fields = property(get_fields)
def save(self, new_data): def get_parameters(self, new_data):
add, change, opts, klass = self.add, self.change, self.opts, self.model
# TODO: big cleanup when core fields go -> use recursive manipulators.
from django.utils.datastructures import DotExpandedDict
params = {} params = {}
for f in opts.fields:
for f in self.opts.fields:
# Fields with auto_now_add should keep their original value in the change stage. # Fields with auto_now_add should keep their original value in the change stage.
auto_now_add = change and getattr(f, 'auto_now_add', False) auto_now_add = self.change and getattr(f, 'auto_now_add', False)
if self.follow.get(f.name, None) and not auto_now_add: if self.follow.get(f.name, None) and not auto_now_add:
param = f.get_manipulator_new_data(new_data) param = f.get_manipulator_new_data(new_data)
else: else:
if change: if self.change:
param = getattr(self.original_object, f.attname) param = getattr(self.original_object, f.attname)
else: else:
param = f.get_default() param = f.get_default()
params[f.attname] = param params[f.attname] = param
if change: if self.change:
params[opts.pk.attname] = self.obj_key params[self.opts.pk.attname] = self.obj_key
return params
def save(self, new_data, expanded=False ):
add, change, opts, klass = self.add, self.change, self.opts, self.model
if not expanded:
from django.utils.datastructures import dot_expand, MultiValueDict
expanded_data = dot_expand(new_data,MultiValueDict)
else:
expanded_data = new_data
params = self.get_parameters(expanded_data)
# First, save the basic object itself. # First, save the basic object itself.
new_object = klass(**params) new_object = klass(**params)
@ -112,14 +119,14 @@ class AutomaticManipulator(Manipulator):
# Now that the object's been saved, save any uploaded files. # Now that the object's been saved, save any uploaded files.
for f in opts.fields: for f in opts.fields:
if isinstance(f, FileField): if isinstance(f, FileField):
f.save_file(new_data, new_object, change and self.original_object or None, change, rel=False) f.save_file(new_data, new_object, change and self.original_object or None, change)
# Calculate which primary fields have changed. # Calculate which primary fields have changed.
if change: if change:
self.fields_added, self.fields_changed, self.fields_deleted = [], [], [] self.fields_added, self.fields_changed, self.fields_deleted = [], [], []
for f in opts.fields: # for f in opts.fields:
if not f.primary_key and str(getattr(self.original_object, f.attname)) != str(getattr(new_object, f.attname)): # if not f.primary_key and str(getattr(self.original_object, f.attname)) != str(getattr(new_object, f.attname)):
self.fields_changed.append(f.verbose_name) # self.fields_changed.append(f.verbose_name)
# Save many-to-many objects. Example: Poll.set_sites() # Save many-to-many objects. Example: Poll.set_sites()
for f in opts.many_to_many: for f in opts.many_to_many:
@ -133,106 +140,126 @@ class AutomaticManipulator(Manipulator):
if change and was_changed: if change and was_changed:
self.fields_changed.append(f.verbose_name) self.fields_changed.append(f.verbose_name)
expanded_data = DotExpandedDict(dict(new_data)) for name, manips in self.children.items():
child_data = expanded_data[name]
#print "with child:", name
# Apply changes to existing objects
for index,manip in enumerate(manips):
obj_data = child_data.get(str(index), None)
child_data.pop(str(index) )
if obj_data:
#save the object with the new data
#print "saving child data:", obj_data
manip.save(obj_data, expanded=True)
else:
#delete the object as it was not in the data
pass
#print "deleting child object:", manip.original_original
if child_data:
# There are new objects in the data, so
# add them.
pass
#print "new data to be added:", child_data
return new_object
# Save many-to-one objects. Example: Add the Choice objects for a Poll. # Save many-to-one objects. Example: Add the Choice objects for a Poll.
for related in opts.get_all_related_objects(): # for related in opts.get_all_related_objects():
# Create obj_list, which is a DotExpandedDict such as this: # Create obj_list, which is a DotExpandedDict such as this:
# [('0', {'id': ['940'], 'choice': ['This is the first choice']}), # [('0', {'id': ['940'], 'choice': ['This is the first choice']}),
# ('1', {'id': ['941'], 'choice': ['This is the second choice']}), # ('1', {'id': ['941'], 'choice': ['This is the second choice']}),
# ('2', {'id': [''], 'choice': ['']})] # ('2', {'id': [''], 'choice': ['']})]
child_follow = self.follow.get(related.name, None) # child_follow = self.follow.get(related.name, None)
if child_follow: # if child_follow:
obj_list = expanded_data[related.var_name].items() # obj_list = expanded_data[related.var_name].items()
obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0]))) # obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0])))
#
# For each related item... # # For each related item...
for null, rel_new_data in obj_list: # for null, rel_new_data in obj_list:
#
params = {} # params = {}
#
# Keep track of which core=True fields were provided. # # Keep track of which core=True fields were provided.
# If all core fields were given, the related object will be saved. # # If all core fields were given, the related object will be saved.
# If none of the core fields were given, the object will be deleted. # # If none of the core fields were given, the object will be deleted.
# If some, but not all, of the fields were given, the validator would # # If some, but not all, of the fields were given, the validator would
# have caught that. # # have caught that.
all_cores_given, all_cores_blank = True, True # all_cores_given, all_cores_blank = True, True
#
# Get a reference to the old object. We'll use it to compare the # # Get a reference to the old object. We'll use it to compare the
# old to the new, to see which fields have changed. # # old to the new, to see which fields have changed.
old_rel_obj = None # old_rel_obj = None
if change: # if change:
if rel_new_data[related.opts.pk.name][0]: # if rel_new_data[related.opts.pk.name][0]:
try: # try:
old_rel_obj = getattr(self.original_object, 'get_%s' % related.get_method_name_part() )(**{'%s__exact' % related.opts.pk.name: rel_new_data[related.opts.pk.attname][0]}) # old_rel_obj = getattr(self.original_object, 'get_%s' % related.get_method_name_part() )(**{'%s__exact' % related.opts.pk.name: rel_new_data[related.opts.pk.attname][0]})
except ObjectDoesNotExist: # except ObjectDoesNotExist:
pass # pass
#
for f in related.opts.fields: # for f in related.opts.fields:
if f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''): # if f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''):
all_cores_given = False # all_cores_given = False
elif f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''): # elif f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''):
all_cores_blank = False # all_cores_blank = False
# If this field isn't editable, give it the same value it had # # If this field isn't editable, give it the same value it had
# previously, according to the given ID. If the ID wasn't # # previously, according to the given ID. If the ID wasn't
# given, use a default value. FileFields are also a special # # given, use a default value. FileFields are also a special
# case, because they'll be dealt with later. # # case, because they'll be dealt with later.
#
if f == related.field: # if f == related.field:
param = getattr(new_object, related.field.rel.field_name) # param = getattr(new_object, related.field.rel.field_name)
elif add and isinstance(f, AutoField): # elif add and isinstance(f, AutoField):
param = None # param = None
elif change and (isinstance(f, FileField) or not child_follow.get(f.name, None)): # elif change and (isinstance(f, FileField) or not child_follow.get(f.name, None)):
if old_rel_obj: # if old_rel_obj:
param = getattr(old_rel_obj, f.column) # param = getattr(old_rel_obj, f.column)
else: # else:
param = f.get_default() # param = f.get_default()
else: # else:
param = f.get_manipulator_new_data(rel_new_data, rel=True) # param = f.get_manipulator_new_data(rel_new_data, rel=True)
if param != None: # if param != None:
params[f.attname] = param # params[f.attname] = param
#
# Create the related item. # # Create the related item.
new_rel_obj = related.model(**params) # new_rel_obj = related.model(**params)
#
# If all the core fields were provided (non-empty), save the item. # # If all the core fields were provided (non-empty), save the item.
if all_cores_given: # if all_cores_given:
new_rel_obj.save() # new_rel_obj.save()
#
# Save any uploaded files. # # Save any uploaded files.
for f in related.opts.fields: # for f in related.opts.fields:
if child_follow.get(f.name, None): # if child_follow.get(f.name, None):
if isinstance(f, FileField) and rel_new_data.get(f.name, False): # if isinstance(f, FileField) and rel_new_data.get(f.name, False):
f.save_file(rel_new_data, new_rel_obj, change and old_rel_obj or None, old_rel_obj is not None, rel=True) # f.save_file(rel_new_data, new_rel_obj, change and old_rel_obj or None, old_rel_obj is not None, rel=True)
#
# Calculate whether any fields have changed. # # Calculate whether any fields have changed.
if change: # if change:
if not old_rel_obj: # This object didn't exist before. # if not old_rel_obj: # This object didn't exist before.
self.fields_added.append('%s "%s"' % (related.opts.verbose_name, new_rel_obj)) # self.fields_added.append('%s "%s"' % (related.opts.verbose_name, new_rel_obj))
else: # else:
for f in related.opts.fields: # for f in related.opts.fields:
if not f.primary_key and f != related.field and str(getattr(old_rel_obj, f.attname)) != str(getattr(new_rel_obj, f.attname)): # if not f.primary_key and f != related.field and str(getattr(old_rel_obj, f.attname)) != str(getattr(new_rel_obj, f.attname)):
self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj)) # self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj))
#
# Save many-to-many objects. # # Save many-to-many objects.
for f in related.opts.many_to_many: # for f in related.opts.many_to_many:
if child_follow.get(f.name, None) and not f.rel.edit_inline: # if child_follow.get(f.name, None) and not f.rel.edit_inline:
was_changed = getattr(new_rel_obj, 'set_%s' % f.name)(rel_new_data[f.attname]) # was_changed = getattr(new_rel_obj, 'set_%s' % f.name)(rel_new_data[f.attname])
if change and was_changed: # if change and was_changed:
self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj)) # self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj))
#
# If, in the change stage, all of the core fields were blank and # # If, in the change stage, all of the core fields were blank and
# the primary key (ID) was provided, delete the item. # # the primary key (ID) was provided, delete the item.
if change and all_cores_blank and old_rel_obj: # if change and all_cores_blank and old_rel_obj:
new_rel_obj.delete(ignore_objects=[new_object]) # new_rel_obj.delete(ignore_objects=[new_object])
self.fields_deleted.append('%s "%s"' % (related.opts.verbose_name, old_rel_obj)) # self.fields_deleted.append('%s "%s"' % (related.opts.verbose_name, old_rel_obj))
# Save the order, if applicable. # Save the order, if applicable.
if change and opts.get_ordered_objects(): #if change and opts.get_ordered_objects():
order = new_data['order_'] and map(int, new_data['order_'].split(',')) or [] # order = new_data['order_'] and map(int, new_data['order_'].split(',')) or []
for rel_opts in opts.get_ordered_objects(): # for rel_opts in opts.get_ordered_objects():
getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order) # getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order)
return new_object
def get_related_objects(self): def get_related_objects(self):
return self.opts.get_followed_related_objects(self.follow) return self.opts.get_followed_related_objects(self.follow)

View File

@ -185,4 +185,19 @@ class DotExpandedDict(dict):
try: try:
current[bits[-1]] = v current[bits[-1]] = v
except TypeError: # Special-case if current isn't a dict. except TypeError: # Special-case if current isn't a dict.
current = {bits[-1]: v} current = {bits[-1] : v}
def dot_expand(key_to_list_mapping, dict_factory=dict):
top = dict_factory()
for k, v in key_to_list_mapping.items():
current = top
bits = k.split('.')
for bit in bits[:-1]:
current = current.setdefault(bit, dict_factory())
# Now assign value to current position
try:
current[bits[-1]] = v
except TypeError: # Special-case if current isn't a dict.
current = dict_factory( (bits[-1], v) )
return top