magic-removal: changed how child manipulators are stored in preparation for reordering and paging.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1777 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-12-24 17:54:37 +00:00
parent 5c0fe75d44
commit 2466d9b0f1
4 changed files with 240 additions and 262 deletions

View File

@ -27,53 +27,45 @@ def add_stage(request, path, show_delete=False, form_url='', post_url='../', pos
new_data = request.POST.copy()
if opts.has_field_type(models.FileField):
new_data.update(request.FILES)
if request.POST.has_key("command"):
#save a copy of the data to use for errors later.
data = new_data.copy()
manipulator.do_html2python(new_data)
#save a copy of the data to use for errors later.
data = new_data.copy()
manipulator.do_html2python(new_data)
#update the manipulator with the effects of previous commands.
manipulator.update(new_data)
#get the errors on the updated shape of the manipulator
#HACK - validators should not work on POSTED data directly...
errors = manipulator.get_validation_errors(data)
if request.POST.has_key("_preview"):
pass
elif request.POST.has_key("command"):
command_name = request.POST.get("command")
manipulator.do_command(new_data, command_name)
manipulator.do_command(command_name)
new_data = manipulator.flatten_data()
elif errors:
new_data = manipulator.flatten_data()
#HACK - validators should not work on POSTED data directly...
errors = manipulator.get_validation_errors(data)
elif request.POST.has_key("_preview"):
errors = manipulator.get_validation_errors(new_data)
manipulator.do_html2python(new_data)
else:
#save a copy of the data to use for errors later.
data = new_data.copy()
manipulator.do_html2python(new_data)
manipulator.update(new_data)
errors = manipulator.get_validation_errors(data)
if errors:
data = manipulator.flatten_data()
data.update(new_data)
new_data = data
else:
new_object = manipulator.save_from_update()
log_add_message(request.user, opts, manipulator, new_object)
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object}
pk_value = getattr(new_object, opts.pk.attname)
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
if request.POST.has_key("_continue"):
request.user.add_message(msg + ' ' + _("You may edit it again below."))
if request.POST.has_key("_popup"):
post_url_continue += "?_popup=1"
return HttpResponseRedirect(post_url_continue % pk_value)
new_object = manipulator.save_from_update()
log_add_message(request.user, opts, manipulator, new_object)
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object}
pk_value = getattr(new_object, opts.pk.attname)
# Here, we distinguish between different save types by checking for
# the presence of keys in request.POST.
if request.POST.has_key("_continue"):
request.user.add_message(msg + ' ' + _("You may edit it again below."))
if request.POST.has_key("_popup"):
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \
(pk_value, repr(new_object).replace('"', '\\"')))
elif request.POST.has_key("_addanother"):
request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
return HttpResponseRedirect(request.path)
else:
request.user.add_message(msg)
return HttpResponseRedirect(post_url)
post_url_continue += "?_popup=1"
return HttpResponseRedirect(post_url_continue % pk_value)
if request.POST.has_key("_popup"):
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \
(pk_value, repr(new_object).replace('"', '\\"')))
elif request.POST.has_key("_addanother"):
request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
return HttpResponseRedirect(request.path)
else:
request.user.add_message(msg)
return HttpResponseRedirect(post_url)
else:
# Add default data.
new_data = manipulator.flatten_data()
@ -84,7 +76,7 @@ def add_stage(request, path, show_delete=False, form_url='', post_url='../', pos
errors = {}
# Populate the FormWrapper.
form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline=True)
form = formfields.FormWrapper(manipulator, new_data, errors)
c = Context(request, {
'title': _('Add %s') % opts.verbose_name,

View File

@ -46,59 +46,51 @@ def change_stage(request, path, object_id):
new_data = request.POST.copy()
if opts.has_field_type(models.FileField):
new_data.update(request.FILES)
if request.POST.has_key("command"):
#save a copy of the data to use for errors later.
data = new_data.copy()
manipulator.do_html2python(new_data)
#save a copy of the data to use for errors later.
data = new_data.copy()
manipulator.do_html2python(new_data)
#update the manipulator with the effects of previous commands.
manipulator.update(new_data)
#get the errors on the updated shape of the manipulator
#HACK - validators should not work on POSTED data directly...
errors = manipulator.get_validation_errors(data)
if request.POST.has_key("_preview"):
pass
elif request.POST.has_key("command"):
command_name = request.POST.get("command")
manipulator.do_command(new_data, command_name)
manipulator.do_command(command_name)
new_data = manipulator.flatten_data()
elif errors:
new_data = manipulator.flatten_data()
#HACK - validators should not work on POSTED data directly...
errors = manipulator.get_validation_errors(data)
elif request.POST.has_key("_preview"):
errors = manipulator.get_validation_errors(new_data)
manipulator.do_html2python(new_data)
else:
#save a copy of the data to use for errors later.
data = new_data.copy()
manipulator.do_html2python(new_data)
manipulator.update(new_data)
errors = manipulator.get_validation_errors(data)
if errors:
flat_data = manipulator.flatten_data()
flat_data.update(new_data)
new_data = flat_data
else:
new_object = manipulator.save_from_update()
log_change_message(request.user, opts, manipulator, new_object)
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object}
pk_value = getattr(new_object, opts.pk.attname)
if request.POST.has_key("_continue"):
request.user.add_message(msg + ' ' + _("You may edit it again below."))
if request.REQUEST.has_key('_popup'):
return HttpResponseRedirect(request.path + "?_popup=1")
else:
return HttpResponseRedirect(request.path)
elif request.POST.has_key("_saveasnew"):
request.user.add_message(_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object})
return HttpResponseRedirect("../../%s/" % pk_value)
elif request.POST.has_key("_addanother"):
request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
return HttpResponseRedirect("../../add/")
new_object = manipulator.save_from_update()
log_change_message(request.user, opts, manipulator, new_object)
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object}
pk_value = getattr(new_object, opts.pk.attname)
if request.POST.has_key("_continue"):
request.user.add_message(msg + ' ' + _("You may edit it again below."))
if request.REQUEST.has_key('_popup'):
return HttpResponseRedirect(request.path + "?_popup=1")
else:
request.user.add_message(msg)
return HttpResponseRedirect("../../")
return HttpResponseRedirect(request.path)
elif request.POST.has_key("_saveasnew"):
request.user.add_message(_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object})
return HttpResponseRedirect("../../%s/" % pk_value)
elif request.POST.has_key("_addanother"):
request.user.add_message(msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
return HttpResponseRedirect("../../add/")
else:
request.user.add_message(msg)
return HttpResponseRedirect("../../")
else:
# Populate new_data with a "flattened" version of the current data.
new_data = manipulator.flatten_data()
errors = {}
# Populate the FormWrapper.
form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline = True)
form = formfields.FormWrapper(manipulator, new_data, errors)
form.original = manipulator.original_object
form.order_objects = []

