Prevent confirm form resubmission with flask - flask

I have a flask app with a form. When the user submits the form, the data is saved and a different message is shown in place of the form. When the user refreshes the page, I get the confirm form resubmission message. I know that I need to use a redirect to avoid this, but I want the site to be one page, so I don't want to redirect to another. If I redirect to "/", then it works, but (as expected) the "thank you" message is not shown. Is it possible to keep the site as a single page, avoid the form resubmission message but still show the thank you message?
main.py
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
import forms
app = Flask(__name__)
app.config["SECRET_KEY"] = ""
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///test.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
class Booking(db.Model):
id = db.Column(db.Integer, primary_key=True)
first = db.Column(db.Text, nullable=False)
last = db.Column(db.Text, nullable = False)
telno = db.Column(db.Text, nullable=False)
message = db.Column(db.Text, nullable = False)
#app.route("/", methods = ["GET", "POST"])
def main():
filled = False
form = forms.BookingForm()
if form.validate_on_submit():
copy = dict(form.data)
del copy["submit"]
del copy["csrf_token"]
filled = True
db.session.add(Booking(**copy))
db.session.commit()
return render_template("formtest.html", form = form, filled = filled)
if __name__ == "__main__":
app.run(debug=True)
forms.py
from wtforms import validators
from wtforms.fields.html5 import TelField
from wtforms.validators import DataRequired
class BookingForm(FlaskForm):
first = StringField("First name", validators = [DataRequired()])
last = StringField("Last name", validators= [DataRequired()])
telno = TelField("Phone number", validators=[DataRequired()])
message = TextField("Please provide details of your appointment:", validators = [DataRequired()])
submit = SubmitField("Submit")
formtest.html
{% extends "base.html" %}
{% block head %}
<title>Booking form</title>
{% endblock %}
{% block body %}
{% if not filled %}
<form action = "/" method = "POST">
{{ form.hidden_tag() }}
<table>
<tr>
<td>{{ form.first.label }}</td>
<td>{{ form.last.label }}</td>
</tr>
<tr>
<td>{{ form.first() }}</td>
<td>{{ form.last() }}</td>
</tr>
<tr>
<td>{{ form.telno.label }}</td>
</tr>
<tr>
<td>{{ form.telno() }}</td>
</tr>
<tr>
<td>{{ form.message() }}</td>
</tr>
<tr>
<td>{{ form.submit() }}</td>
</tr>
</table>
</form>
{% else %}
<p>Thank you for your interest</p>
{% endif %}
{% endblock %}

Add below script in your html file.
<script>
if ( window.history.replaceState ) {
window.history.replaceState( null, null, window.location.href );
}
</script>

You could try to pass the filled parameter in the redirect call like this:
#app.route("/", methods = ["GET", "POST"])
def main():
form = forms.BookingForm()
filled = request.args.get('filled')
if filled is None:
filled = False
if form.validate_on_submit():
copy = dict(form.data)
del copy["submit"]
del copy["csrf_token"]
filled = True
db.session.add(Booking(**copy))
db.session.commit()
return redirect(url_for('main',filled=filled))
return render_template("formtest.html", form = form, filled = filled)

Related

How can I capture the name or reg_no of the book in this list?

