diff --git a/django/contrib/admin/views/stages/add.py b/django/contrib/admin/views/stages/add.py
index 182b0c5ac3..92a1d4b321 100644
--- a/django/contrib/admin/views/stages/add.py
+++ b/django/contrib/admin/views/stages/add.py
@@ -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('' % \
- (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('' % \
+ (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,
diff --git a/django/contrib/admin/views/stages/change.py b/django/contrib/admin/views/stages/change.py
index 233e21d57d..c0cb8b8636 100644
--- a/django/contrib/admin/views/stages/change.py
+++ b/django/contrib/admin/views/stages/change.py
@@ -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 = []
diff --git a/django/db/models/manipulators.py b/django/db/models/manipulators.py
index 1b77de4e25..53119511e7 100644
--- a/django/db/models/manipulators.py
+++ b/django/db/models/manipulators.py
@@ -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 "" % (self.model)
+ return "" % (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 "" % (self.model, self.obj_key)
+ return "" % (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:
diff --git a/django/db/models/related.py b/django/db/models/related.py
index f24c0620fa..f2f9005243 100644
--- a/django/db/models/related.py
+++ b/django/db/models/related.py
@@ -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)