About •  Books •  Apps •  Jobs •   Feeds •  Twitter
Login

Javascript inline forms outside of contrib.admin

This recipe follows on from the last two and shows how to do the addition of inline fields to a model form, outside of contrib.admin.

Note: this is excerpt a recipe from cleardjango. You can download the source from SVN. Once you've checked it out, do: python tools/configure.py -s 72 to install and run this recipe.

In the last two recipes, we’ve seen how to add inlines through the admin interface. We’ll repeat this process in this last recipe of the series but without the admin interface. This uses some different API’s and therefore can be slightly different. Again, we’ll show a quick snippet of the end result:

_images/recipe_72.jpg

The models have remain unchanged since recipe 70. The forms for this setup will also be similar to recipe 70. We’ll just need to add in one new form - the Recipe form. This is a basic Django ModelForm:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from django import forms

from recipe_72.models import Recipe

class RecipeForm(forms.ModelForm):
    class Meta:
        model = Recipe

def ingredient_form_callback(instance, field, *args, **kw):
    if field.name == "other_recipe":
        if instance:
            return field.formfield(queryset=Recipe.objects.exclude(pk=instance.pk), **kw)
    return field.formfield(**kw)

We’ll set up two URLs for this recipe, one to add and one to add:

1
2
3
class RecipeForm(forms.ModelForm):
    class Meta:
        model = Recipe

The add template is pretty straightforward. We are going to assume that the view is passing a form (the Recipe form) and a formset (the Ingredient formset) to the template. Notice in the template that I’ve used the same classes from the admin in order to make the Javascript a little bit easier. Each formset is also rendered in its own table. This makes for a slightly ugly markup but it works.

                  {{ formsets.management_form }}
                  {% for formset in formsets.forms %}
                  <table>
                      {{ formset }}
                  </table>
                  {% endfor %}

When rendering a formset manually in this manner, we also need to include the management form (line 1). This form is the value that gets incremented every time Add another row is pressed [1]. The JavaScript is very similar to the one from recipe 72 with a few minor alterations. Since we now have control over the template, the button is rendered in the template not the JavaScript.

Those are all the elements. Now we need to take a look at the main part of this recipe, the views. I covered how to use the formfield_callback and curry to process the field correctly in recipe 70 . We’ll repeat this process in our view. The admin won’t be constructing the formset for us this time. We’ll have to construct it using an inlineformset_factory [2]:

1
2
3
    IngredientFormSet = inlineformset_factory(Recipe, Ingredient, 
                fk_name="recipe", 
                formfield_callback=curry(ingredient_form_callback, None))

Once we’ve got that formset built, most of the rest of the form is straightforward and follows the standard form population, validation and save process. One slight wrinkle is before the formset can be saved we’ll need a recipe to link to it. For this reason, the view populates the formset, validates it, then repopulates it with the saved recipe:

1
2
3
4
5
        form = RecipeForm(request.POST)
        formset = IngredientFormSet(request.POST)
        if form.is_valid() and formset.is_valid():
            recipe = form.save()
            formset = IngredientFormSet(request.POST, instance=recipe)

There’s little difference for the edit page and most of that should be familiar by now. Since we ended up using the formfield_callback, it’s easy to have these models editable in both the admin and outside the admin.

Sponsored Recipe: This recipe was sponsored by Blue Fountain Systems Ltd. Please contact Clearwind if you’d like to sponsor a recipe.

[1] http://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-a-formset-in-views-and-templates
[2] http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets

Comments

There are 2 comment(s).

It doesn't store the information on ajax added forms; only in the ones that come with the page. I think you have to add JS to increase the number in the management_form (increasing TOTAL_FORMS).

Marcos on Nov, 24

You are right, the link for the p was in the wrong place causing the jquery lookup to fail. I've moved that and works great now. Thanks!

Andy McKay on Dec, 01

Login to add comments