13

I am trying to automate the creation of something like this:

<input type='text' name='asdf[]' />
<input type='text' name='asdf[]' />
<input type='text' name='asdf[]' />

By cycling through a range in the form. I've been trying things like this, along with several other variations:

# in a model class
for i in range(1, prim+1):
    self.fields['asdf'] = forms.CharField(label=i)

# in the template
<form action='#' method='post'>
    {{form.as_p}}
</form>

But I haven't had any luck though.

How can I go about automating an array of inputs?

** edit ** To clarify, eventually I need to be able to access the fields in the template like this:

{% for input in form.fields.asdf %}
{{input}}
{% endfor %}

Which would then hopefully get me the original input list shown above...

3
  • Your loop is just creating a new CharField with an invalid label (can't be an integer) and overwriting self.field['asdf'] . I'm not sure what it is that you're trying to do. Do you mean like using the choices attribute? Commented Mar 10, 2010 at 20:02
  • Yea, that particular version of it just overwrites. I had more complicated code before that was tried to create lists and/or dictionaries but I couldn't get them to work either. Commented Mar 10, 2010 at 20:08
  • Perhaps you could make your label 'asdf' or something, then loop through all fields, outputting it if {% ifequal field.label 'asdf' %} matches, and then use forloop.counter to show 1, 2, 3, etc. I updated my answer below. Commented Mar 10, 2010 at 22:12

4 Answers 4

12

Jacob Kaplan-Moss (co-author of Django) recently posted a great article for handling dynamic forms, which should solve your problem in a preferred way: http://jacobian.org/writing/dynamic-form-generation/

He's using the same method that Felix suggests, but it's worth reading the whole article to get a better grasp on the concept.

Using the asdf[] technique is sloppy, because then you have to deal with ordering. It's also not the standard practice.

Edit:

To handle the situation where you need to detect when you hit these dynamic fields:

{% for input in form.fields %}
    {% ifequal input.label 'asdf' %}
        {{ forloop.counter }}: {{input}}<br />
    {% endifequal %}
{% endfor %}
Sign up to request clarification or add additional context in comments.

2 Comments

This is how I originally had things setup. Unfortunately, it doesn't quite serve my needs. There is a paragraph of description text that I need to insert before the asdf[] inputs begin... and the number of asdf[] inputs needs to be dynamic. So, I need to be able to detect that the asdf inputs are about to begin, display a paragraph, then spit them all out.
Summary of the article: let your Form class do the heavy lifting. Great read.
2

It should be more like e.g.:

# in a model class
for i in range(1, prim+1):
    self.fields['asdf_%s' % i] = forms.CharField(label='Label %i' % i)

But it very depends on what you want to achieve.

4 Comments

I don't want the fields to have different names when outputted.
Is there a particular reason you don't want different field names? Just curious.
@Brant: Well obviously a dictionary cannot contain more than one value for a key. In your code you are just overriding the field again and again.
Yea, it was a more complex dictionary with nested lists or something inside. Either way, it didn't work.
2

It looks like I can do what I need to do by breaking the form into multiple formsets...

http://docs.djangoproject.com/en/dev/topics/forms/formsets/#topics-forms-formsets

Then, I should be able to access each formset individually from the template, wrapping all of them into one

Comments

0

You may want to try something like this:

form_view.html

<!-- simple form page -->
<form action="{{ request.path }}" method="post">
  {{ form }}
</form>

views.py

# Some form controller
class DynamicFormView(FormView):
   form_class = DynamicForm
   template_name = "form_view.html"

   def get_form_kwargs(self):
        kwargs = super(DynamicFormView, self).get_form_kwargs()
        # Decide on a number of dynamic fields
        # to pass into the form
        # i.e. form(num_products=5)
        kwargs['num_products'] = 5  
        return kwargs

models.py

# A model
class Company(models.Model):
    name = models.CharField(max_length=200)

# A model with a many to one relationship with another model
class Product(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)

forms.py

class DynamicForm(forms.ModelForm):
    product_label = 'Product name'
    class Meta:
        model = MyModel
        fields = ['name']

    def __init__(self, *args, **kwargs):
        # get the number of dynamic fields to create
        num_products = kwargs.pop('num_products')
        # initialize the form before creating fields
        super(DynamicForm, self).__init__(*args, **kwargs)
        # create the dynamic fields
        for row in range(num_products):
            self.fields['product_'.format(row)] = forms.CharField(label=self.question_label)

    def save(self, commit=True):
        # create the default object
        company = super(DynamicForm, self).save(commit)
        # create child products from the dynamic fields
        products = []
        for key, value in self.cleaned_data.items():
            if key.startswith('product_'):
                product = Product()
                product.name = value
                product.company = company
                products.append(product)
        if commit is True:
            Product.objects.filter(company=company).delete()
            Product.objects.bulk_create(products)
        return company
        

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.