Django formwizard with formsets and forms on same page - django

I'm working with Django 1.4 FormWizard (specifically, NamedUrlFormWizard)
first, the basics. i have a 3 step form wizard i'm building. The final outcome is along the lines of defining a template, and then choosing some people to use it, and then send them an email.
Step 1 - enter in basic template data (name, description, etc)
Step 2 - define a list of N fields, each with their own set of attributes but all identical in structure
Step 3 - choose one or more users to email, AND customize the contents of this email before saving
so far, in the form wizard:
Step 1 is a standard Form class, and works just fine.
Step 2 is a standard Formset class, and works just fine.
Step 3 is giving me some trouble. It needs to be a formset (list of email addresses), but also an additional form input field with email text. I can't figure out how to have both a formset in addition to a non-repeating form input on the same page inside of a form wizard.
in a perfect world, i could define a Formset as just another form field in a Form definition.
ie:
class EmailAddressForm(forms.Form):
email = forms.EmailField()
class EmailAddressesAndText(forms.Form):
emailText = forms.Textarea()
emailAddressFormSet = formset_factory(EmailAddressForm, etc etc)
then point my FormWizard page at 'EmailAddressesAndText' and be done with it. but its not a perfect world. Any ideas on how I can achieve such a thing?

You can use get_form() method of WizardView to customize the form for particular step. Refer: WizardView.get_form. This answer can help you to add a field
Another option would be to add the field in formset and through JS disable and hide all instances other than first one.

Related

Specifying specific form format in Django

I am using materializecss to give my django site some material elements. I have put together a form (the 'old' way using html) but now realised I need to use a django form instead. The problem is, these forms don't play well with materialises built in column system (they use classes to determine rows and column spacing). Here is an example of the layout I set up so far. However when defining the form through form.py, it spits out one input per layer.
My question is: what can I do to either a) get django to work with the html-defined form or b) make a 'form template' to give the input fields the appropriate classes?
If you want to see the code I can post some but I'm quite a new coder so it's messy.
Thanks!
There are three ways I can think of off the top of my head.
If you want full control over the HTML form, in a Django template or HTML form, simply map the names of your fields to match the underlying field names in the Django form. This way, when POSTed back to your view, Django will automatically link up the POSTed fields with the Django form fields.
For example, if you have a field username in your Django form (or Django model if using ModelForm), you could have an element <input type="text" name="username" maxlength="40"> (that you can style any way you need) on your HTML form that Django will happily parse into your Django form field, assuming your view is plumbed correctly. There is an example of this method in the Django documentation.
Another way is to customize the Django form field widgets in your Django form definition. The Django documentation talks a little bit about how to do this. This is great for one offs, but is probably not the best approach if you expect to reuse widgets.
The final approach would be to subclass Django form field widgets to automatically provide whatever attributes you need. For example, we use Bootstrap and have subclassed nearly all of the widgets we use to take advantage of Bootstrap classes.
class BootstrapTextInput(forms.TextInput):
def __init__(self, attrs=None):
final_attrs = {'class': 'form-control'}
if attrs is not None:
final_attrs.update(attrs)
super().__init__(attrs=final_attrs)
Then it's simply a matter of letting the Django form know which widget to use for your form field.
class UsernameForm(forms.ModelForm):
class Meta:
model = auth.get_user_model()
fields = ['username']
widgets = {'username': BootstrapTextInput()}
Hope this helps. Cheers!

submit multiple forms synchronously in django

Is there a way to submit simultaneously two forms connected through a foreign key in django?
I need to send them in order, so the best idea is to use synchronous option, right?
Any ideas? A simple example would be very useful!
Edit: Each form is on a different tab in the template. I have 5 tabs so I have five form tags.
You can post 2 forms at the same time using prefixes. Let's say you have these models:
Post => Category and you want to save a post and a category at the same time.
In your view:
post_form = PostForm(prefix="post", ...)
category_form = CategoryForm(prefix="category", ...)
...
if post_form.is_valid() and category_form.is_valid():
...
In your template you just show them within the same form tag.

Best way to get dynamic fields in Django

I have a Django application that allows people to create a record. A record can contain one or more people and the form by default contains three fields to capture a person's name. One for first name, one for middle initial and one for last name. When a person is creating a record they can add additional people by clicking a plus button. The plus button adds another set of three text boxes. They can do this for as many people as they want to add.
Once they click the plus button a minus button shows up next to each row so they can remove those fields if they decide to.
What is the best way to name the text fields so that I can get all the text fields and iterate through them in the back end of the application?
I thought if I named them all the same I would get an array of names when I do:
request.POST.getlist('firstname')
However, that is not the case. Instead I get the value from the last input field with that name.
Any suggestions are appreciated.
I suggest you take a look at formsets instead of reinventing this one.
https://docs.djangoproject.com/en/dev/topics/forms/formsets/
from django.forms.formsets import formset_factory
FormSet = formset_factory(MyForm, extra=2)
formset = FormSet()
print formset.forms # you will see it is a collection of MyForm objects
You can dynamically add / remove formsets by modifying a the "management form" and adding the next form (you can copy {{ formset.empty_form }} and modify the IDs to be the N-th form) and there are many examples online of how to do so:
How would you make a dynamic formset in Django?
Dynamically adding a form to a Django formset with Ajax

Add entry to form within a form via button in Django

