1
0
mirror of https://github.com/django/django.git synced 2025-06-05 03:29:12 +00:00

magic-removal: start of command functionality for manipulators. This will provide the fallback functionality for inline editing in the admin without JS.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1768 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-12-23 00:02:47 +00:00
parent a422575d4b
commit df36122320
2 changed files with 160 additions and 132 deletions

View File

@ -3,3 +3,6 @@ class FieldDoesNotExist(Exception):
class BadKeywordArguments(Exception): class BadKeywordArguments(Exception):
pass pass
class BadCommand(Exception):
pass

View File

@ -3,9 +3,11 @@ from django.core import formfields
from django.core.formfields import Manipulator from django.core.formfields import Manipulator
from django.db.models.fields import FileField, AutoField from django.db.models.fields import FileField, AutoField
from django.db.models.fields.related import ManyToOne from django.db.models.fields.related import ManyToOne
from django.db.models.exceptions import BadCommand
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.db.models import signals from django.db.models import signals
from django.utils.functional import curry from django.utils.functional import curry
from django.utils.datastructures import dot_expand, MultiValueDict
import types import types
@ -39,6 +41,34 @@ class ManipulatorDescriptor(object):
self.man._prepare(type) self.man._prepare(type)
return self.man return self.man
class ManipulatorHelper(object):
def __init__(self, manip, related_collection):
self.manip = manip
self.related_collection = related_collection
class FillHelper(ManipulatorHelper):
def matched_item(self,child_manip, obj_data):
child_manip._fill_data(obj_data)
def missing_item(self, index, child_manip):
self.related_collection[index] = None
def new_item(self):
child_manip = self.manip.model.AddManipulator()
self.related_collection.append(child_manip)
child_manip._fill_data(obj_data)
class SaveHelper(ManipulatorHelper):
def matched_item(self, index, child_manip, obj_data):
child_manip._save_expanded(obj_data)
def missing_item(self, index, child_manip):
child_manip.manip.original_object.delete(ignore_objects=[parent.original_object])
def new_item(self, index, obj_data):
child_manip = self.manip.model.AddManipulator()
child_manip._save_expanded(obj_data)
class AutomaticManipulator(Manipulator): class AutomaticManipulator(Manipulator):
def _prepare(cls, model): def _prepare(cls, model):
cls.model = model cls.model = model
@ -73,47 +103,120 @@ class AutomaticManipulator(Manipulator):
#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)
if manipulators: if manipulators:
self.children[f.var_name] = manipulators self.children[f] = manipulators
def get_fields(self): def get_fields(self):
l = list(self.fields_) l = list(self.fields_)
for child_manips in self.children.values(): for child_manips in self.children.values():
for manip in child_manips: for manip in child_manips:
l.extend(manip.fields) if manip:
l.extend(manip.fields)
return l return l
fields = property(get_fields) fields = property(get_fields)
def get_parameters(self, new_data): def get_original_value(self, field):
raise NotImplementedError
def get_new_object(self, expanded_data):
params = {} params = {}
for f in self.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 = self.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(expanded_data)
else: else:
if self.change: param = self.get_original_value(f)
param = getattr(self.original_object, f.attname)
else:
param = f.get_default()
params[f.attname] = param params[f.attname] = param
if self.change: if self.change:
params[self.opts.pk.attname] = self.obj_key params[self.opts.pk.attname] = self.obj_key
return params return self.model(**params)
def save(self, new_data, expanded=False ): def _fill_related_objects(self, expanded_data, helper_factory):
for related, manips in self.children.items():
helper = helper_factory(self, related)
child_data = expanded_data[related.var_name]
# existing objects
for index,manip in enumerate(manips):
obj_data = child_data.get(str(index), None)
child_data.pop(str(index) )
if obj_data != None:
#the object has new data
helper.matched_item(index,manip, obj_data )
else:
#the object was not in the data
helper.missing_item(index,manip)
if child_data:
# There are new objects in the data
for index, obj_data in child_data:
helper.new_item(obj_data)
def _fill_data(self, expanded_data):
self.original_object = self.get_new_object()
# TODO: many_to_many
self._fill_related_objects(expanded_data,FillHelper)
def do_command(self, new_data, command):
expanded_data = dot_expand(new_data, MultiValueDict)
# Deal with the effects of previous commands
self.fill_data(expanded_data)
# Do this command
command_parts = command.split('.')
self._do_command_expanded(self, expanded_data, command_parts)
def _do_command_expanded(self, expanded_data, command_parts):
part = command_parts.pop(0, None)
if part == None:
raise BadCommand, "Not enough parts in command"
# must be the name of a child manipulator collection
child_manips = None
related = None
for rel,manips in self.children:
if rel.var_name == part:
related = rel
child_manips = manips
break
if child_manips == None:
raise BadCommand, "%s : unknown manipulator collection name." % (part,)
child_data = expanded_data.get(part, None)
if child_data == None:
raise BadCommand, "%s : could not find data for manipulator collection." % (part,)
# The next part could be an index of a manipulator,
# or it could be a command on the collection.
index_part = command_parts.pop(0)
try:
index = int(index_part)
manip = child_manips.get(index, None)
if manip == None:
raise BadCommand, "No %s manipulator found for index %s in command." % (part, index)
if command_parts == ["delete"]:
child_manips[index] = None
else:
manip._do_command_expanded(expanded_data,command_parts)
except ValueError:
# Must be a command on the collection. Possible commands:
# add.
if index_part == "add":
child_manips.append(related.model.AddManipulator())
def save(self, new_data):
expanded_data = dot_expand(new_data,MultiValueDict)
return self._save_expanded(expanded_data)
def _save_expanded(self, expanded_data):
add, change, opts, klass = self.add, self.change, self.opts, self.model add, change, opts, klass = self.add, self.change, self.opts, self.model
if not expanded:
from django.utils.datastructures import dot_expand, MultiValueDict new_object = self.get_new_object(expanded_data)
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.save() new_object.save()
# Now that the object's been saved, save any uploaded files. # Now that the object's been saved, save any uploaded files.
@ -122,8 +225,8 @@ class AutomaticManipulator(Manipulator):
f.save_file(new_data, new_object, change and self.original_object or None, change) 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:
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)
@ -139,120 +242,34 @@ class AutomaticManipulator(Manipulator):
was_changed = getattr(new_object, 'set_%s' % f.name)(new_vals) was_changed = getattr(new_object, 'set_%s' % f.name)(new_vals)
if change and was_changed: if change and was_changed:
self.fields_changed.append(f.verbose_name) self.fields_changed.append(f.verbose_name)
for name, manips in self.children.items(): # Save inline edited objects
child_data = expanded_data[name] self._fill_related_objects(expanded_data,SaveHelper)
#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 return new_object
# Save many-to-one objects. Example: Add the Choice objects for a Poll. # for related, manips in self.children.items():
# for related in opts.get_all_related_objects(): # child_data = expanded_data[related.var_name]
# Create obj_list, which is a DotExpandedDict such as this: # #print "with child:", name
# [('0', {'id': ['940'], 'choice': ['This is the first choice']}), # # Apply changes to existing objects
# ('1', {'id': ['941'], 'choice': ['This is the second choice']}), # for index,manip in enumerate(manips):
# ('2', {'id': [''], 'choice': ['']})] # obj_data = child_data.get(str(index), None)
# child_follow = self.follow.get(related.name, None) # child_data.pop(str(index) )
# if obj_data != None:
# if child_follow: # #save the object with the new data
# obj_list = expanded_data[related.var_name].items() # #print "saving child data:", obj_data
# obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0]))) # manip._save_expanded(obj_data)
# # else:
# # For each related item... # #delete the object as it was not in the data
# for null, rel_new_data in obj_list: # manip.original_object.delete(ignore_objects=[self])
# # #print "deleting child object:", manip.original_original
# params = {} # if child_data:
# # # There are new objects in the data, so
# # Keep track of which core=True fields were provided. # # add them.
# # If all core fields were given, the related object will be saved. # for index, obj_data in child_data:
# # If none of the core fields were given, the object will be deleted. # manip = related.model.AddManipulator()
# # If some, but not all, of the fields were given, the validator would # manip._save_expanded(obj_data)
# # have caught that. # #print "new data to be added:", child_data
# all_cores_given, all_cores_blank = True, True
#
# # 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_rel_obj = None
# if change:
# if rel_new_data[related.opts.pk.name][0]:
# 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]})
# except ObjectDoesNotExist:
# pass
#
# 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, ''):
# 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, ''):
# all_cores_blank = False
# # If this field isn't editable, give it the same value it had
# # previously, according to the given ID. If the ID wasn't
# # given, use a default value. FileFields are also a special
# # case, because they'll be dealt with later.
#
# if f == related.field:
# param = getattr(new_object, related.field.rel.field_name)
# elif add and isinstance(f, AutoField):
# param = None
# elif change and (isinstance(f, FileField) or not child_follow.get(f.name, None)):
# if old_rel_obj:
# param = getattr(old_rel_obj, f.column)
# else:
# param = f.get_default()
# else:
# param = f.get_manipulator_new_data(rel_new_data, rel=True)
# if param != None:
# params[f.attname] = param
#
# # Create the related item.
# new_rel_obj = related.model(**params)
#
# # If all the core fields were provided (non-empty), save the item.
# if all_cores_given:
# new_rel_obj.save()
#
# # Save any uploaded files.
# for f in related.opts.fields:
# if child_follow.get(f.name, None):
# 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)
#
# # Calculate whether any fields have changed.
# if change:
# if not old_rel_obj: # This object didn't exist before.
# self.fields_added.append('%s "%s"' % (related.opts.verbose_name, new_rel_obj))
# else:
# 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)):
# self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj))
#
# # Save many-to-many objects.
# for f in related.opts.many_to_many:
# 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])
# if change and was_changed:
# 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
# # the primary key (ID) was provided, delete the item.
# if change and all_cores_blank and old_rel_obj:
# new_rel_obj.delete(ignore_objects=[new_object])
# 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():
@ -277,6 +294,9 @@ class ModelAddManipulator(AutomaticManipulator):
def __init__(self, follow=None, name_prefix=''): def __init__(self, follow=None, name_prefix=''):
super(ModelAddManipulator, self).__init__(follow=follow, name_prefix=name_prefix) super(ModelAddManipulator, self).__init__(follow=follow, name_prefix=name_prefix)
def get_original_value(self, field):
return field.get_default()
class ModelChangeManipulator(AutomaticManipulator): class ModelChangeManipulator(AutomaticManipulator):
change = True change = True
add = False add = False
@ -314,6 +334,11 @@ class ModelChangeManipulator(AutomaticManipulator):
if self.opts.get_ordered_objects(): if self.opts.get_ordered_objects():
self.fields.append(formfields.CommaSeparatedIntegerField(field_name="order_")) self.fields.append(formfields.CommaSeparatedIntegerField(field_name="order_"))
self.fields_added, self.fields_changed, self.fields_deleted = [], [], []
def get_original_value(self, field):
return getattr(self.original_object, field.attname)
def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data): def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data):
from django.utils.text import get_text_list from django.utils.text import get_text_list
field_list = [opts.get_field(field_name) for field_name in field_name_list] field_list = [opts.get_field(field_name) for field_name in field_name_list]