3

I have a model form in which I need to store an unknown number of helpers alongside a thing. The names can be serialised upon save, and that's not a problem. It's being able to clean and validate them upon submission.

The form looks like;

class ThingForm(forms.ModelForm):
    """
    Form for the Thing
    """
    owner = forms.CharField()
    helpers = forms.CharField()

    class Meta:
        model = Thing

    def save(self, *args, **kwargs):
        """
        Serialize helpers to JSON.
        """
        ...

And the model is using a JSONField to store the serialised helpers.

class Thing(models.Model):
    owner = models.CharField()
    helpers = JSONField()

I have JavaScript adding as many helpers as required with the same input name:

<input name="helpers" value="Fred" />
<input name="helpers" value="Joe" />

Which is returning a tuple of the helpers. The problem is that the if the form isn't valid - those names will be lost and the cleaning isn't working.

My first thought was to add to the form's constructor:

    def __init__(self, *args, **kwargs):
        super(ThingForm, self).__init__(*args, **kwargs)
        try:
            helpers = args[0].pop('helpers')
            for name in helpers:
                # Add a charfield, or something?
        except:
            pass

But I'm not really getting anywhere...

3 Answers 3

1

Thanks to AdamKG for the answer to this. You can just use the list in your view again:

View:

if request.method == 'POST':
    helpers = request.POST.getlist('helpers')
    form = ThingForm(request.POST)
    if form.is_valid():
        form.save()
        return HttpResponseRedirect('/saved/')
else:
    helpers = None
    form = ThingForm()

return render_to_response('my_template.html',
    {'helpers': helpers, 'form': form},
    context_instance=RequestContext(request))

Template:

{% for field in form %}
    {% if field.name == 'helpers' %}
        {% for name in helpers %}
            <input name="helpers" value="{{ name }}" />
        {% endfor %}
    {% else %}
        {{ field }}
    {% endif %}
{% endfor %}
Sign up to request clarification or add additional context in comments.

Comments

0

I think all you need to do is do something like this in your template:

{% if form.data %}{# eg, was invalid and this is a re-render w/ errors #}
{% for helper in form.data.helpers %}
<input type="hidden" name="helpers" value="{{ helper }}">
{% endfor %}
{% endif %}

Note that this will break if you start passing a prefix kwarg to your form - but so would your original code, fixing that is a separate issue :)

6 Comments

The problem is that the extra helpers are cleaned by the form, so they don't exist by the time we get to the template.
Is this a clean method you added? It should still be in form.data, as long as you're passing the form bound with request.POST back to the template to render the errors.
No, the form is pretty vanilla now. If I use that iterator in the template and it just iterates the letters in the last input (there's only a string in form.data.helpers)
Ah, right. Just accessing the helpers key won't give you the list. You'll need to pass {'prev_helpers': request.POST.getlist("helpers")} as context to the template and iterate over that instead.
BTW - .cleaned_data gets totally cleared if there are any errors; it will only get populated if .is_valid() succeeds.
|
0

We had a similar problem and found no ready solution. So we did ours (https://github.com/vialink/vlk-django-jsonfield).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.