Code


Version 3 (modified by akaihola, 8 years ago) (diff)

added link to google groups discussion

CookBook - Using Manipulators With Only Some Fields

Description

If your template should only edit part of an object because some fields are not to be changed by the user, you can use the following view-function-template to do the job. It just populates those fields that are left out of the template and the form and fills in there values from known content. Especially it overwrites fields in the new_data with known values so that users can't just fake POST requests to populate left-out fields.

The code is stolen from my gallery project - there you have a picturefolder that has a path and a slug that point to the filesystem and a user_id that denotes the owner (actually it's a ForeignKey to the auth.users model). So users should never be able to change the owner or change the filesystem path (otherwise they might be able to fetch files from security relevant parts of the filesystem). The following code prevents changing those fields by overwriting them in the POST data with data from the original object. That way even faked POST requests won't get access to those attributes.

Note: see also this discussion for a different approach using an undocumented Django feature.

Code

def update_folder(request, folder):
  # this just resolves a object from it's slug - you will most definitely
  # need to change this to your own model
  try: f = picturefolders.get_object(slug__exact=folder)
  except picturefolders.PicturefolderDoesNotExist: raise Http404

  # now just fetch a standard change manipulator for that object
  manipulator = picturefolders.ChangeManipulator(f.id)
  if request.POST:
    new_data = request.POST.copy()

    # here I am filling in fields from the fetched object
    # to make sure that the user can't pass in security relevant
    # data. Make sure that you turn your values into strings,
    # as that's expected by the later html2python call.
    new_data['path'] = f.path
    new_data['slug'] = f.slug
    new_data['user'] = str(f.user_id)

    # the rest of this is pretty standard as we now have a
    # fully populated POST data dict
    errors = manipulator.get_validation_errors(new_data)
    if not errors:
      manipulator.do_html2python(new_data)
      manipulator.save(new_data)
      return HttpResponseRedirect(request.path)
    else:
      new_data = f.__dict__
      errors = {}
    form = formfields.FormWrapper(manipulator, new_data, errors)
    t = template_loader.get_template('picturefolder_form')
    c = Context(request, {
      'form': form,
      'folder': f,
    })
    return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8')
  else:
    raise Http404