Generating forms based on user choices in Django - django

I am working on a Django project whose purpose is to allow the user to fill in some forms. In some of these forms, the user must make a choice between several options and, based on the choice made, a particular form must be generated. At the end of all these forms, the data entered must be used to write a pdf file.
As for the functionality related to generating the pdf, what I'm interested in for the purposes of the question is the use of data entered in one view in another view using them as context.
Here's what I tried to do.
First of all I created some forms in forms.py:
class ChoiceForm(forms.Form):
CHOICES = [
('1', 'Choice-One'),
('2', 'Choice Two'),
]
choice = forms.ChoiceField(choices=CHOICES)
class ChoiceOneForm(forms.Form):
name_one = forms.CharField(max_length=200)
class ChoiceTwoForm(forms.Form):
name_two = forms.CharField(max_length=200)
Then I created this view in views.py:
def contact(request):
if request.method == 'POST':
num_people = int(request.POST.get('num_people'))
people_formset = [forms.ChoiceForm() for i in range(num_people)]
return render(request, 'home.html', {'people_formset': people_formset})
else:
return render(request, 'home.html')
def generate_pdf(request):
context = {}
return render(request, 'pdf.html', context)
And finally I have this HTML file called 'home.html':
<h1>Contact</h1>
<form method="post">
{% csrf_token %}
People number: <input type="number" name="num_people" required>
<input type="submit" value="Submit">
</form>
{% if people_formset %}
{% for form in people_formset %}
<form>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
{% endfor %}
{% endif %}
what I've been able to achieve so far is to generate as many 'choice' fields as the value of the number entered in the 'num_people' field.
What I'm missing is:
1. Being able to create, for each 'choiche' field in the formset, a ChoiceOneForm or ChoicheTwoForm form based on the choice made in the 'choice' field;
2. Being able to use all this data in the 'generate_pdf' view (for now what interests me is being able to include this data in the context of this view).

Related

django form populate multiple identical field form in one submit

I don't want to use django form class as they will not give me much flexibility.
I have a form where will random number field in easy request. i am trying to populate the multiple value of forms that appears.
this is my models.py
class Profile(models.Model):
name = models.CharField(max_length=100)
photo = models.FileField()
and this my form.html
<form method="POST" action="{% url 'form' %}">
{% csrf_token %}
{% for i in range %}
<input type="text" id="name" name="name"><br>
<input type="file" id="photo" name="photo"><br><br>
{% endfor %}
<input type="submit" value="Submit">
</form>
You may notice I am rendering field with for loop.
that is means, there will be as much field as possible can be appear based on user request.
So I want to populate these models.
my view looks like
def form_view(request):
if request.method == 'POST':
# Need to puplate the form
return render(request, 'form.html', {'range': range(40)})
Can anyone please help me how can i achieve this? i am just struggling to achieve this.
you can use modelformset_factory for this. this way,
in your views.py
from .models import Profile
from django.forms import modelformset_factory
def form_view(request):
form_range = 40 # set the range here
ProfileFormSet = modelformset_factory(Profile, fields=("name", "photo"), extra=form_range, max_num=form_range)
formset = ProfileFormSet(request.POST or None)
if request.method == "POST":
if formset.is_valid():
formset.save()
return render(request, "form.html", {"profile_formset": formset})
and in your form html
<form method="POST" action="{% url 'form' %}">
{% csrf_token %}
{{ profile_formset.as_p }}
<input type="submit" value="Submit">
</form>

Django: Return to template along with template variables

