23

Here's the way I'm doing it:

{{ formset.management_form }}
<table>
    {% for form in formset.forms %}
        {{ form }}
    {% endfor %}
</table>
<a href="javascript:void(0)" id="add_form">Add Form</a>   

And here's the JS:

var form_count = {{formset.total_form_count}};
$('#add_form').click(function() {
    form_count++;
    var form = '{{formset.empty_form|escapejs}}'.replace(/__prefix__/g, form_count);
    $('#forms').append(form)
    $('#id_form-TOTAL_FORMS').val(form_count);
});

What specifically bothers me is that I had to write that escapejs template tag myself. It just strips all newlines and escapes any single quotes so that it doesn't mess up my string. But what exactly did the Django makers expect us to do in this situation? And why do they have this TOTAL_FORMS hidden field, when they could have just used an array like <input name="my_form_field[0]" /> and then counted its length instead?

8
  • 1
    But why are you mixing up django template and javascript? Commented Mar 1, 2010 at 4:38
  • 1
    I like the usage of .empty_form - nice and short. Thanks! Commented May 24, 2011 at 1:09
  • 2
    This solution IMO is far better than the typical clone method generally used. - Works great with forms that have javascript such as django-CKeditor - Works when there aren't any initial forms too - Handles initial data One point, escapejs is a built in tag, maybe it wasn't when this was written. Also for those who don't just want the basic form, you can do formset.empty_form.as_custom|escapejs where as_custom is a form function that returns a rendered template. Commented Jul 25, 2011 at 16:14
  • 1
    Your description of escapejs doesn't property escape the string "</script>", if this JS appears inside a script tag. Commented Feb 14, 2012 at 14:21
  • 1
    __prefix__ must be replaced with form_count - 1. It starts with 0, not 1. Commented Dec 28, 2014 at 12:58

5 Answers 5

8

There are a few places in Django where "the reason why" is because that's how it was implemented for the Django admin app, and I believe this is one of them. Thus the answer is they expect you to implement your own javascript.

See this SO question Dynamically adding a form... for some more javascript ideas.

There are also two pluggable apps available, django-dynamic-formset and django-dinamyc-form which I hadn't seen until just now when looking up the first one.

Sign up to request clarification or add additional context in comments.

4 Comments

Writing my own JS isn't really a problem... I'm just curious why they would only gave us half the tools we need.... they say the empty_form can be used for this, yet it prints with line-breaks and such which isn't very friendly to work with when trying to stuff it into JS, unless I'm misunderstanding something. I think I like my method of dynamic formset better... I suspect it puts less strain on the client, and requires less code :) Oh well... I guess I'm just nitpicking, but I still really disagree with a lot of Django's design decisions.
Well Django proper design decision is to remain JS-framework neutral. The admin is a contrib app and so technically isn't part of Django core, although some functionality such as formsets (apparently) came from the work on the admin and, while I think the admin is a fine app, it probably does suffer somewhat from lack of a grand design.
Formsets are a huge pain in the butt if you want to do anything with ajax or to dynamically add new objects. By far the biggest problem with Django when trying to design a Web 2.0 type app. We almost switched to a Java framework after having 6 months invested in Django for this very reason, but decided we'd spent too much time.
can you help me stackoverflow.com/questions/62285767/… , i've tried alot but didnt get an answer ! i much appreciate you
4

This question is a bit old, but it took me a while to figure this out as well.

I suggest rendering formset.empty_form in your template as a hidden field, and referencing this field in your javascript.

Here's a complicated dynamic formset example from the django admin site: (but note that it has not been updated to use empty_form....)

[js] http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/media/js/inlines.js

[html template] http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/templates/admin/edit_inline/tabular.html

2 Comments

I recently (a few hours ago actually) redid this... it's really nasty to work with.. to try and add and remove forms, and update all the IDs scattered about, and ensure there's at least one form, and to validate them all properly... combine that with ajax/cascading/contingent select drop downs and it becomes a miniature nightmare.
Did you work off of the django inlines.js example? I modified it for my site (wanted the add buttons in a separate list). Wasn't trivial (i'm new to js, so it took a while), but I think I managed to contain most of the logic in a single independent script.
2

It's because formset have been created to work without javascript, using only the usual HTTP workflow.

Django is javascript agnostic.

If you want to add some javascript to the mix, you can use the dedicated jquery plugin.

10 Comments

Wish I could see a demo, but thanks. Django is designed to work without JavaScript, yes, but it seems to have some minor hooks to build JavaScript on....I just wish they took it further, because it's painful.
@Mark: Actually there is a demo :-) Download the project at code.google.com/p/django-dynamic-formset it includes a django demo.
Sure, a guy is spending a lot of times to solve a complicated problem, make it open source, create a django project you can test and install in 5 second and make your life incredibly easier for free. But I agree, he is not trying hard enough, you deserve better, you have the right to complain.
That's not what I'm saying. You have to see it from both sides. I'm sure he's done a wonderful job, and he's doing a great thing by sharing it with the world, and if he doesn't want to spend a bit more time to promote it, that's his call, and I won't hold it against him. I'm just saying that without a demo a lot of people are going to gloss over it, because there are plenty of alternatives (maybe not so much in this specific case) that have nice flashy demos that prove they work and you can quickly and easily determine if they're a fit for you.
And it's not 5 seconds when you're trying to evaluate a couple dozen alternatives. It's a big time investment to compare numerous programs or software libraries. The faster you can rule them out, the better (for you...maybe not the authors). Again, this is not to say I don't appreciate this guy's effort. It very well could be exactly what I was looking for.
|
1

in my case. i used the plugin django-dynamic-formset (https://code.google.com/p/django-dynamic-formset/wiki/Usage)

and modified the option "added" and worked good.

        $('#formset-table tbody tr').formset({
            prefix: '{{ formset.prefix }}',
            formCssClass: '{{ formset.prefix }}-inlineformset',
            added: function(obj_tr){ 
                var form = $(obj_tr).html().replace(/\-(\w+)\-(\w+)(fix__)\-/g, '-');
                $(obj_tr).html(form);

           },

this regular expression replace the string [prefix]-prefix peer '-'

maybe isn't the best solution, but worked.

Comments

1

There are some cases of possible XSS when using formset.empty_form as a string, replacing '__prefix__' to actual formset form index. My pluggable application converts formset.empty_form into Knockout.js template which is then cloned via custom Knockout.js bindings. Also Knockout.js automatically re-calculates form field id indexes, when newly added formset form is dynamically deleted before the whole form with inlineformsets was submitted. Here is the documentation:

https://django-jinja-knockout.readthedocs.org/en/latest/forms.html#dynamically-adding-new-related-formset-forms

Knockout.js binding also prevents XSS when loading custom fields with inline Javascript.

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.