I'm working on a library system. I am unable to get the registration number of a book/books to be returned back to library...
My intention is to click on Return which captures the book name for return processing.. With what I have, when I print(book) it returns None meaning nothing has been taken from the click
My models
class Books(models.Model):
DEPARTMENT = (
('COM', 'Computer'),
('ELX', 'Electronics'),
('CIV', 'Civil'),
('BBS', 'Business'),
('MSC', 'Miscellaneous'),
)
reg_no = models.CharField(max_length=20, blank=True)
book_name = models.CharField(max_length=200)
no_of_books = models.IntegerField()
book_detail = models.TextField(default='text')
department = models.CharField(max_length=3, choices=DEPARTMENT)
def Claimbook(self):
if self.no_of_books>1:
self.no_of_books=self.no_of_books-1
self.save()
else:
print("not enough books to Claim")
def Addbook(self):
self.no_of_books=self.no_of_books+1
self.save()
def __str__(self):
return self.book_name
class Return(models.Model):
return_date = models.DateField(default=datetime.date.today)
borrowed_item = models.ForeignKey(Issue,on_delete=models.CASCADE)
def new_issue(request):
if request.method == 'POST':
i_form = IssueForm(request.POST)
if i_form.is_valid():
name = i_form.cleaned_data['borrower_id']
book = i_form.cleaned_data['book_id']
i_form.save(commit=True)
books = Books.objects.get(book_name=book)#Get a book names as selected in the dropdown
semest = Student.objects.get(name=name).semester#Get a student with a semester as selected in the dropdown
departm = Student.objects.get(name=name).depart
Books.Claimbook(books)
return redirect('new_issue')
else:
i_form = IssueForm()
semest = None
departm = None
sem_book = Semester.objects.filter(sem=semest, depart=departm)
return render(request, 'libman/new_issue.html', {'i_form': i_form, 'sem_book': sem_book})
The return view
def return_book(request):
book = request.GET.get('book_pk')
print(book)
books = Books.objects.get(id=book)
#b_id = r_form.cleaned_data['borrower_id']
Books.Addbook(books)
Issue.objects.filter(borrower_id=1, id=book).delete()
return render(request,'libman/view_issue.html',{'issue':issue})
The template that displays the borrowed books with a link to return beside each book.
{% if issue %}
<table class="layout">
<thead>
<th>Reg No.</th>
<th>Student Name</th>
<th>Book Name</th>
<th>Issue Date</th>
<th>Action</th>
</thead>
{% for borrow in issue %}
<tr>
<td>{{ borrow.borrower_id.student_id }}</td>
<td>{{ borrow.borrower_id }}</td>
<td>{{ borrow.book_id }}</td>
<td>{{ borrow.issue_date }}</td>
<td name='book_pk'>Return </td>
</tr>
{% endfor %}
</table>
{% else %}
<p> There are no books registered. </p>
{% endif %}
Issue model
class Issue(models.Model):
borrower_id = models.ForeignKey(Student,on_delete=models.CASCADE)
book_id = models.ForeignKey(Books,on_delete=models.CASCADE)
issue_date = models.DateField(default=datetime.date.today)
def __str__(self):
return str(self.book_id)
if i understood correctly - I believe you need to pass the borrow.book_id to the return view. so the return view knows which book you want return
in your template add the variable book_pk as follows
<td name='book_pk'>Return </td>
also you need to update your urls.py file to accept the new variable something like this
urlpatterns = [
path('returnbook/<book_pk>/', return_book),
]
but the above will need to also update your view function to handle the new passed argument and fetch the object etc..
def return_book(request,book_pk):
Or
you can add a form with a submit button
<form action="{% url 'return_book' %}">
<label for="book_id">Borrowed Book_id</label>
<input type="text" id="book_id" name="book_pk" value="{{ borrow.book_id }}" disabled><br><br>
<input type="submit" value="Submit">
</form>
it should work with your current code i think

How to create a table of update forms in Django

