Flask - pass form input to url_for - python

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)

Related

Django AdminDateWidget apprearing as TextInput

I don't understand how widgets work.
I tried this minimum example :
in my forms.py
class PartialResetForm(forms.Form):
date = forms.DateField(
label="Starting date",
widget=AdminDateWidget()
)
in my admin/intermediary_reset_page.html
{% extends "admin/base_site.html" %}
<!--Loading necessary css and js -->
{{ form.media }}
{% block content %}
<form action="" method="post">{% csrf_token %}
<!-- The code of the form with all input fields will be automatically generated by Django -->
{{ form }}
<!-- Link the action name in hidden params -->
<input type="hidden" name="action" value="custom_action" />
<!-- Submit! Apply! -->
<input type="submit" name="apply" value="Submit" />
</form>
{% endblock %}
in my admin.py as the definition of an action
def custom_action(self, request, queryset):
form = PartialResetForm()
return render(request, "admin/intermediary_reset_page.html", {
"items": queryset, "form": form
})
For now I don't care about the queryset, it will be my next topic. With this simple example, I wanted to have a calendar in order to help pick a date, but only a TextInput appeared. I believe it is due to the fact that AdminDateWidget inheritates from TextInput.
My question is why isn't it appearing as a calendar ? I imported the media and declared my widget, I don't understand what else I'm supposed to do.
you should declare type
AdminDateWidget(attrs={'type': 'date'})
And it should be enough ;)

Django: Pass arguments from other form to submit button