View File

@ -2,7 +2,7 @@ from django.core.exceptions import ObjectDoesNotExist
from django.core import formfields
from django.core.formfields import Manipulator
from django.db.models.fields import FileField, AutoField
from django.db.models.fields.related import ManyToOne
from django.db.models.exceptions import BadCommand
from django.dispatch import dispatcher
from django.db.models import signals
@ -41,35 +41,6 @@ class ManipulatorDescriptor(object):
self.man._prepare(type)
return self.man
class ManipulatorHelper(object):
def __init__(self, manip, related, child_manipulators):
self.manip = manip
self.related = related
self.child_manipulators = child_manipulators
class FillHelper(ManipulatorHelper):
def matched_item(self,index, child_manip, obj_data):
child_manip._fill_data(obj_data)
def missing_item(self, index, child_manip):
child_manip.needs_deletion = True
def new_item(self, obj_data):
child_manip = self.manip._add_manipulator_for_child(self.related)
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.original_object.delete(ignore_objects=[parent.original_object])
def new_item(self, obj_data):
child_manip = self.manip._add_manipulator_for_child(self.related)
overrides = { self.related.field : self.manip.obj_key }
child_manip._save_expanded(obj_data, overrides)
class AutomaticManipulator(Manipulator):
def _prepare(cls, model):
cls.model = model
@ -122,7 +93,7 @@ class AutomaticManipulator(Manipulator):
fields = property(get_fields)
def get_original_value(self, field):
raise NotImplementedError
raise NotImplemented
def get_new_object(self, expanded_data, overrides=None):
params = {}
@ -143,167 +114,110 @@ class AutomaticManipulator(Manipulator):
params[self.opts.pk.attname] = self.obj_key
return self.model(**params)
def _fill_related_objects(self, expanded_data, helper_factory):
for related, manips in self.children.items():
helper = helper_factory(self, related, manips)
child_data = MultiValueDict(expanded_data.get(related.var_name, MultiValueDict()) )
# existing objects
for index,manip in enumerate(manips):
obj_data = child_data.get(str(index), None)
child_data.pop(str(index), None)
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
items = sorted(child_data.items(),cmp = lambda x, y: cmp(x[0], y[0]))
for index, obj_data in items:
helper.new_item(obj_data)
def _fill_data(self, expanded_data):
if self.needs_deletion:
raise BadCommand, "Filling %s with %r when it needs deletion" % (self, expanded_data)
self.original_object = self.get_new_object(expanded_data)
# TODO: many_to_many
self._fill_related_objects(expanded_data,FillHelper)
for related, manips in self.children.items():
child_data = MultiValueDict(expanded_data.get(related.var_name, MultiValueDict()) )
manips._fill_data(child_data)
def update(self, new_data):
expanded_data = dot_expand(new_data, MultiValueDict)
# Deal with the effects of previous commands
self._fill_data(expanded_data)
def save_from_update(self):
def save_from_update(self, parent_key=None):
if self.needs_deletion:
if self.original_object != None:
self.original_object.delete()
return
return
# TODO: many to many
self.original_object.save()
if not hasattr(self, 'obj_key'):
self.obj_key = getattr(self.original_object, self.opts.pk.attname)
for related, manips in self.children.items():
for i, manip in enumerate(manips):
setattr(manip.original_object, related.field.attname , self.obj_key)
manip.save_from_update()
manips.save_from_update(self.obj_key)
return self.original_object
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)
def do_command(self, command):
# Do this command
command_parts = command.split('.')
self._do_command_expanded(expanded_data, command_parts)
self._do_command_expanded(command_parts)
def _do_command_expanded(self, expanded_data, command_parts):
def _do_command_expanded(self, command_parts):
try:
part = command_parts.pop(0)
except IndexError:
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.items():
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, MultiValueDict())
# The next part could be an index of a manipulator,
# or it could be a command on the collection.
try:
index_part = command_parts.pop(0)
except IndexError:
raise BadCommand, "Not enough parts in command"
try:
index = int(index_part)
manip = child_manips[index]
if manip == None:
raise BadCommand, "No %s manipulator found for index %s in command." % (part, index)
obj_data = child_data.get(index_part, None)
if obj_data == None:
raise BadCommand, "Could not find data for manipulator %s in %s collection." % (index, part)
if command_parts == ["delete"]:
child_manips[index] = None
if part == "delete":
self.needs_deletion = True
else:
# must be the name of a child manipulator collection
child_manips = None
for rel,manips in self.children.items():
if rel.var_name == part:
child_manips = manips
break
if child_manips == None:
raise BadCommand, "'%s' : unknown manipulator collection name." % (part,)
else:
manip._do_command_expanded(obj_data,command_parts)
except ValueError:
# Must be a command on the collection. Possible commands:
# add.
if index_part == "add":
self._add_manipulator_for_child(related)
else:
raise BadCommand, "%s, unknown command" % (part)
def _add_manipulator_for_child(self, related):
child_manips = self.children.setdefault(related, [])
fol = self.follow[related.name]
prefix = "%s%s.%s." % (self.name_prefix, related.var_name, len(child_manips) )
child_manip = related.model.AddManipulator(follow=fol,name_prefix=prefix)
child_manips.append(child_manip)
return child_manip
child_manips._do_command_expanded(command_parts)
def save(self, new_data):
expanded_data = dot_expand(new_data,MultiValueDict)
return self._save_expanded(expanded_data)
self.update(new_data)
self.save_from_update()
def _save_expanded(self, expanded_data, overrides = None):
add, change, opts, klass = self.add, self.change, self.opts, self.model
new_object = self.get_new_object(expanded_data, overrides)
# First, save the basic object itself.
new_object.save()
# Save the key for use in creating new related objects.
if not hasattr(self, 'obj_key'):
self.obj_key = getattr(new_object, self.opts.pk.attname)
# Now that the object's been saved, save any uploaded files.
for f in opts.fields:
if isinstance(f, FileField):
f.save_file(new_data, new_object, change and self.original_object or None, change)
# Calculate which primary fields have changed.
# for f in opts.fields:
# 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)
# Save many-to-many objects. Example: Poll.set_sites()
for f in opts.many_to_many:
if self.follow.get(f.name, None):
if not f.rel.edit_inline:
if f.rel.raw_id_admin:
new_vals = new_data.get(f.name, ())
else:
new_vals = new_data.getlist(f.name)
was_changed = getattr(new_object, 'set_%s' % f.name)(new_vals)
if change and was_changed:
self.fields_changed.append(f.verbose_name)
# Save inline edited objects
self._fill_related_objects(expanded_data,SaveHelper)
return new_object
# Save the order, if applicable.
#if change and opts.get_ordered_objects():
# order = new_data['order_'] and map(int, new_data['order_'].split(',')) or []
# for rel_opts in opts.get_ordered_objects():
# getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order)
# def _save_expanded(self, expanded_data, overrides = None):
# add, change, opts, klass = self.add, self.change, self.opts, self.model
#
# new_object = self.get_new_object(expanded_data, overrides)
#
# # First, save the basic object itself.
# new_object.save()
#
# # Save the key for use in creating new related objects.
# if not hasattr(self, 'obj_key'):
# self.obj_key = getattr(new_object, self.opts.pk.attname)
#
# # Now that the object's been saved, save any uploaded files.
# for f in opts.fields:
# if isinstance(f, FileField):
# f.save_file(new_data, new_object, change and self.original_object or None, change)
#
# # Calculate which primary fields have changed.
#
#
# # for f in opts.fields:
# # 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)
#
# # Save many-to-many objects. Example: Poll.set_sites()
# for f in opts.many_to_many:
# if self.follow.get(f.name, None):
# if not f.rel.edit_inline:
# if f.rel.raw_id_admin:
# new_vals = new_data.get(f.name, ())
# else:
# new_vals = new_data.getlist(f.name)
# was_changed = getattr(new_object, 'set_%s' % f.name)(new_vals)
# if change and was_changed:
# self.fields_changed.append(f.verbose_name)
#
# # Save inline edited objects
# self._fill_related_objects(expanded_data,SaveHelper)
#
# return new_object
#
# # Save the order, if applicable.
# #if change and opts.get_ordered_objects():
# # order = new_data['order_'] and map(int, new_data['order_'].split(',')) or []
# # for rel_opts in opts.get_ordered_objects():
# # getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order)
def get_related_objects(self):
@ -334,7 +248,7 @@ class ModelAddManipulator(AutomaticManipulator):
return field.get_default()
def __repr__(self):
return "<Automatic AddManipulator for %s>" % (self.model)
return "<Automatic AddManipulator '%s' for %s>" % (self.name_prefix, self.model.__name__, )
class ModelChangeManipulator(AutomaticManipulator):
change = True
@ -380,9 +294,96 @@ class ModelChangeManipulator(AutomaticManipulator):
return getattr(self.original_object, field.attname)
def __repr__(self):
return "<Automatic ChangeManipulator for %s:%r >" % (self.model, self.obj_key)
return "<Automatic ChangeManipulator '%s' for %s:%r >" % (self.name_prefix, self.model.__name__, self.obj_key)
class ManipulatorCollection(list):
def __init__(self,related, parent_prefix, instance, follow):
self.related = related
self.name_prefix = '%s%s.' % ( parent_prefix, related.var_name)
self.follow = follow
self._load(instance)
def _load(self, instance):
man_class = self.related.model.ChangeManipulator
if instance != None:
meth_name = 'get_%s_list' % self.related.get_method_name_part()
list = getattr(instance, meth_name)()
else:
list = []
for i,obj in enumerate(list):
prefix = '%s%d.' % (self.name_prefix, i)
self.append(man_class(obj,self.follow, prefix) )
def save_from_update(self, parent_key):
for manip in self:
if manip:
setattr(manip.original_object, self.related.field.attname , parent_key)
manip.save_from_update()
def _fill_data(self, expanded_data):
for index,manip in enumerate(self):
obj_data = expanded_data.get(str(index), None)
expanded_data.pop(str(index), None)
if manip:
if obj_data != None:
#the object has new data
manip._fill_data(obj_data)
else:
#the object was not in the data
manip.needs_deletion = True
if expanded_data:
# There are new objects in the data
items = [ (int(k),v ) for k,v in expanded_data.items() ]
items.sort(cmp = lambda x, y: cmp(x[0], y[0]))
for index, obj_data in items:
child_manip = self.add_child(index)
child_manip._fill_data(obj_data)
def _do_command_expanded(self, command_parts):
# The next part could be an index of a manipulator,
# or it could be a command on the collection.
try:
index_part = command_parts.pop(0)
except IndexError:
raise BadCommand, "Not enough parts in command"
try:
index = int(index_part)
try:
manip = self[index]
except IndexError:
raise BadCommand, "No %s manipulator found for index %s in command." % (part, index)
if manip == None:
raise BadCommand, "No %s manipulator found for index %s in command." % (part, index)
manip._do_command_expanded(command_parts)
except ValueError:
# Must be a command on the collection. Possible commands:
# add.
# TODO: page.forward, page.back, page.n, swap.n.m
if index_part == "add":
self.add_child()
else:
raise BadCommand, "%s, unknown command" % (part)
def add_child(self, index = None):
man_class = self.related.model.AddManipulator
if index == None:
index = len(self)
# Make sure that we are going to put this in the right index, by prefilling with Nones.
for i in range(len(self), index + 1):
self.append(None)
prefix = '%s%s.' % (self.name_prefix, index )
child_manip = man_class(self.follow, prefix)
self[index] = child_manip
return child_manip
def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data):
from django.db.models.fields.related import ManyToOne
from django.utils.text import get_text_list
field_list = [opts.get_field(field_name) for field_name in field_name_list]
if isinstance(field_list[0].rel, ManyToOne):
@ -414,6 +415,7 @@ def manipulator_validator_unique_together(field_name_list, opts, self, field_dat
{'object': capfirst(opts.verbose_name), 'type': field_list[0].verbose_name, 'field': get_text_list(field_name_list[1:], 'and')}
def manipulator_validator_unique_for_date(from_field, date_field, opts, lookup_type, self, field_data, all_data):
from django.db.models.fields.related import ManyToOne
date_str = all_data.get(date_field.get_manipulator_field_names('')[0], None)
date_val = formfields.DateField.html2python(date_str)
if date_val is None:

View File

@ -1,3 +1,5 @@
from django.db.models.manipulators import ManipulatorCollection
class BoundRelatedObject(object):
def __init__(self, related_object, field_mapping, original):
self.relation = related_object
@ -78,20 +80,10 @@ class RelatedObject(object):
return ([], self.get_manipulators(manipulator, follow) )
def get_manipulators(self,parent_manipulator, follow):
name_prefix = parent_manipulator.name_prefix
obj = parent_manipulator.original_object
man_class = self.model.ChangeManipulator
if parent_manipulator.original_object:
meth_name = 'get_%s_list' % self.get_method_name_part()
list = getattr(parent_manipulator.original_object, meth_name)()
else:
list = []
manipulators = []
for i,obj in enumerate(list):
prefix = '%s%s.%d.' % (parent_manipulator.name_prefix, self.var_name, i)
manipulators.append(man_class(obj,follow, prefix) )
return manipulators
return ManipulatorCollection(self, name_prefix, obj, follow)
def bind(self, field_mapping, original, bound_related_object_class=BoundRelatedObject):
return bound_related_object_class(self, field_mapping, original)