Can't get form errors when using crispy forms - django

I'm using django-crispy-forms, a third party library, in a Django project, I would like to customize a form to get the errors at top of the form, but I can't.
This is a snippet of the code:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-sm-6">
<h1>Add New Task</h1>
<form action="" method="post">{% csrf_token %}
<div class="row">
<div class="col-sm-12">{{ form|as_crispy_errors:"bootstrap3" }}</div>
<div class="col-sm-10">{{ form.project|as_crispy_field }}</div>
<div class="col-sm-2" id="add-new">
Add new
</div>
</div>
<div class="row">
<div class="col-sm-12">{{ form.title|as_crispy_field }}</div>
</div>
<button class="btn btn-primary" type="submit">Add</button>
</form>
</div>
{% endblock content %}
The view:
def new_task(request):
form = NewTaskForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return redirect('/pomodoro/home')
return render(request, 'pomodoro/new-task.html', {
'form': form,
})
This is my form:
from django import forms
from .models import Task
class NewTaskForm(forms.ModelForm):
class Meta:
model = Task
fields = ['project', 'title',]

Related

Django- Template not found

I can't seem to get my delete, edit and add review functionality working. The errors come as soon as I try to navigate to the urls I have set up. When I try and add a new review using my link on the reviews page I get the below message:
TemplateDoesNotExist at /reviews/add
I don't understand why because I have linked the url above to the template, which I have created.
The issue I have with my edit/delete views is that the url it searches for when I click the button is just /edit/ or /delete/ rather than reviews/edit/int:pk or reviews/delete/int:pk as per my urls.
I have pasted my code below, any help would be much appreciated! I have the feeling I am going to kick myself when I realise!
reviews.html:
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container-fluid home-container">
<div class="row align-items-center">
<div class="col-sm-12 text-center mt-4">
<h2><strong>Reviews</strong></h2>
</div>
</div>
{% for review in reviews %}
<hr class="hr-1">
<div class="row featurette">
<div class="col-sm-12">
<h2 class="featurette-heading">{{ review.title }}</h2>
<p class="lead">{{ review.content }}</p>
<div class="row justify-content-between mx-1">
<p>By: {{ review.user }}</p>
<p>Created on: {{ review.created }}</p>
<p>Last Updated: {{ review.updated }}</p>
</div>
<!-- Add user authentication if -->
<div class="text-center">
<a href="edit/{{ review.id }}" class="mx-2">
<button class="positive-button mb-2">Edit</button></a>
<a href="delete/{{ review.id }}" class="mx-2 mb-2">
<button class="negative-button">Delete</button></a>
</div>
</div>
</div>
{% endfor %}
<div class="row">
<div class="col-sm-12 text-center py-4">
{% if user.is_authenticated %}
<a href="{% url 'home:add_review' %}">
<button class="positive-button-lg">Add a review</button>
</a>
{% else %}
<p>If you would like to add your own review, please login or sign up if you haven't already!</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}
add_review.html:
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-auto text-center p-3">
<form method="post" style="margin-top: 1.3em;">
{{ review_form }}
{% csrf_token %}
<button type="submit" class="btn btn-primary btn-lg">Submit</button>
</form>
</div>
</div>
{% endblock %}
views.py:
from django.shortcuts import render
from django.views import View
from django.urls import reverse_lazy
from django.views.generic import UpdateView, DeleteView
from .models import Reviews
from .forms import ReviewForm
def home(request):
''' Returns the home page.'''
return render(request, 'home/index.html')
def reviews(request):
''' Returns the reviews page.'''
serialized_reviews = []
reviews = Reviews.objects.all()
for review in reviews:
serialized_reviews.append({
"title": review.title,
"content": review.content,
"user": review.user,
"created": review.created,
"updated": review.updated,
})
context = {
"reviews": serialized_reviews
}
print(serialized_reviews)
return render(request, 'home/reviews.html', context)
class AddReview(View):
'''View which allows the user to add a new review.'''
def get(self, request, *args, **kwargs):
review = Reviews
review_form = ReviewForm
context = {
'review': review,
'review_form': review_form,
'user': review.user,
'title': review.title,
'content': review.content,
}
return render(request, 'add_review.html', context)
def post(self, request, *args, **kwargs):
review_form = ReviewForm(data=request.POST)
if review_form.is_valid():
obj = review_form.save(commit=False)
obj.user = request.user
obj.save()
return redirect("home:reviews")
class DeleteReview(DeleteView):
'''View which allows the user to delete the selected review.'''
model = Reviews
template_name = 'delete_review.html'
success_url = reverse_lazy('reviews')
class EditReview(UpdateView):
'''View which allows the user to edit the selected review.'''
model = Reviews
template_name = 'edit_review.html'
fields = ['title', 'content']
urls.py:
from django.urls import path
from . import views
app_name = 'home'
urlpatterns = [
path('', views.home, name='home'),
path('reviews', views.reviews, name='reviews'),
path('reviews/add', views.AddReview.as_view(), name='add_review'),
path('reviews/delete/<int:pk>', views.DeleteReview.as_view(), name='delete_review'),
path('reviews/edit/<int:pk>', views.EditReview.as_view(), name='edit_review'),
]
The main difference is my app name, which is 'core'. Also, I forgot to add the content field to the model, but that is easily done, the form will just handle it as soon as you do the migration. (except on list.html)
models.py
from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
class Reviews(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
title = models.CharField(max_length=128)
created_at = models.DateTimeField(auto_now_add=timezone.now())
updated_at = models.DateTimeField(auto_now=timezone.now())
forms.py
from django import forms
from core.models import Reviews
class ReviewsForm(forms.ModelForm):
class Meta:
model = Reviews
fields = '__all__'
views.py
from core.models import Reviews
from core.forms import ReviewsForm
from django.shortcuts import render, redirect
def list_reviews(request):
reviews = Reviews.objects.all()
context = {
"reviews": reviews
}
return render(request, 'reviews/list.html', context)
def add_review(request):
if request.method == 'POST':
form = ReviewsForm(request.POST)
if form.is_valid():
form.save()
return redirect('/reviews/')
else:
form = ReviewsForm()
context = {
'form': form
}
return render(request, 'reviews/detail.html', context)
def edit_review(request, pk):
if request.method == 'POST':
form = ReviewsForm(request.POST)
if form.is_valid():
obj = Reviews.objects.get(id=pk)
obj.title = form.cleaned_data['title']
obj.user = form.cleaned_data['user']
obj.save()
return redirect('/reviews/')
else:
obj = Reviews.objects.get(id=pk)
form = ReviewsForm(instance=obj)
context = {
'form': form
}
return render(request, 'reviews/detail.html', context)
def delete_review(request, pk):
obj = Reviews.objects.get(id=pk)
obj.delete()
return redirect('/reviews/')
urls.py
from django.urls import path
import core.views as views
app_name = 'core'
urlpatterns = [
path('reviews/', views.list_reviews, name='list-reviews'),
path('reviews/add', views.add_review, name='add-review'),
path('reviews/edit/<int:pk>/', views.edit_review, name='edit-review'),
path('reviews/delete/<int:pk>/', views.delete_review, name='delete-review'),
]
list.html
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container-fluid home-container">
<div class="row align-items-center">
<div class="col-sm-12 text-center mt-4">
<h2><strong>Reviews</strong></h2>
</div>
</div>
{% for review in reviews %}
<hr class="hr-1">
<div class="row featurette">
<div class="col-sm-12">
<h2 class="featurette-heading">{{ review.title }}</h2>
<p class="lead">{{ review.content }}</p>
<div class="row justify-content-between mx-1">
<p>By: {{ review.user }}</p>
<p>Created on: {{ review.created_at }}</p>
<p>Last Updated: {{ review.updated_at }}</p>
</div>
<!-- Add user authentication if -->
<div class="text-center">
<a href="{% url 'core:edit-review' pk=review.id %}" class="mx-2">
<button class="positive-button mb-2">Edit</button></a>
<a href="{% url 'core:delete-review' pk=review.id %}" class="mx-2 mb-2">
<button class="negative-button">Delete</button></a>
</div>
</div>
</div>
{% endfor %}
<div class="row">
<div class="col-sm-12 text-center py-4">
{% if user.is_authenticated %}
<a href="{% url 'core:add-review' %}">
<button class="positive-button-lg">Add a review</button>
</a>
{% else %}
<p>If you would like to add your own review, please login or sign up if you haven't already!</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}
detail.html
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-auto text-center p-3">
<form method="post" style="margin-top: 1.3em;">
{% csrf_token %}
<table>
{{ form }}
</table>
<button type="submit" class="btn btn-primary btn-lg">Save</button>
</form>
</div>
</div>
{% endblock %}
According to your urls, It is a review/edit/<int:pk>.
So you must add same thing in href tag.
Change this:
<a href="edit/{{ review.id }}"
To this:
<a href="review/edit/{{ review.id }}"
That's why you are getting that error
I've fixed it, firstly the path in my add_review view was wrong, which I have amended and it now works (thanks Ivan).
I also was not actually bringing the ID through on my 'reviews' view in the first place, so when following the links on my edit and review buttons, it didn't know what I meant by 'id', as I hadn't specified what that was in the view context.
Thanks for the help all!
I think you're writing in your urls the wrong way, like this below your div text-center:
<a href="edit/{{ review.id }}" class="mx-2">
It should be:
<a href="{% url 'yourappname:edit' review.id %}">

How to show django form in django template?

I am trying to create a form using django and css.
views.py
from django.shortcuts import render
from .forms import ContactForm
def home(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
pass
else:
form = ContactForm()
return render(request, 'home.html', {'form':form})
forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length = 30)
email = forms.EmailField(max_length = 254)
message = forms.CharField(max_length = 2000, widget = forms.Textarea(),help_text = "Write Your Message here")
def clean(self):
cleaned_data = super(ContactForm, self).clean()
name = cleaned_data.get('name')
email = cleaned_data.get('email')
message = cleaned_data.get('message')
if not name and not email and not message:
raise forms.ValidationError('You have to write something!')
When I try to add the form to my html page like the following it doesn't show up. Just the button shows up, no form fields -
{% extends 'store/main.html' %}
{% load static %}
{% block content %}
<h3>Store</h3>
<form method = "post" novalidate>
{% csrf_token %}
{{ form }}
<button type='submit'>Submit</button>
</form>
{% endblock content %}
If I do css form instead it obviously show up the way it should.
{% extends 'store/main.html' %}
{% load static %}
{% block content %}
<h3>Store</h3>
<form>
<label for="fname">First Name</label>
<input type="text" id="fname" name="fname">
<button type='submit'>Submit</button>
</form>
{% endblock content %}
So I decided to add the form fields individually to the css form. Where does the {{form.name}} or {{form.email}} tag go?
EDIT:
Hey Vivek, the contact form code is this -
class ContactForm(forms.Form):
name = forms.CharField(max_length = 30)
email = forms.EmailField(max_length = 254)
message = forms.CharField(max_length = 2000, widget = forms.Textarea(),help_text = "Write Your Message here")
The html template looks like this-
{% extends 'store/main.html' %}
{% load static %}
{% block content %}
<h3>Store</h3>
<form method = "post" novalidate>
{% csrf_token %}
<label class="float-left" for="name">Name</label>
{{ form.name }}
<button type='submit'>Submit</button>
</form>
{% endblock content %}
Thanks for any input.
Accessing form fields individually will make you to render the form errors individually as well. {{form}} encapsulates everything:- Form fields , errors, non_field_errors..So if you have to access the fields individually do not forget to add the form errors.
I have written a sample code which will server the purpose.
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger" style="text-align:left">
<ul>
{% for field in form %}
{% for error in field.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="form-row">
<div class="col-md-4 mb-3">
<label class="float-left" for="id_name">Name</label>
{{ form.name }}
</div>
<div class="col-md-8 mb-3">
<label class="float-left" for="id_email">Email ID</label>
{{ form.email }}
</div>
</div>
<br>
<input type="submit" class="btn btn-primary" value="Pay" id="submit">
</form>

Django Nested inline formset-- nested form does not save to DB, no errors thrown

We have a few nested inline formsets in an application. Ideally, the goal is to allow for dynamic and unlimited population of these fields so that users can add an arbitrary number of notes. The form renders, the JS calls are populating; however, I am not seeing the update on the nested from manager.
This is my first Django project and I am not finding anything regarding what is causing the hang up. The Organization is saved in the DB, but the notes are not.
Thanks in advance for any help
Model.py:
class Organization(models.Model):
//irrelevant organization information//
class OrganizationNote(AbstractNotes):
note = models.TextField(blank=True)
org = models.ForeignKey(Organization, on_delete=models.CASCADE,blank=True, null=True)
modelforms.py:
class OrganizationForm(AbstractBigThree):
class Meta:
model = custModels.Organization
fields = '__all__'
orgNoteFormSet = inlineformset_factory(custModels.Organization, custModels.OrganizationNote,
form=OrganizationForm, extra=0)
ModelView.py
class OrganizationCreateView(CreateView, AbstractOrganizationView):
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
org_note_form = orgNoteFormSet()
return self.render_to_response(
self.get_context_data(form=form,
org_note_form=org_note_form))
def get_context_data(self, **kwargs):
data = super(OrganizationCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['notes'] = orgNoteFormSet(self.request.POST)
else:
data['notes'] = orgNoteFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
notes = context['notes']
with transaction.atomic():
self.object = form.save()
if notes.is_valid():
notes.instance = self.object
notes.save()
return super(OrganizationCreateView, self).form_valid(form)
def get_success_url(self):
return '/portal'
template:
{% extends 'base.html' %}
{% load i18n widget_tweaks %}
{% block __file__ %}
<!-- filename == organization_create_form.html -->
{% endblock %}
{% block container %}
<script type="text/javascript">
$(function() {
$(".inline.{{ org_note_form.prefix }}").formset({
prefix: "{{ org_note_form.prefix }}",
})
})
</script>
<div class="content">
<div class="thermometer">
<div style="float:left;padding:10px;">
Dashboard
</div>
<div style="float:left;padding:10px;">
>><a class="back-link" style="padding-left:10px;"href="">Organization List</a>
</div>
</div>
<div class="col-md-7 main">
<h1>Create Organization</h1>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
<div id = "form_set">
<legend>Notes</legend>
</div>
<input type="button" value="Add Note" id="add_more">
<div id="form_set">
{{ org_note_form.management_form }}
{{ org_note_form.non_form_errors}}
{% for form in org_note_form.forms %}
{{form.non_field_errors}}
{{form.errors}}
<table class='no_error'>
{{ form }}
</table>
{% endfor %}
</div>
<div id="empty_form" style="display:none">
<table class='no_error'>
<fieldset>
{{ org_note_form.empty_form}}
<div class="inline {{ org_note_form.prefix }}">
{{ form.note.errors }}
{{ form.note.label_tag }}
{{ form.note }}
</div>
</fieldset>
</table>
</div>
<div>
<input style="margin-top: 30px;" type="submit" class="btn btn-primary" value="Save" />
</div>
</form>
</div>
</div> {% endblock %}
{% block javascripts %}
<script type="text/javascript">
$('#add_more').click(function() {
var form_idx = $('#id_form-TOTAL_FORMS').val();
$('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
});
</script>
{% endblock %}
<script> $('#add_more').click(function() {
var form_idx = $('#id_organizationnote_set-TOTAL_FORMS').val();
$('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_organizationnote_set-TOTAL_FORMS').val(parseInt(form_idx) + 1);
});</script>
the issue was solved by editing the prefix of the model manager. By default django names the prefix as table_set which was missing from above.
add another option to your code:
def form_invalid(self, form):
print(form.errors)
...
to see if you have any validation errors in your form, don't forget to check errors in your inline

Django contact form doesnt submit

I am trying to set up a contact form. I have implemented the Django-crispy-forms and now my form is not submitted (I don't have any errors).
I've added action="" to my form in my template without any success.
forms.py
class ContactForm(forms.Form):
name = forms.CharField(max_length=100, help_text='Enter your name or username')
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea(attrs={'rows': 3, 'cols': 40}), help_text='Example: I forgot my password!')
views.py
def contact_us(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
sender_name = form.cleaned_data['name']
sender_email = form.cleaned_data['email']
message = "From {0}:\n\n{1}".format(sender_name, form.cleaned_data['message'])
send_mail('PLM_Tool contact', message, sender_email, ['myadress#gmail.com'])
return redirect('home:index')
else:
form = ContactForm()
return render(request, 'accounts/contact.html', {'form': form})
urls.py
app_name = 'accounts'
urlpatterns = [path('contact/', views.contact_us, name='contact'),]
contact.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block main %}
<form method="post" action="">
{% csrf_token %}
<div class="row">
<div class="col-6">
{{ form.name|as_crispy_field }}
</div>
<div class="col-6">
{{ form.email|as_crispy_field }}
</div>
<div class="col-6">
{{ form.message|as_crispy_field }}
</div>
</div>
</form>
<button type="submit" class="btn btn-success">Send</button>
Cancel
<br><br>
{% endblock %}
Here is the problem, and do not give action to form
crispy forms create the field not the button.
<form method="post">
{% csrf_token %}
<div class="row">
<div class="col-6">
{{ form.name|as_crispy_field }}
</div>
<div class="col-6">
{{ form.email|as_crispy_field }}
</div>
<div class="col-6">
{{ form.message|as_crispy_field }}
</div>
</div>
<button type="submit" class="btn btn-success">Send</button>
</form>
just add the button inside the form

Twitter-Bootstrap modal and django form

I'd like to show last_item in a Twitter-Bootstrap modal after django form submission, however I don't know how to handle the modal. I tried the form button suggested in documentation, but it doesn't process the form data. What do I have to do?
<button data-toggle="modal" data-target="#myModal2">Submit</button>
views.py
def main(request):
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
request.session['name'] = name
mm = MyModel.objects.create(name=name)
mm.save()
return HttpResponseRedirect('/') # Redirect after POST
else:
form = MyModelForm()
args = {}
args['last_item'] = MyModel.objects.all().order_by('pk').reverse()[0]
args['form'] = form
return render(request, 'form.html', args)
form.html
{% extends "base.html" %}
{% block content %}
<form method="POST" id="" action="">
{% csrf_token %}
{{ form.as_p }}
<button>Submit</button>
</form>
<div class="modal" id="myModal2" tabindex="-1" role="dialog"
aria-labelledby="myModal2Label" aria-hidden="true" style="display: none">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModal2Label">Modal header</h3>
</div>
<div class="modal-body">
<p>Last item: {{ last_item }}</p>
</div>
</div>
{% endblock %}
{% block scripts %}
{% endblock %}
It seems like bootstrap calls event.preventDefault() on click, which prevents the form from being submited.
You should bind your own event on this button and close the modal programaticaly.
It could look like:
$('form').submit(function() {
$('#myModal2').modal('hide');
})
I did not test this code but it should be a good start.