Form filled out but still invalid - django

I am trying to get data from a form and redirect if the form is valid. I am, however, seemingly not grabbing any data. In a test, I fill out each form with "test". I would expect these forms to be valid and redirect to a success page, and am unable to figure out where I am going wrong with my logic.
Error:
form.errors = <ul class="errorlist"><li>category<ul class="errorlist"><li>This field is required.</li></ul></li><li>comment<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
forms.py
from django import forms
class CandidateReportForm(forms.Form):
category = forms.CharField(label='Category', max_length=10)
comment = forms.CharField(label='Comment', max_length=1000)
views.py
from django.shortcuts import render
from django.views import View
from .forms import CandidateReportForm
class CandidateReportView(View):
form_class = CandidateReportForm
form_template = 'Reports/candidate_report.html'
form_submit_template = 'Reports/report_submitted.html'
form_submit_error = 'Reports/report_error.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.form_template, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
return render(request, self.form_submit_template)
else:
print(f"form.errors = {form.errors}")
return render(request, self.form_submit_error)
html
<div class="container">
<div class="card">
<h2 class="card-title">Report</h2>
{{ form.as_p }}
<form action="submit_candidate_report" method="post">{% csrf_token %}
<input class="btn btn-primary" id="report" name="report" type="submit" value="Submit">
</form>
</div>
</div>

You are rendering your form outside the form tag! Only fields inside the form tag are submitted to the server, so your html for the form tag should be:
<form action="submit_candidate_report" method="post">
{% csrf_token %}
{{ form.as_p }}
<input class="btn btn-primary" id="report" name="report" type="submit" value="Submit">
</form>

Related

Why don't my entries get saved in the database in Django?

The post requests from the frontend do not get saved in the database, without any error shown. However, when I manually add entries from the admin panel, it shows on the frontend.
My index.html(form part):
<form class="main__input--form" method="POST">
{% csrf_token %}
<p class="main__input--text">
<textarea name="content" id="content" class="main__input--content" cols="35" rows="8" aria-label="Entry content" placeholder="Enter text here..."></textarea>
</p>
<button class="main__input--submit" type="submit">Vent</button>
</form>
My extension of index which loops through the database entries:
{% for obj in all_vents %}
<div>
<h1>{{obj.vent}}</h1>
</div>
<br />
{% endfor %}
My models.py:
class Vents(models.Model):
vent = models.CharField(max_length=10000)
def __str__(self):
return self.vent
My forms.py:
from django import forms
from .models import Vents
class VentForm(forms.ModelForm):
class Meta:
model = Vents
fields = ['vent']
My views.py:
from django.shortcuts import render, redirect
from .forms import VentForm
from .models import Vents
def ventout(request):
if request.method == "POST":
form = VentForm(request.POST or None)
if form.is_valid():
form.save()
return redirect("ventout")
else:
all_vents = Vents.objects.all()
return render(request, "ventout.html", {"all_vents": all_vents})
Views:
def ventout(request):
all_vents = Vents.objects.all()
if request.method == "POST":
form = VentForm(request.POST or None)
if form.is_valid():
form.save()
return redirect("ventout")
else:
form = VentForm()
context = {"all_vents": all_vents, "form":form}
return render(request, "ventout.html", context)
Template:
<form class="main__input--form" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="main__input--submit">Vent</button>
</form>
you could install/use "crispy_forms_tags" to make the form look better,
https://django-crispy-forms.readthedocs.io/en/latest/index.html
if you want to go further you could install/use "widget_tweaks"
https://pypi.org/project/django-widget-tweaks/
Your index.html from part should have {{ form }} form tag, as I guess.
Try Using following code
<form class="main__input--form" method="POST">
{% csrf_token %}
{{ form }}
<p class="main__input--text">
<textarea name="content" id="content" class="main__input--content"
cols="35" rows="8" aria-label="Entry content" placeholder="Enter text here...">
</textarea>
</p>
<button class="main__input--submit" type="submit" value="Submit">Vent</button>
</form>

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>

How do i add <input type="button"> as a formfield in django

I want to have an input field as a button in my template. Just like this.I am manually rendering form fields in my template.So, how do i create a field like that in my form.
Formfield in forms.py
class DetailForm(forms.Form):
owner=forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
views.py
def getDetail(request):
form=DetailForm()
return render(request,'materials/addpage.html',{'form':form})
and template,
<div class="form-group">
{{form.owner}}
</div>
A minimal example of using buttons as input in Django looks like this:
Template:
<form method="POST">
{% csrf_token %}
<input type="submit" name="btn" value="yes">
<input type="submit" name="btn" value="no">
</form>
{{ val }}
Form:
class Fooform(forms.Form):
btn = forms.CharField()
View:
def test_view(request):
if request.method == 'POST':
form = Fooform(request.POST)
if form.is_valid():
val = form.cleaned_data.get("btn")
else:
form = Fooform()
return render(request, 'template.html', locals())
Libraries like crispy-forms have button widgets.

Validation errors don't show in Django form