I'm building a simple Django app that lets users track stuff for specific days:
It records entries with a name and a date using the upper form.
<form action="" method="post" style="margin-bottom: 1cm;">
{% csrf_token %}
<div class="form-group">
{{ form.entry_name.label_tag }}
<div class="input-group">
<input type="text" class="form-control" id="{{ form.entry_name.id_for_label }}" name="{{ form.entry_name.html_name }}" aria-label="new entry field">
{{ form.entry_date }}
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
<small id="{{ form.entry_name.id_for_label }}Help" class="form-text text-muted">This can be anything you want to track: An activity, food, how you slept, stress level, etc.</small>
</div>
</form>
Below the form, there are quick add buttons that let users quickly add a new entry with a specific name. In addition, I'd like to use the date selected in the form above. I.e., if a user sets a date in the upper form but then clicks one of the suggested buttons, it should still use the selected date for adding the new entry.
This is what the code for the suggested buttons currently looks like:
{% if entry_counts and entry_dict|length > 0 %}
<div class="card" style="margin-bottom: 1cm;">
<div class="card-body">
<div class="card-title">Suggested entries</div>
{% for name, count in entry_counts.items %}
<form method="post" action="{% url 'app:add_entry_with_date' name form.entry_date.value %}" style="display: inline-block;">
{% csrf_token %}
<button type="submit" class="btn btn-secondary" name="{{ name }}" style="margin-bottom: 5px;">{{ name }}</button>
</form>
{% endfor %}
</div>
</div>
{% endif %}
I'm trying to access the selected date and pass it to the corresponding view: action="{% url 'app:add_entry_with_date' name form.entry_date.value %}", but it still adds the entry at the default date (today) not on the selected date.
My guess, is that the problem is with <button type="submit" class="btn btn-secondary" name="{{ name }}" style="margin-bottom: 5px;">{{ name }}</button>. Does this just pass name but not the date when submitting?
Here are the relevant URL patterns:
class DateConverter:
regex = '\d{4}-\d{2}-\d{2}'
def to_python(self, value):
return datetime.datetime.strptime(value, '%Y-%m-%d')
def to_url(self, value):
return value
register_converter(DateConverter, 'yyyymmdd')
urlpatterns = [
path('', views.index, name='index'),
path('add/<entry_name>/', views.add_entry, name='add'),
path('add/<entry_name>/<yyyymmdd:entry_date>/', views.add_entry, name='add_entry_with_date'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
So whenever adding a new entry (with or without specific date), my add_entry view is called:
#login_required
def add_entry(request, entry_name, entry_date=datetime.date.today()):
# only works for post
# if request.method == 'POST':
entry_name = entry_name.strip().lower()
entry = Entry.objects.create(name=entry_name, date=entry_date, owner=request.user)
return HttpResponseRedirect(reverse('app:index'))
You're trying to pass the date value as part of the URL,
{% url 'app:add_entry_with_date' name form.entry_date.value %}
however, form.entry_date.value won't have a defined value unless your form is bound before it's passed to the template for rendering. As a result, probably, your add_entry view is being called via the URL pattern add, not add_entry_with_date.
Another challenge with your current code is that you want to have the same date-type input element ({{ form.entry_date }}) serve as the source for different, separate HTML forms (you have the first form for adding entries, and then you have one form for each suggested entry). Changing the value of that input when the page is already rendered in the browser won't update the action URLs for the suggested entry forms—unless you use JavaScript.
I think the quickest way to make your existing code work is to write some JavaScript to manipulate the action attribute for the suggested-entry forms whenever the date input value changes.
Manipulating action attributes looks strange though, and also I believe your view, which should work only for POST requests, should use only POST data and not rely on URL parameters. Therefore I recommend that you use hidden inputs, e.g.
<input type="hidden" name="variable-name" value="temporary-date-value-here">
and then have the JavaScript manipulate these input elements' values instead of the form action attribute. Of course you have to update the view too.
Update: sample JS for synchronizing inputs across forms
HTML:
<html>
<head>
<title>Sample synchronization of inputs across forms</title>
</head>
<body>
<h1>Sample synchronization of inputs across forms</h1>
<h2>Form 1</h2>
<form>
<input class="synchronized-inputs" type="date" name="input_date">
</form>
<h2>Form 2</h2>
<form>
<input class="synchronized-inputs" type="date" name="input_date">
</form>
<script src="sync-inputs-across-forms.js" type="text/javascript"></script>
</body>
</html>
JavaScript (sync-inputs-across-forms.js):
var syncedInputs = document.getElementsByClassName('synchronized-inputs');
Array.from(syncedInputs).forEach((source) => {
source.addEventListener('change', () => {
Array.from(syncedInputs).forEach((target) => {
target.value = source.value;
});
});
});
Note that:
Without the JS, selecting a date in one form won't update the other form's value
As indicated in the original answer, you'd want to use hidden inputs for the suggested-entry forms. To do that, just change type="date" to type="hidden" for the other form. Synchronization will still work as the value is tracked in the (invisible parts of the) DOM.

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']).

Django templates not altering form action

I have a URL file chat.urls.py:
`urlpatterns = patterns('',
url(r'^message/(?P<username>\w+)/$',views.message,name='message'),
url(r'^message/(?P<username>\w+)/submit/$',views.send_message,name='send_message'),
url(r'^inbox/$',views.inbox,name='inbox'),
url(r'^inbox/(?P<username>\w+)/$', views.inbox_by_user,name='inbox_by_user'),
)`
and a message.html template to send a message from with a form like this:
<form action="{% url 'inbox' %}" method="post">
{% csrf_token %}
<input type="text" name="text" id="text" value="" />
<label for="message">Enter your message here</label><br />
<input type="submit" value="Send" />
</form>
where I substituted previously working code for "url 'inbox'", and no matter what I substitute for the form action I always get html source rendered as
<form action="/chat/message/[username]/" method="post"...
no matter what. I have restarted the server, made sure I saved changes, and like it has a mind of its own, it's always /chat/message/[username]. When I changed that URL reverse to 'inbox' I should see chat/inbox based on the URLs.
According to the information in comment, you need {% url 'chat:inbox' %} not {% url 'inbox' %} in the form.

Jinja2 html buttons: Catch POST on different pages

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>

Categories