I want the ability to update records in a table format so that I can quickly make updates. I am close to figuring this out, but form.valid() is still returning False.
My model:
class Actions(models.Model):
meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE)
dateAdded = models.DateTimeField(default = timezone.now, editable = False)
dateComplete = models.DateTimeField(null=True, blank=True)
action = models.TextField(max_length=1000,)
responsibility = models.ForeignKey(staff, on_delete=models.CASCADE, blank=True,null = True,)
complete = models.BooleanField(default = False)
My view:
def actionItemsView(request):
ActionFormSet = modelformset_factory(Actions, fields=('action', 'responsibility','complete','meeting','dateComplete'),max_num=1)
if request.method == "POST":
action_formset = ActionFormSet(request.POST, request.FILES,queryset=Actions.objects.filter())
for action_form in action_formset:
print(action_form.errors)
if action_form.is_valid():
action = action_form.save()
return HttpResponseRedirect('/saved!/')
else:
formset = ActionFormSet(queryset=Actions.objects.filter(complete = False))
return render(request, 'action_items.html', {'formset': formset})
My template:
<table class="table table-hover table-sm">
<tr>
<th>decision</th>
<th>responsibility</th>
<th>complete?</th>
<th>meeting</th>
<th>date complete</th>
<th>submit</th>
</tr>
{%for form in formset%}
<form method="post" enctype= multipart/form-data>
<tr>
{{ formset.management_form }}
{{ form.management_form }}
{% csrf_token %}
<td>{{ form.action }}</td>
<td>{{ form.responsibility }}</td>
<td>{{ form.complete }}</td>
<td>{{ form.meeting }}</td>
<td>{{ form.dateComplete }}</td>
<td><button type="submit">Save</button></td>
</tr>
</form>
{% endfor %}
</table>
When I run this, the template is rendered exactly how I would expect, but when I make any changes to an item and hit submit, it throws The view meetings.views.actionItemsView didn't return an HttpResponse object. It returned None instead.
Because form.valid() is False
form.errors is returning:
<ul class="errorlist"><li>id<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
<ul class="errorlist"><li>action<ul class="errorlist"><li>This field is required.</li></ul></li><li>meeting<ul class="errorlist"><li>This field is required.</li></ul></li><li>id<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
<ul class="errorlist"><li>action<ul class="errorlist"><li>This field is required.</li></ul></li><li>meeting<ul class="errorlist"><li>This field is required.</li></ul></li><li>id<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
But I can see in the template that each record has a meeting assigned to it in the dropdown...
The view meetings.views.actionItemsView didn't return an HttpResponse object. It returned None instead.
Given this is the error, it's saying that you are not returning any HTTPRESPONSE, which is true since you don't have any return statements if it's a POST request.
if request.method == "POST":
action_formset = ActionFormSet(request.POST, request.FILES,queryset=Actions.objects.filter())
for action_form in action_formset:
print(action_form.errors)
if action_form.is_valid():
action = action_form.save()
All methods in views.py are required to return an HTTPRESPONSE, so simply try to add any kind of HTTP response in there and it should solve your problem.

How to append values in a tabular format from JSON in Django Template?