When I try to test my form with an email which has already been entered into the database it doesn't give the error message like it should, it redirects back to the homepage.
My views.py file looks like this:
from django.shortcuts import render
from .forms import LotteryForm
from django.http import HttpResponseRedirect
# Create your views here.
def lottery_list(request):
return render(request, 'lottery/lottery.html', {})
def lottery_new(request):
if request.method == 'POST':
form = LotteryForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('lottery_submitted')
else:
return render(request, 'lottery/lottery.html' {'form': LotteryForm()})
else:
form = LotteryForm()
return render(request, 'lottery/lottery.html', {'form': LotteryForm()})
My form is:
from django import forms
from django.db import models
from django.forms import ModelForm
from .models import Lottery
from .models import Lottery_user
from django.core.exceptions import ValidationError
class LotteryForm(forms.ModelForm):
lottery_type = forms.ModelChoiceField(queryset = Lottery.objects.all(), empty_label=None)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
lottery_numbers = forms.CharField(max_length=12, required=True)
email = forms.EmailField(required=True)
telephone = forms.CharField(max_length=18,
error_messages={'invalid':'Enter a valid mobile number',
'required':'Enter a valid mobile number'})
def clean_email(self):
email = self.cleaned_data['email']
if Lottery_user.objects.filter(email=email).exists():
raise ValidationError("Email already exists")
return email
if Lottery_user.objects.filter(email=self.cleaned_data['email']).exists():
raise ValidationError("You've already entered")
return email
class Meta:
model = Lottery_user
fields = ['lottery_numbers', 'lottery_type', 'first_name', 'last_name', 'email', 'telephone',]
Template form:
<form action="{% url 'lottery_new' %}" method="post">
{% csrf_token %}
<div class="fieldWrapper">
<label for="lotterytype">Choose a lottery:</label>
{{ form.lottery_type }}
<p>See <a href=lottery_instructions>instructions and rules</a></p>
</div>
<div class="fieldWrapper">
<label for="lotterynumbers">Lottery Numbers:</label>
{{ form.lottery_numbers }}
</div>
<div class="fieldWrapper">
<label for="firstname">First name:</label>
{{ form.first_name }}
</div>
<div class="fieldWrapper">
<label for="lastname">Last name:</label>
{{ form.last_name }}
</div>
<div class="fieldWrapper">
{{ form.email.errors }}
{{ form.email.non_field_errors }}
<label for="email">Email:</label>
{{ form.email }}
</div>
<div class="fieldWrapper">
<label for="phonenumber">Telephone Number:</label>
{{ form.telephone }}
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
I want a user to not be able to enter twice with the same email, so it should error when the user clicks submit with a used email address.
Also, does anyone know how to get the error messages to show before the user submits rather than afterwards?
You return a newly instantiated form when the form is invalid, when you should instead return the invalid form (with the errors). Try:
def lottery_new(request):
if request.method == 'POST':
form = LotteryForm(request.POST)
if form.is_valid():
....
else:
return render(request, 'lottery/lottery.html' {'form': form})

how to submit a form and formset at the same time

