Code


Version 6 (modified by hugo <gb@…>, 9 years ago) (diff)

--

CookBook - Manipulator With Hidden Fields

This is totally insecure, n'est-ce pas? A user can fiddle with hidden form fields - in fact, if you get the developer extensions for Mozilla Firefox, it's very easy to do. In this case you could then spoof the user doing the post. A better solution would be as above, but instead of pre-populating data into newData, you override the manipulator constructor to allow it to take parameters which you want to fix (such as the user), which it stores for later use. Then you override its save() method, which enforces those stored values (e.g. by changing the copy of the posted back data) and then calls the base save() method. (I haven't tried this, but I can't see a problem)

This is absolutely insecure. The better way indeed would be to populate parts of the POST data to overwrite those fields you know about - see CookBookManipulatorWithPostpopulatedFields. I am using that in a project myself and it works fine. Passing security relevant data through hidden fields is just a horrible idea, don't do that at home, kids'' -- hugo

Description

I needed to present a form to allow a user to create a new object. However, some fields in the object were already known by the system (such as the user who was adding the object). Rather than presenting these fields to the user, I created a custom manipulator that allowed me to hide them. The example will be a user creating a new message.

Code

Here's a simple Message model as an example:

class Message(meta.Model):
    title = meta.CharField(maxlength=255)
    body = meta.TextField()
    postDate = meta.DateTimeField(auto_now_add=True)
    user = meta.ForeignKey(auth.User)

Here's the custom manipulator that allows us to hide the user and project fields. It inherits from the model's pre-made AddManipulator, the iterates over the fields replacing some of them with a formfields.HiddenField

class MessageManipulator(messages.AddManipulator):
    def __init__(self):
        # Set everything up initially using the built in manipulator
        messages.AddManipulator.__init__(self)
        newFields = []
        # Iterate over all the fields looking for the ones I want to hide,
        # and replacing them with HiddenFields
        for field in self.fields:
            if field.field_name in ('user',):
                field = formfields.HiddenField(field.field_name,
                                               field.is_required,
                                               field.validator_list)
            newFields.append(field)
        self.fields = newFields

Here's the view code:

def addMessage(request):
    manipulator = MessageManipulator()
    if request.POST:
        newData = request.POST.copy()
        errors = manipulator.get_validation_errors(newData)
        if not errors:
            manipulator.do_html2python(newData)
            newMessage = manipulator.save(newData)
            return HttpResponseRedirect(newMessage.get_absolute_url())
    else:
        errors = {}
        # Add the the data that you already know and don't want to burden
        # the user with here.
        newData = {'user': request.user.id,}
    form = formfields.FormWrapper(manipulator, newData, errors)
    t = template_loader.get_template('addMessage')
    c = Context(request, {'form': form,})
    return HttpResponse(t.render(c))

Here's part of the template code. {{ form.user_id }} places the hidden field in the form. It will be populated with the correct data because we set that in newData above. Title and Body are other fields defined in the Message model. messages.AddManipulator took care of populating those correctly.

<form method="post" action=".">
{{ form.user }}
<p>
    <label for="id_title">Title:</label> {{ form.title }}
    {% if form.title.errors %}*** {{ form.title.errors|join:", " }}{% endif %}
</p>
<p>
    <label for="id_body">Body:</label> {{ form.body }}
    {% if form.body.errors %}*** {{ form.body.errors|join:", " }}{% endif %}
</p>

And the HTML generated from this template:

<form method="post" action=".">
<input type="hidden" id="id_user" name="user" value="2" />
<p>
    <label for="id_title">Title:</label> <input type="text" id="id_title"
class="vTextField required" name="title" size="30" value="" maxlength="255" />
</p>
<p>
    <label for="id_body">Body:</label> <textarea id="id_body"
class="vLargeTextField required" name="body" rows="10" cols="40"></textarea>
</p>

Enjoy!