I am making a small web application that allow user to perform OCR on the selected image and provides the JSON output in a tabular Format like this.
JSON DATA :
{
"data": {
"test_1": {
"test_name": "FASTING SUGAR",
"results": "121.00",
"units": "mg%",
"low_range": 70.0,
"high_range": 110.0
}
}
}
What I'm facing now is when I'm selected new image to add values in the table, it overrides the values instead of appending.
And what I want is to append new value in the table by choosing another image, the previous row show me stay as it is and new will get append.
So that finally I can submit all the values to the database all together by clicking on the submit button.
Here is my template code :
<form method="post">{% csrf_token %}
<div class="table-responsive">
<table id="datatable2" class="table order-column hover">
<thead>
<tr>
<th>Investigation Name</th>
<th>Result</th>
<th>UOM</th>
<th>Low Range</th>
<th>High Range</th>
</tr>
</thead>
<tbody>
{% for key, value in data.items %}
{% for key2,value2 in value.items %}
<tr class="gradeX">
<td>{{ value2.test_name }}</td>
<td>{{ value2.results }}</td>
<td>{{ value2.units }}</td>
<td>{{ value2.low_range }}</td>
<td>{{ value2.high_range }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
<!--end .table-responsive -->
<button type="submit" class="btn btn-primary" name="data">Submit</button>
</form>
And my views.py
from django.shortcuts import render, redirect
import json
import urllib.request
from .models import Panel
from django.contrib import messages
from .forms import PanelForm, SingleDataForm
def photo_single(request):
if request.method=='POST':
form = PanelForm(request.POST, request.FILES)
form1 = SingleDataForm()
if form.is_valid():
print("valid")
temp,data = form.save()
return render(request, 'album/photo_single.html', {'data': data, 'form1':form1, 'form': form})
#return redirect('photo_list')
else:
form = PanelForm()
if request.method=='POST' and 'data' in request.POST:
form1 = SingleDataForm(request.POST)
if form1.is_valid():
print("INside this loop")
if form1.save():
print("INside this loop")
return redirect('album/photo_single.html', messages.success(request, 'Order was successfully created.', 'alert-success'))
else:
return redirect('album/photo_single.html', messages.error(request, 'Data is not saved', 'alert-danger'))
else:
return redirect('album/photo_single.html', messages.error(request, 'Form is not valid', 'alert-danger'))
else:
form1 = SingleDataForm()
return render(request, 'album/photo_single.html', {'form': form, 'form1':form1})
How can we use list in DTL to store the previous values and stay as it is and the new value just get append in the next row.. ?
I'm new in Django. Please Guide. Thanks.

How to display specific output for specific username in django?

I have made a simple django app. In my models.py I have defined a table-
class events(models.Model):
id_campaign = models.CharField(default='newevent',max_length=100)
status = models.CharField(default='100',max_length=100)
name = models.CharField(default='100',max_length=100)
This is my views.py -
from django.shortcuts import render
# Create your views here.
from django.views.generic import TemplateView
from django.shortcuts import render
from website.models import *
from website.models import events
from django.views.generic import ListView
class HomePageView(TemplateView):
template_name = 'base.html'
class AboutPageView(TemplateView):
template_name = 'about.html'
class ContactPageView(TemplateView):
template_name = 'contact.html'
class Success(TemplateView):
template_name = 'success.html'
def get_context_data(self, **kwargs):
context = super(Success, self).get_context_data(**kwargs)
query_results = events.objects.all()
context.update({'query_results': query_results})
return context
And this is the success.html -
{% if user.is_authenticated %}
<header>
Logout
</header>
{% block content %}
<h2>you are logged in successfully. this is your personal space.</h2>
<table>
<tr>
<th>id_campaign</th>
<th>status</th>
<th>name</th>
</tr>
{% for item in query_results %}
<tr>
<td>{{ item.id_campaign }}</td>
<td>{{ item.status }}</td>
<td>{{ item.name }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
{% else %}
<h2>you are not logged in.</h2>
{%endif %}
I need to check the username in success.html and based on the user I need to show results, how do I do that?
if request.user.is_authenticated():
username = request.user.username
if username is 'xyz' then I want to display the values from query_results and if username is 'abc' then I don't want to display.
P.S. - I am absolutely new to django
You can use builtin template tag if and do it as below
{% if username == 'xyz' %}
{% else %}
{% endif %}
Refer this link for documentation.

How to pass a variable from a template to a view in Django

I am not able to GET a variable from a template into another view.
I have a table with some records. Each row has a button which I would like to click and retrieve more details about the record in another page. I have been looking online but I cannot figure out how I should implement this. Everything I have tried either crashed or gave back None.
list.html
{% for trainee in trainees_list %}
<tr>
<td>{{ trainee.last_name }}</td>
<td>{{ trainee.first_name }}</td>
<td><a class="btn btn-primary" href="{% url 'traineedetails'%}" value="{{ trainee.pk }}" >View</a></td>
</tr>
{% endfor %}
view.py
def traineedetails(request):
if request.method == 'GET':
trainee_details = request.POST.get('trainee.pk')
print(trainee_details)
return render(request, 'trainee_details.html')
def listoftrainees(request):
trainees_list = UserTraining.objects.all()
return render_to_response('list.html', {'trainees_list': trainees_list})
url.py
urlpatterns = [
path('traineedetails', views.traineedetails, name='traineedetails'),
path('listoftrainees', views.listoftrainees, name='listoftrainees'),
]
form.py
class UserTrainingForm(forms.ModelForm):
scope_requirements = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=microscopes.MICROSCOPES)
class Meta:
model = UserTraining
fields = (
'first_name',
'last_name',
)
model.py
class UserTraining(models.Model):
first_name = models.CharField('first name', max_length = 100)
last_name = models.CharField('last name', max_length = 100)
I would like to be able to click on the button in the row of the table and retrive more information about the record.
You pass the value directly in the url like : traineedetails/<pk>
In the template:
{% for trainee in trainees_list %}
<tr>
<td>{{ trainee.last_name }}</td>
<td>{{ trainee.first_name }}</td>
<td><a class="btn btn-primary" href="{% url 'traineedetails' trainee.pk%}">View</a></td>
</tr>
{% endfor %}
Edit your urls.py:
path('traineedetails/<pk>', views.traineedetails, name='traineedetails'),
Then you can retrieve it in your view like this:
from django.shortcuts import get_object_or_404
def traineedetails(request, pk):
if request.method == 'GET':
#try to get your objet or throw a 404 error
trainee = get_object_or_404(UserTraining, pk=pk)
#pass it back to the template
return render(request, 'trainee_details.html',{'trainee':trainee})