I am trying to render a form and a formset at once.
The formset is working fine (i think), but the form is not validating (as if there was no data being posted)
i have tried playing with the button but its main submit function comes through js.
the forms all work independently but not when submitted togetehr so it seem like the problem is in the views
here is the code:
views.py
from django.shortcuts import render, render_to_response
from django.http import HttpResponseRedirect
from forms import LessonForm, AddMaterialForm
from models import Lesson, SUBJECT_OPTIONS, Materials, MATERIAL_TYPES
from django.forms.formsets import formset_factory
def Create_Lesson(request):
AddMaterials=formset_factory(AddMaterialForm, extra=9)
if request.method == "POST": # If the form has been submitted...
lesson = LessonForm(request.POST, prefix="lesson") # A form bound to the POST data
formset = AddMaterials(request.POST, request.FILES) # A form bound to the POST data
if lesson.is_valid() and formset.is_valid(): # All validation rules pass
lesson = lesson.save(commit=False)
lesson.creator = request.user
lesson.save()
for form in formset:
form = form.save(commit=False)
form.lesson = lesson.pk
form.save()
return render(request, 'index.html',)
else:
lesson= LessonForm(prefix='lesson') # An unbound form
formset = AddMaterials()
return render(request, 'create_lesson/create.html', {
'form': lesson,'formset':formset
})
.html
<form id="create_lesson_form" method="post" action="">
<h2>1: Create Your Lesson</h2>
{{ form.non_field_errors }}
<label for="subject"><span>Subject</span></label>
{{form.subject}}
{{ form.subject.errors }}
<label for="title"><span>Title</span></label>
<input type="text" id="title" name="name" placeholder="Give it a name"/>
{{ form.name.errors }}
<label class="error" for="title" id="title_error">You must choose a title!</label>
<label for="subtitle"><span>Subtitle</span></label>
<input type="text" id="subtitle" name="subtitle" placeholder="Type your subtitle here"/>
{{ form.subtitle.errors }}
<label class="error" for="subtitle" id="subtitle_error">are you sure you want to leave subtititle blank?</label>
<label for="description"><span>Description</span></label>
<textarea id="description" name= "description" cols="42" rows="5" placeholder="why is it important? this can be a longer description"'></textarea>
{{ form.description.errors }}
<label class="error" for="description" id="description_error">are you sure you want to leave the description blank?</label>
<label for="success" id="Goals_title"><span>Goals</span></label>
<textarea id="success" name="success" cols="42" rows="5" placeholder="explain what sucess might look like for someone doing this lesson...what does mastery look like?" '></textarea>
{{ form.success.errors }}
<label class="error" for="success" id="success_error">are you sure you want to leave the goals blank?</label>
{{ form.directions.errors }}
<label class="error" for="directions" id="directions_error">are you sure you do not want to include dierections?</label>
<label for="directions" id="Directions_title"><span>Directions</span></label>
<textarea id="directions" name="directions" cols="42" rows="5" placeholder="you can add simple directions here" '></textarea><br>
</form>
<form id="add_elements_form" method="post" action="">
{% csrf_token %}
{{ formset.as_p}}
<button type='submit' id='finish'>Finish Editing Lesson</button>
</form>
This will submit the form and the formset at the same time.
//When your uploading files or images don't forget to put "multipart/form-data"
// in your form.
//To connect formset in your form, don't forget to put the model in the formset
// for instance.
//In this you can add many lines as you want or delete it.
forms.py
class LessonForm(forms.ModelForm):
class Meta:
model = Lesson
MaterialsFormset = inlineformset_factory(Lesson, Materials,
fields=('field_name', 'field_name'), can_delete=True)
views.py
def view_name(request):
form = LessonForm()
formset = MaterialsFormset(instance=Lesson())
if request.method == 'POST':
form = LessonForm(request.POST)
if form.is_valid():
lesson = form.save()
formset = MaterialsFormset(request.POST, request.FILES,
instance=lesson)
if formset.is_valid():
formset.save()
return render(request, 'index.html',)
return render(request, "page.html", {
'form': form, 'formset': formset
})
html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ formset.as_p }}
<input type="submit" value="Save"/>
</form>
You only need one form tag. If you expect to receive all of the data at the same time, you need to wrap all of the fields with one form tag.
Now that django 4 is out it's possible to do the same thing within the form itself using the Reusable templates. I prefer this solution because it is then simpler to reuse complex forms without messing with the views.
For the record, here is how I do it
The 2 related models:
# models.py
from django.db import models
class Recipe(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Ingredient(models.Model):
name = models.CharField(max_length=100)
quantity = models.FloatField()
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name="ingredients")
def __str__(self):
return self.name
The forms bound together:
# forms.py
from django import forms
from .models import Recipe, Ingredient
class IngredientForm(forms.ModelForm):
class Meta:
model = Ingredient
exclude = ('recipe',)
IngredientFormSet = forms.inlineformset_factory(Recipe, Ingredient, form=IngredientForm)
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = '__all__'
template_name = 'recipe_form.html'
def __init__(self, *args, **kwargs):
"""Initialize the formset with its fields."""
self.formset = IngredientFormSet(*args, **kwargs)
super().__init__(*args, **kwargs)
def get_context(self):
"""Add the formset to the context for rendering."""
context = super().get_context()
context['formset'] = self.formset
return context
def save(self, commit=True):
"""Bind both models together."""
instance = super().save(commit=False)
self.formset.instance = instance
if self.formset.is_valid():
instance.save()
self.formset.save()
return instance
The template for the form:
<!-- templates/recipe_form.html -->
<p>Recipe: {{ form.name }}</p> <!-- calling "form" creates a rendering recursion -->
<p>Ingredients:</p>
{{ formset.management_form }}
<ul>
{% for elt in formset %}
<li>{{ elt }}</li>
{% endfor %}
</ul>
And the view using it:
# views.py
from django.views.generic import TemplateView
from .forms import RecipeForm
class RecipeView(TemplateView):
template_name = 'recipe.html'
def get_context_data(self):
context = super().get_context_data()
context['form'] = RecipeForm()
return context
def post(self, *args, **kwargs):
form = RecipeForm(self.request.POST)
if form.is_valid():
form.save()
else:
raise Exception('Something bad happened!')
return self.get(*args, **kwargs)
With a very basic template:
<!-- templates/recipe.html -->
<form action="." method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Submit</button>
</form>
And finally you're good to go:
# tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Recipe
class TestFormSet(TestCase):
def test_new_recipe(self):
data = {
"name": "quiche",
"ingredients-TOTAL_FORMS": 3,
"ingredients-INITIAL_FORMS": 0,
"ingredients-0-name": 'bacon bits',
"ingredients-0-quantity": 200,
"ingredients-1-name": 'eggs',
"ingredients-1-quantity": 4,
"ingredients-2-name": 'cream',
"ingredients-2-quantity": 150,
}
r = self.client.post(reverse('recipe'), data=data)
self.assertEqual(Recipe.objects.first().ingredients.count(),3)
$ python manage.py test
OK
Hopefully it will be useful to somebody.