I am using Django to build a student-teacher portal.
I have two groups of users - teachers and students. If user is a teacher, i need to provide a different template (a form for selecting student's registration number). I followed this link to do the same.
Here are the code snippets:
home.html
{% if is_teacher %}
<p style="color:blue; text-align:center; font-size:160%"><b>Course taken: <span style="color:green">IT000</span></b></p>
<form action="/" method="post" align="center">
{% csrf_token %}
<div align="center">{{ form }}</div>
<input type="submit" value="Get student's results!" class="btn btn-primary" style="margin-top:10px"/>
</form>
{% else %}
<p style="color:blue; text-align:center; font-size:160%"><b>Performance for the subject <span style="color:green">IT000</span> is shown below.</b></p>
{% endif %}
views.py
#login_required(login_url="login/")
def home(request):
is_teacher = request.user.groups.filter(name='teachers').exists()
if is_teacher:
if request.method == 'POST':
form = Regno(request.POST)
if form.is_valid():
selected_reg = Student.objects.filter(regno=request.POST.get('regno'))
return render(request, 'home.html',{'selected_reg': selected_reg,'form':form})
else:
form = Regno()
return render(request, 'home.html', {'form': form,'user':request.user,'is_teacher':is_teacher})
else:
selected_reg = Student.objects.filter(regno=request.user)
return render(request, 'home.html', {'user':request.user,'is_teacher':is_teacher,'selected_reg':selected_reg})
Here, Regno is a Form for the teacher to enter student's registration no.
When a teacher initially logs in, he is displayed the form. However, after he submits the form, it is not displaying the form. It executes the {% else %} part of the template. How do I make sure the is_teacher template variable is passed on to this template after the teacher submits the form?
I read about Django sessions but I'm not sure if it can help.
Instead of passing the is_teacher every time, you should use RequestContext to pass it along with your requests.
Looks like in your post call on form submit, you are not passing is_teacher variable. (in below code)
if form.is_valid():
selected_reg = Student.objects.filter(regno=request.POST.get('regno'))
return render(request, 'home.html',{'selected_reg': selected_reg,'form':form})
You should add is_teacher in above.
Also, better way to handle it via custom template tag -
register = template.Library()
#register.filter(name='has_group')
def has_group(user, group_name):
group = Group.objects.get(name=group_name)
return True if group in user.groups.all() else False
and in the template -
{% if request.user|has_group:'teachers' %}
....
{% else %}
....
{% endif %}
You can avoid passing it with each call. Hope it helps.

How can I ask for user input with an action in django admin?

In my code, I am writing an action for grouping, I would like to ask the user how many people would they like per group and then respond with an alert box that says something along the lines of you have 4 groups, based on user input. How do I do this in django admin, how do I create some kind of pop up that asks for the amount of people that they would like to put in a group? (I'm trying to achieve this with an action)
admin.py:
Def howmany (modeladmin, request, queryset):
people = queryset.count()
amount_per = [the number that the user inputs]
Amount_of_groups = people/amount_per
A more simplified and better approach I found here:
You just need to create an Action Form like this.
from django.contrib.admin.helpers import ActionForm
from django import forms
class XForm(ActionForm):
x_field = forms.ModelChoiceField(queryset=Status.objects.all(), required=False)
Now, define that XForm in your admin.py
class ConsignmentAdmin(admin.ModelAdmin):
action_form = XForm
actions = ['change_status']
def change_status(modeladmin, request, queryset):
print(request.POST['x_field'])
for obj in queryset:
print(obj)
change_status.short_description = "Change status according to the field"
admin.py Something like:
class MyAdmin(admin.ModelAdmin):
def howmany(modeladmin, request, queryset):
people = queryset.count()
amount_per = [the number that the user inputs]
Amount_of_groups = people/amount_per
if 'apply' in request.POST:
form = AmountPerForm(request.POST)
if form.is_valid():
amount_per = form.cleaned_data['amount_per']
self.message_user(request, u'You selected - %s' % amount_per)
return HttpResponseRedirect(request.get_full_path())
else:
form = AmountPerForm()
return render(request, 'admin/amount_per_form.html', {
'items': queryset.order_by('pk'),
'form': form,
'title': u'Your title'
})
File "admin/amount_per_form.html" contains something like:
{% extends 'admin/base_site.html' %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="action" value="howmany" />
{% for item in items %}
<input type="hidden" name="_selected_action" value="{{ item.pk }}"/> {# thanks to #David Hopkins for highligting it #}
{% endfor %}
{{ form }}
<p>Apply for:</p>
<ul>{{ items|unordered_list }}</ul>
<input type="submit" name="apply" value="Apply" />
</form>
{% endblock %}

Edit a Key/Value Parameters list Formset

I'm looking for a convenient solution to create an 'edit settings' key/values page.
Parameters model :
class Parameter(models.Model):
key = models.CharField(max_length=50)
value = models.CharField(max_length=250)
showInUI = models.SmallIntegerField()
Initial Keys/Values are already inserted in table.
I load them and send them using a model formset factory using these lines :
ParameterFormSet = modelformset_factory(Parameter, extra=0, fields=('key', 'value'))
parameterFormSet = ParameterFormSet(queryset=Parameter.objects.filter(showInUI=1))
return render_to_response('config.html', {'parameterFormSet': parameterFormSet}, context_instance=RequestContext(request))
Template side, when formset is displayed, keys and values are shown as inputs.
I'd like to find a convenient way to display form keys as readonly labels and values as inputs. And, when submited, validate them according django standards.
I've read a lot of stuff, I guess the solution may be a custom widget, but I could find a reliable solution.
Thanks for reading.
EDIT :
Working solution
views.py
def config(request):
ParameterFormSet = modelformset_factory(Parameter, extra=0, fields=('value',))
if request.method == "POST":
try:
formset = ParameterFormSet(request.POST, request.FILES)
except ValidationError:
formset = None
return HttpResponse("ko")
if formset.is_valid():
formset.save()
return HttpResponse("ok")
#ParameterFormSet = modelformset_factory(Parameter, extra=0, fields=('value',))
parameterFormSet = ParameterFormSet(queryset=Parameter.objects.filter(showInUI=1))
return render_to_response('config.html', {'parameterFormSet': parameterFormSet}, context_instance=RequestContext(request))
template
<form method="post">
{% csrf_token %}
{{ parameterFormSet.management_form }}
{% for form in parameterFormSet %}
<div>
{{ form.instance.key }}
{{ form }}
</div>
{% endfor %}
<input type="submit" />
</form>
If you do not want the value to be editable, don't include it in fields when creating the form set.
ParameterFormSet = modelformset_factory(Parameter, extra=0, fields=('value',)) # don't forget the trailing comma after 'value' otherwise it's not a tuple!
In your template, you can then loop through the forms in the form set, and display the key at the same time.
{% for form in parameter_form_set %}
{{ form.instance.key }}{# display the key related to this form #}
{{ form }}{# display the form #}
{% endfor %}

Django formset doesn't validate

I am trying to save a formset but it seems to be bypassing is_valid() even though there are required fields.
To test this I have a simple form:
class AlbumForm(forms.Form):
name = forms.CharField(required=True)
The view:
#login_required
def add_album(request, artist):
artist = Artist.objects.get(slug__iexact=artist)
AlbumFormSet = formset_factory(AlbumForm)
if request.method == 'POST':
formset = AlbumFormSet(request.POST, request.FILES)
if formset.is_valid():
return HttpResponse('worked')
else:
formset = AlbumFormSet()
return render_to_response('submissions/addalbum.html', {
'artist': artist,
'formset': formset,
}, context_instance=RequestContext(request))
And the template:
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ formset.management_form }}
{% for form in formset.forms %}
<ul class="addalbumlist">
{% for field in form %}
<li>
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
</li>
{% endfor %}
</ul>
{% endfor %}
<div class="inpwrap">
<input type="button" value="add another">
<input type="submit" value="add">
</div>
</form>
What ends up happening is I hit "add" without entering a name then HttpResponse('worked') get's called seemingly assuming it's a valid form.
I might be missing something here, but I can't see what's wrong. What I want to happen is, just like any other form if the field is required to spit out an error if its not filled in. Any ideas?
Heh, I was having this exact same problem. The problem is that you're using a formset!! Formsets allow all fields in a form to be blank. If, however, you have 2 fields, and fill out only one, then it will recognize your required stuffs. It does this because formsets are made for "bulk adding" and sometimes you don't want to fill out all the extra forms on a page. Really annoying; you can see my solution here.
For each of the fields that are required, add an extra entry in the attrs parameter
resident_status = forms.ChoiceField(widget=forms.Select(
attrs={'class': 'form-control', 'required': 'required'}), choices=President.RESIDENT_STATUS,
required=True)
As you can see, I maintain the required=True for django's form validation but specify 'required':'required' for the template to insist for the field be required.
Hope that helps.
Add 2 lines.
if request.method == 'POST':
def initial_form_count(self): return 10 # the number of forms
AlbumFormSet.initial_form_count = initial_form_count
formset = AlbumFormSet(request.POST, request.FILES)
Good luck!
use:
if not any(formset.errors): ...
instead of:
if formset.is_valid(): ...