Jinja2 html buttons: Catch POST on different pages - python

I've got a GAE-app that uses Jinja2 templates to serve its html pages.
Now in my main python file I've got one class, mainhandler, with a GET and a POST method. This all works for the welcome screen where there is a button to do something. When the button is clicked, the POST method is invoked which calls a second page.
I can't find anything about how to catch the button events on the second page, result.html. And make it progress methods in the main python file.
So: "How can I work with errorMail and toCalendar buttons on result.html?
This is my main file:
# -*- coding: utf8 -*-
import webapp2
from apiclient.discovery import build
from oauth2client.appengine import OAuth2Decorator
from format import formatFile
import jinja2
import os
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
decorator = OAuth2Decorator(secret)
class MainHandler(webapp2.RequestHandler):
#decorator.oauth_required
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render())
#processes the file and shows the results
def post(self):
# Get the authorized Http object created by the decorator.
http = decorator.http()
service = build('calendar', 'v3', http=http,
developerKey='secret')
# Make a list of calendars
calendar_list = service.calendarList().list().execute()
totalList = formatFile(self.request.get('file'))
template_values = {"totalList": totalList, "calendar_list": calendar_list}
template = jinja_environment.get_template('result.html')
self.response.out.write(template.render(template_values))
app = webapp2.WSGIApplication([('/', MainHandler)],
debug=True)
This is page index.html:
<!DOCTYPE html>
<html>
<head><title></title></head>
<body>
<form method="post">
<div><label>Select file:</label</div>
<input type="file" name="file">
<br>
<input type="submit" name="upload" value="Upload">
</form>
</body>
</html>
This is page result.html:
<html>
<head>
</head>
<body>
<h3>De volgende data staat klaar voor je agenda:</h3>
<table border="1" cellpadding="3">
<tr>
<th>Dag</th>
<th>Datum</th>
<th>Tijd</th>
<th>Omschrijving</th>
</tr>
{% for line in totalList %}
<tr>
{% for item in line %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<br>
<b>Selecteer de agende waar de diensten in geplaatst worden:</b>
<br>
<select>
{% for calendar_list_entry in calendar_list['items'] %}
<option value=>{{ calendar_list_entry['summary'] }}</option>
{% endfor %}
</select>
<br>
<form method="post">
<input type="submit" name="toCalendar" value="In kalender plaatsen">
</form>
<br>
<b>Uitvoer incorrect? Klik dan op onderstaande knop om foutmeldings-email te sturen.</b>
<form method="post">
<input type="submit" name="errorMail" value="Uitvoer incorrect!">
</form>
</body>
</html>

You do not have to receive buttons events. You receive the form data (including the buttons) in the post like the self.request.get('file')
You can add more than one button to a post.
Every form can have its own post handler, by adding an action:
index.html (results in post to /result1):
<form action="/result1" method="post">
result.html (results in post to /result2):
<form action="/result2" method="post">
<input id="toCalender " type="submit" name="toCalendar" value="In kalender plaatsen">
<br>
<b>Uitvoer incorrect? Klik dan op onderstaande knop om foutmeldings-email te sturen.</b>
<input id="errorMail" type="submit" name="errorMail" value="Uitvoer incorrect!">
</form>

Related

How do I pass id from HTML to Python Flask [duplicate]

This question already has answers here:
Sending data from HTML form to a Python script in Flask
(2 answers)
Closed 1 year ago.
So I got a delete route to delete data from SQL. How do I get the id from HTML so I can Delete the data from application.py using db.execute("DELETE FROM birthdays WHERE name=?",id)
Delete method reference:https://www.digitalocean.com/community/tutorials/how-to-modify-items-in-a-one-to-many-database-relationships-with-flask-and-sqlite
This is my code:
application.py:
import os
from cs50 import SQL
from flask import Flask, flash, jsonify, redirect, render_template, request, session
# Configure application
app = Flask(__name__)
# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Configure CS50 Library to use SQLite database
db = SQL("sqlite:///birthdays.db")
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
# TODO: Add the user's entry into the database
name = request.form.get("name")
month = request.form.get("month")
date = request.form.get("date")
if not name or month or date :
return render_template("error.html", message="Missing something")
db.execute("INSERT INTO birthdays (name,month,day) VALUES(?,?,?)",name,month,date)
return redirect("/")
else:
# TODO: Display the entries in the database on index.html
entries=db.execute("SELECT * FROM birthdays" )
return render_template("index.html",entries=entries)
#FIX HERE
#app.route("/delete", methods=["GET", "POST"])
def delete(id):
if request.method == "post":
db.execute("DELETE FROM birthdays WHERE name=?",id)
return redirect("/")
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght#500&display=swap" rel="stylesheet">
<link href="/static/styles.css" rel="stylesheet">
<title>Birthdays</title>
</head>
<body>
<div class="jumbotron">
<h1>Birthdays</h1>
</div>
<div class="container">
<div class="section">
<h2>Add a Birthday</h2>
<!-- TODO: Create a form for users to submit a name, a month, and a day -->
<form action="/" method="post">
<input autocomplete="off" type="text" name="name" placeholder="Name">
<input autocomplete="off" type="number"min=1 max=12 name="month" placeholder="month">
<input autocomplete="off" type="number"min=1 max=31 name="date" placeholder="date">
<input type="submit" value="submit">
</form>
</div>
<div class="section">
<h2>All Birthdays</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Birthday</th>
</tr>
</thead>
<tbody>
<!-- TODO: Loop through the database entries to display them in this table -->
{% for person in entries%}
<tr>
<td>{{person.name}}</td>
<td>{{person.day}}/{{person.month}}</td>
<!--DELETE BUTTON-->
<td><form action="{{ url_for('delete', id=person.name) }}" method='post'><input type="submit" value="delete"></form></td>
</tr>
{% endfor%}
</tbody>
</table>
</div>
</div>
</body>
</html>
For the delete route, you need to add the id variable:
#FIX HERE
#app.route('/<int:id>/delete/', methods=('POST',))
def delete(id):
https://flask.palletsprojects.com/en/1.1.x/quickstart/#variable-rules

Django can't search "method not allowed"

im new to django and im currently doing a website for my friend. he wants me to make a system where the users can search the database and the website gives the relevent items according to their serial number.
i followed a tutorial from the following site: https://learndjango.com/tutorials/django-search-tutorial to figure out how to do db searchs which helped a lot, but im still having a problem: my search bar works, and the result page also works but it only works when i manually type the query on the searchbar myself (e.x. results/?q=number1). However when i search using the input bar and the submit button it sends me to /results/ page and the page gives this:
This page isn’t working
If the problem continues, contact the site owner.
HTTP ERROR 405
-when i open up pycharm to see the error in terminal it says:
Method Not Allowed (POST): /result/
Method Not Allowed: /result/
[27/Oct/2020 20:06:02] "POST /result/ HTTP/1.1" 405 0
here are my codes(python3.7,pycharm) websites/urls:
from . import views
from django.urls import path
from django.contrib.auth import views as auth_views
urlpatterns = [
path('register/',views.UserFormView.as_view(), name='register'),
path('login/', auth_views.LoginView.as_view(), name='login'),
path('', views.IndexViews.as_view(), name='index'),
path('scan/', views.ScanView.as_view(), name='scan'),
path('result/', views.SearchResultsView.as_view(), name='result'),
]
websites/views:
class IndexViews(generic.ListView):
template_name = "websites/index.html"
context_object_name = "object_list"
def get_queryset(self):
return Website.objects.all()
class ScanView(TemplateView):
form_class = SerialFrom
template_name = 'websites/scan.html'
class SearchResultsView(ListView):
model = SerialNumber
template_name = 'websites/result.html'
def get_queryset(self): # new
query = self.request.GET.get('q')
context = self.get_context_data(object=self.object)
object_list = SerialNumber.objects.filter(
Q(number__iexact=query)
)
return object_list
scan.html:
{% extends 'websites/base.html' %}
{% block albums_active %}active{% endblock %}
{% block body %}
<head>
<meta charset="UTF-8">
<title>Scan</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<form class="box" action="{% url 'result' %}" method="POST">
<h1>Product Check</h1>
<p> Please enter the serial id of your product to check it.</p>
{% csrf_token %}
<input type="text" name="q" placeholder="Serial Number">
<input type="submit" name="q" placeholder="Check">
</form>
</body>
{% endblock %}
thank you for taking your time and reading, please help me i really need to do this.
A ListView [Django-doc] by default does not implement a handler for a POST request. Searching is normally done through a GET request, so you should use:
<form class="box" action="{% url 'result' %}" method="GET">
<h1>Product Check</h1>
<p> Please enter the serial id of your product to check it.</p>
<input type="text" name="q" placeholder="Serial Number">
<input type="submit" placeholder="Check">
</form>
Furthermore the <input type="submit"> should not have a name="q" attribute.
As #Melvyn says, you can also alter the type to type="search" [mozilla] for the text box:
<form class="box" action="{% url 'result' %}" method="GET">
<h1>Product Check</h1>
<p> Please enter the serial id of your product to check it.</p>
<input type="search" name="q" placeholder="Serial Number">
<input type="submit" placeholder="Check">
</form>

Flask - pass form input to url_for

I have an HTML template that lets the user select a date via jQuery datepicker.
How can I pass the date selected into an action?
The idea is, the user selects a date, then that passes to Flask's route.py, via app.route("/date/<date>")
calendar.html
{% block topscripts %}
<link rel="stylesheet" type="text/css" href= "{{ url_for('static',filename='styles/calendar.css') }}">
<script>
$(function() {
$("#datepicker").datepicker({dateFormat: 'yy-mm-dd'});
});
</script>
{% endblock %}
{% block content %}
<form method="post" action="{{ url_for('specific_date', date='2019-04-11') }}">
<p>Date: <input type="text" id="datepicker" name='go-to-date'></p>
<input type="hidden" name="calendar-form">
<input type="submit">
</form>
{% endblock %}
So, when the user selects a date in the datepicker ID, I want to pass that date to the url_for. Currently I hardcoded the date (2019-04-11) just to check that it works, and it does. How can I have that part be dynamic to whatever the user selects in the Calendar?
...If it helps, here's in routes.py (default_template() is the function that renders the template in the end).:
#app.route("/date/<date>/", methods=["GET", "POST"])
def specific_date(date):
print("\n\nDate:", date, "\n\n")
images = get_files_on(date)
print("\n\nSpecific date images:", images)
return default_template(date=date, image_list=images)
Make a POST request to the /date route like so.
Changes to calendar.html:
{% block content %}
<form method="post" action="{{ url_for('specific_date') }}">
<p>Date: <input type="text" id="datepicker" name='go-to-date'></p>
<input type="hidden" name="calendar-form">
<input type="submit">
</form>
{% endblock %}
Changes to the date route:
from flask import request
# only allow POST request method
#app.route("/date/", methods=["POST"])
def specific_date():
# getting the date from the POST request
date = request.form['go-to-date']
print("\n\nDate:", date, "\n\n")
images = get_files_on(date)
print("\n\nSpecific date images:", images)
return default_template(date=date, image_list=images)

400 Bad Request python flask

Hi I'm new to python flask, i have what seems a simple issue of a 400 bad request error, that should be due to having wrongly named variables in form when trying to pass them to a function in python flask. I have done some research but i still can't figure out where i go wrong with this code, any help would be really appreciated.
Here is the code for the html form
<html>
<body>
<h1>Add a Munroe to your list</h1>
<form action = "{{ url_for('addmunro') }}" method="POST"
enctype = "multipart/form-data">
Name<br>
<input type="text" name="mnName"/><br>
Description<br>
<input type="text" name="mnDesc"/><br>
Region<br>
<input type="text" name="mnRegion"/><br>
Height<br>
<input type="text" name="mnHeight"/><br>
Walk date<br>
<input type="text" name="mnDate"/><br>
Walk image<br>
<input type="text" name="mnImage"/><br>
<br>
<br>
<input type="submit" name="add-munro.html" value = "ADD MUNRO"/>
</form>
</body>
</html>
And here is the code for the python flask application
from flask import Flask, render_template, url_for, redirect, json, request
app = Flask(__name__)
#app.route('/add-munro.html', methods=['GET'])
def addmunro():
#Create an empty list
mnList={}
#Create a munro dictionary
munro = {'name':request.form['mnName'],
'desc':request.form['mnDesc'],
'region':request.form['mnRegion'],
'height':request.form['mnHeight'],
'date':request.form['mnDate'],
'image':request.form['mnImage']}
#the munro dictionary is added to mnList
#mnList.append(munro)
return render_template('add-munro.html')
if __name__ == "__main__":
app.run(host = '0.0.0.0', debug = True)
There are several mistakes:
You are making POST request but handling only GET request
.html is not needed in routing
munro object is not passed to the template
I have updated these and now it's good to go:
application.py
from flask import Flask, render_template, request, url_for
app = Flask(__name__)
#app.route('/add-munro', methods=['GET','POST'])
def addmunro():
if request.method == "POST":
#Create an empty list
mnList={}
#Create a munro dictionary
munro = {'name':request.form['mnName'],
'desc':request.form['mnDesc'],
'region':request.form['mnRegion'],
'height':request.form['mnHeight'],
'date':request.form['mnDate'],
'image':request.form['mnImage']}
return render_template('add-munro.html', munro=munro)
else:
return render_template('add-munro.html')
if __name__ == '__main__':
app.run(debug=True)
add-munro.html
<html>
<body>
{% if munro is defined -%}
<h3>
Name: {{ munro.name }}
</h3>
<h3>
Description: {{ munro.desc }}
</h3>
<h3>
Region: {{ munro.region }}
</h3>
<h3>
Height: {{ munro.height }}
</h3>
{%- endif %}
<h1>Add a Munroe to your list</h1>
<form action = "{{ url_for('addmunro') }}" method="POST"
enctype = "multipart/form-data">
Name<br>
<input type="text" name="mnName"/><br>
Description<br>
<input type="text" name="mnDesc"/><br>
Region<br>
<input type="text" name="mnRegion"/><br>
Height<br>
<input type="text" name="mnHeight"/><br>
Walk date<br>
<input type="text" name="mnDate"/><br>
Walk image<br>
<input type="text" name="mnImage"/><br>
<br>
<br>
<input type="submit" value = "ADD MUNRO"/>
</form>
</body>
</html>
Output:
You are making a POST request in your form, but only allow the GET method in your app route. Change #app.route('/add-munro.html', methods=['GET']) to #app.route('/add-munro.html', methods=['POST']).

good way to redirect url in a django app

I'm constructing a Django app where I have a main page that links to different URLs to perform actions (for example there's a link to add a record to the database). This is done using forms in the templates and changes the current URL from app/index to app/add. The problem is that when I use render_to_response in the script that manages that action it doesn't change the URL but it does change the html being used back to the index page. So this works if you use the links in the app but if the user at any point tries to refresh one of those pages it might perform the action again (ie if you refresh app/add it'll add that record again). I would use HttpResponseRedirect or redirect, but I need to pass a context that includes a jobList (see below wherever I use render_to_response). My current fix is to lump all of the action into the index page's script and have all of action buttons redirect to it with options submitted but this is a terrible way to structure a program I feel and surely there has to be a better way to do this. Hope this isn't too much text. For reference, here's a couple of the relevant files. Thanks in advance for any help.
urls.py
from django.conf.urls import patterns, include, url
from django.views.generic import DetailView, ListView
from django.conf.urls.defaults import *
from Minion.models import Job
urlpatterns = patterns('Minion.views',
# URLs for the MinionUI app
url(r'^$', 'home'),
url(r'^index/$', 'detail'),
url(r'^add/$', 'add'),
url(r'^output/$', 'output'),
url(r'^change/$', 'change'),
url(r'^cal/$', 'calendarTest'),
url(r'^delete/$', 'delete'),
url(r'^change/$', 'change'),
)
views.py (only parts of it, don't want to clutter the screen too much)
#This script is responsible for whatever operations need to happen before viewing the index page again and then displaying the index page
#This includes:
#user authentication
#adding entries to the database
def detail(request):
if request.user.is_authenticated():
if 'isAdding' in request.POST:
#Add data to database
job = Job()
job.name = request.POST['name']
job.user = request.POST['user']
job.command = request.POST['command']
job.ipRange = request.POST['ipRange']
job.whenToRun = str(request.POST['dateToRun']) + ' ' + str(request.POST['timeToRun'])
job.save()
elif 'isChanging' in request.POST:
#Change selected data in database
job = Job()
job.name = request.POST['name']
job.user = request.POST['user']
job.command = request.POST['command']
job.ipRange = request.POST['ipRange']
job.whenToRun = str(request.POST['dateToRun']) + ' ' + str(request.POST['timeToRun'])
job.save()
jobList = Job.objects.all()
return render_to_response('Minion/detail.html', {'jobList': jobList}, context_instance=RequestContext(request))
else: #need to authenticate user
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
jobList = Job.objects.all()
return render_to_response('Minion/detail.html', {'jobList': jobList}, context_instance=RequestContext(request))
else:
return render_to_response('Minion/home.html', {'loginFail': True}, context_instance=RequestContext(request))
else:
return render_to_response('Minion/home.html', {'loginFail': True})
else:
return render_to_response('Minion/home.html', {'improperURL': True}, context_instance=RequestContext(request))
detail.html(main index template)
{% load i18n %}
<head>
<!script src="//ajax.googleapis.com/ajax/libs/jquery/jquery.min.js" />
<script type="text/javascript">
//These two function are used to change where we direct our POST data to
function determineWhereToGo(){
try{
var element = document.getElementById('dropdown');
var selected = element.options[element.selectedIndex].text;
if(selected === "Delete"){
document.getElementById('checkboxes').action = "{% url 'Minion.views.delete' %}";
}else if(selected === "Change"){
document.getElementById('checkboxes').action = "{% url 'Minion.views.change' %}";
}
}catch(err){
alert("Javascript Error\n" + err.message);
}
}
</script>
</head>
<table border="2">
<tr>
<th />
<th>Job ID</th>
<th>Name</th>
<th>User</th>
<th>Command</th>
<th>IP Range</th>
<th>Date to Run</th>
</tr>
<form id="checkboxes" method="post" action="{%url 'Minion.views.detail' %}">
{% csrf_token %}
{% for job in jobList %}
<tr>
<td><input type="checkbox" name="{{ job.id }}Selection"/></td>
<td>{{ job.id }}</td>
<td>{{ job.name }}</td>
<td>{{ job.user }}</td>
<td>{{ job.command }}</td>
<td>{{ job.ipRange }}</td>
<td>{{ job.whenToRun }}</td>
</tr>
{% endfor %}
<select id="dropdown" onchange="determineWhereToGo();">
<option />
<option value="Delete">Delete</option>
<option value="Change">Change</option>
</select>
<input type="submit" value="Go" />
</form>
<form method="post" action="{% url 'Minion.views.add' %}">
{% csrf_token %}
<input type="submit" value="Add" />
</form>
</table>
add.html (this is a template with the use of the isAdding option selected for reference)
{% load i18n %}
<h1>Add a Job</h1>
<hr width="18%" size="3" align="left" />
<form method="post" action="{% url 'Minion.views.detail' %}">
{% csrf_token %}
Name<input type="text" name="name" /><br /><br />
User<input type="text" name="user" /><br /><br />
Command<input type="text" name="command" /><br /><br />
IP Range<input type="text" name="ipRange" /><br /><br />
Date to Run<input type="date" name="dateToRun" value="YYYY-MM-DD" /><br /><br />
Time to Run<input type="time" name="timeToRun" value="HH:MM:SS" /><br /><br />
<input type="hidden" name="isAdding" value="True" />
<input type="submit" value="Add Job" />
</form>

Categories