How would you make a dynamic formset in Django?
Asked Answered
L

5

22

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?

Lowther answered 1/3, 2010 at 3:19 Comment(8)
But why are you mixing up django template and javascript?Rubellite
So that I can process it. Otherwise I have to write completely custom form handling in the view too. And Django seems to encourage this method, that's why they provided this empty_form with __prefix__ so that you could replace it, supposedly.Lowther
I like the usage of .empty_form - nice and short. Thanks!Cakewalk
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.Lower
Your description of escapejs doesn't property escape the string "</script>", if this JS appears inside a script tag.Festschrift
@JesseRusak: Good point. I ran into that problem somewhat recently in another project. It's funny that </script> will break out of the script even when it's inside a string. Although, I believe different browsers will handle <scripts> differently when injected via JS (some will execute it, some won't).Lowther
__prefix__ must be replaced with form_count - 1. It starts with 0, not 1.Teofilateosinte
Here's one way to do it using pure javascript. Not optimal, but I guess it illustrates the principle.Anacreontic
E
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.

Ephah answered 1/3, 2010 at 18:56 Comment(4)
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.Lowther
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.Ephah
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.Rigamarole
can you help me #62286267 , i've tried alot but didnt get an answer ! i much appreciate youSelfabasement
H
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

Higbee answered 11/8, 2010 at 0:44 Comment(2)
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.Lowther
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.Higbee
H
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.

Hortative answered 4/1, 2012 at 19:55 Comment(10)
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.Lowther
@Mark: Actually there is a demo :-) Download the project at code.google.com/p/django-dynamic-formset it includes a django demo.Hortative
That's not what I meant by a demo. I meant without having to download it ("live demo"?). There's millions of projects and programs out there...people like to know what it is they're getting before they invest in the time and effort and download and extract things, and/or install/compile. Anyway, since you recommended it and it isn't just some random thing I stumbled upon, I'll check it out... (if it's such a wonderful library, you'd expect it would be in use somewhere that they could just link to...)Lowther
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.Hortative
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.Lowther
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.Lowther
In our case: I'm not the guy, so I don't know why you said that as a public comment on SO. Plus you don't have many alternatives. Actually besides running your own, you have none. And the guy is not doing a commercial product anyway, so he is not loosing anything when somebody misses it. Open source software is a hard, time consuming activity, and I can understand if he doesn't want to add a demo maintenance to his work load.Hortative
Said what? I was speaking in general, and then I clarified by saying that there may not be many alternatives "in this specific case" -- i.e., the case of a dynamic django formset. Usually though, there are many alternatives. I know he's not losing anything, and he has nothing to gain by doing the public a service. Why do other open source libraries putting up a demo then? They have nothing to gain either. Anyway, we both agree that he's not obligated to put up a demo, all I wanted to say in the first place is "it would have been nice" -- not that he should, he has to, --Lowther
-- or that he's a jerk because he didn't. Then I explained why demos are nice. Again, not why he has to make one. That is all.Lowther
can you help me #62286267 , i've tried alot but didnt get an answer ! i much appreciate youSelfabasement
U
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.

Uncommonly answered 4/10, 2013 at 12:45 Comment(0)
T
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.

Tarango answered 18/3, 2016 at 8:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.