To illustrate what I am trying to do I will use an example:
I have a create recipe form and within this form I intend to have some character fields and two other forms:
1. A steps form
2. An ingredients form
for each of these forms I want to have a button that allows you to add another step or another ingredient.
Here is a screenshot of what I have so far to give you an idea.
Then I need to compact all of the fields and forms into the single create_recipe form.
Has anyone tried to do something like this before?
I can attach some of the code I have if that would be helpful.
Thanks,
katie
I would set it up as follows:
Create a recipe form
Create a steps form and based on that a steps formset
Create a ingredient form and based on that a ingredient formset
Add the recipe form, steps formset and ingredient formset to your template.
Build the handling of those 3 in your underlying view
Make your formsets dynamic, i.e. add and delete buttons, with http://code.google.com/p/django-dynamic-formset/
This link might be very helpful for some implementation details: http://haineault.com/blog/155/

How to make a "workflow" form

For my project I need many "workflow" forms. I explain myself:
The user selects a value in the first field, validates the form and new fields appear depending on the first field value. Then, depending on the others fields, new fields can appear...
How can I implement that in a generic way ?
I think the solution you are looking for is django form wizard
Basically you define separate forms for different pages and customize the next ones based on input in previous screens, at the end, you get all form's data together.
Specifically look at the process step advanced option on the form wizard.
FormWizard.process_step()
"""
Hook for modifying the wizard's internal state, given a fully validated Form object. The Form is guaranteed to have clean, valid data.
This method should not modify any of that data. Rather, it might want to set self.extra_context or dynamically alter self.form_list, based on previously submitted forms.
Note that this method is called every time a page is rendered for all submitted steps.
The function signature:
"""
def process_step(self, request, form, step):
# ...
If you need to only modify the dropdown values based on other dropdowns within the same form, you should have a look at the implemented dajaxproject
I think it depends on the scale of the problem.
You could write some generic JavaScript that shows and hides the form fields (then in the form itself you apply these css classes). This would work well for a relatively small number showing and hiding fields.
If you want to go further than that you will need to think about developing dynamic forms in Django. I would suggest you don't modify the ['field'] in the class like Ghislain suggested. There is a good post here about dynamic forms and it shows you a few approaches.
I would imagine that a good solution might be combining the dynamic forms in the post above with the django FormWizard. The FormWizard will take you through various different Forms and then allow you to save the overall data at the end.
It had a few gotchas though as you can't easily go back a step without loosing the data of the step your on. Also displaying all the forms will require a bit of a customization of the FormWizard. Some of the API isn't documented or considered public (so be wary of it changing in future versions of Django) but if you look at the source you can extend and override parts of the form wizard fairly easily to do what you need.
Finally a simpler FormWizard approach would be to have say 5 static forms and then customize the form selection in the wizard and change what forms are next and only show the relevant forms. This again would work well but it depends how much the forms change on previous choices.
Hope that helps, ask any questions if have any!
It sounds like you want an AJAXy type solution. Checkout the Taconite plugin for jQuery. I use this for populating pulldowns, etc. on forms. Works very nicely.
As for being "generic" ... you might have standard methods on your container classes that return lists of children and then have a template fragmen t that knows how to format that in some 'standard' way.
Ok, I've found a solution that does not use ajax at all and seems nice enough to me :
Create as many forms as needed and make them subclass each other. Put an Integer Hidden Field into the first one :
class Form1(forms.Form):
_nextstep = forms.IntegerField(initial = 0, widget = forms.HiddenInput())
foo11 = forms.IntegerField(label = u'First field of the first form')
foo12 = forms.IntegerField(label = u'Second field of the first form')
class Form2(Form1):
foo21 = forms.CharField(label = u'First field of the second form')
class Form3(Form2):
foo31 = forms.ChoiceField([],
label=u'A choice field which choices will be completed\
depending on the previous forms')
foo32 = forms.IntegerField(label = u'A last one')
# You can alter your fields depending on the data.
# Example follows for the foo31 choice field
def __init__(self, *args, **kwargs):
if self.data and self.data.has_key('foo12'):
self.fields['foo31'].choices = ['make','a','nice','list',
'and you can','use your models']
Ok, that was for the forms now here is the view :
def myview(request):
errors = []
# define the forms used :
steps = [Form1,Form2,Form3]
if request.method != 'POST':
# The first call will use the first form :
form = steps[0]()
else:
step = 0
if request.POST.has_key('_nextstep'):
step = int(request.POST['_nextstep'])
# Fetch the form class corresponding to this step
# and instantiate the form
klass = steps[step]
form = klass(request.POST)
if form.is_valid():
# If the form is valid, increment the step
# and use the new class to create the form
# that will be displayed
data = form.cleaned_data
data['_nextstep'] = min(step + 1, len(steps) - 1)
klass = steps[data['_nextstep']]
form = klass(data)
else:
errors.append(form.errors)
return render_to_response(
'template.html',
{'form':form,'errors':errors},
context_instance = RequestContext(request))
The only problem I saw is that if you use {{form}} in your template, it calls form.errors and so automagically validates the new form (Form2 for example) with the data of the previous one (Form1). So what I do is iterate over the items in the form and only use {{item.id}}, {{item.label}} and {{item}}. As I've already fetched the errors of the previous form in the view and passed this to the template, I add a div to display them on top of the page.