Flask Form Action is not referring to the variable - python

I have a form and am passing the variable as below using Jinja template
<form action = "/user_data/{{period}}" method="POST">
It is not redirecting required page /user_data/Oct-2022
But while just using {{period}} for testing in html page, variable is returning the result as Oct-2022. Not sure why the same variable is not getting passed in the form action.
Variable is printing as below in html page,
{{period}}
But it is not printing in the form,
<form action = "/user_data/{{period}}" method="POST">
This is the route method,
#app.route("/user_listing/<period>", methods = ['POST', 'GET'])
def user_data(period):
....
....
return render_template('user_data.html', period=period)

First we imported the Flask class. An instance of this class will be our WSGI application.
Next we create an instance of this class. The first argument is the name of the application’s module or package. If you are using a single module (as in this example), you should use name because depending on if it’s started as application or imported as module the name will be different ('main' versus the actual import name). This is needed so that Flask knows where to look for templates, static files, and so on. For more information have a look at the Flask documentation.
We then use the route() decorator to tell Flask what URL should trigger our function.
The function is given a name which is also used to generate URLs for that particular function, and returns the message we want to display in the user’s browser.

Related

Integrating a custom function in Django

I would like to access and print metadata (attributes and values) served by an ERDDAP server, which is a variety of OPeNDAP, on my Django website.
so I prepared a simple example function called get_conventions to access a metadata field on this public server hosting data and metadata. To get started, I install the required packages:
$ pip install pandas erddapy
and then,
import pandas as pd
from erddapy import ERDDAP
def get_conventions(dataset_id):
e = ERDDAP(server='https://gliders.ioos.us/erddap/', protocol='tabledap', response='csv')
url = e.get_info_url(dataset_id, response='csv')
df = pd.read_csv(url)
# this replace spaces with underscores in column names
df.columns = [col_name.replace(' ', '_') for col_name in df.columns]
conventions = df[df.Attribute_Name == 'Conventions'].Value
return conventions
Using a Python interpreter, one could call the function like this with this sample dataset id as an argument (amelia-20180501T0000), which is found on the server; the output follows:
>>> get_conventions('amelia-20180501T0000')
6 Unidata Dataset Discovery v1.0, COARDS, CF-1.6
Name: Value, dtype: object
>>>
I would like my website to print on a webpage the output of the above function.
I can print the argument string in a page (model.py, views.py and a related html templates - those being similar to the Django tutorial), but I am unsure how to refer the argument to the function (and expect a return) or how/where to integrate the function in the Django structure.
The argument is stored in a local database which can be referred using models/views/template
But I am unsure how to integrate the part involving the function in Django?
The approach is going to depend on whether the function is going to be used cross-app, in a single app, or is tied to a Django model.
For cross-app functions, I typically create a separate app called core or utils with python files for each set of functionality. For the sake of your example let's call it erddapy.py. In the views.py that your view is in, simply from utils.erddapy import get_conventions and call it in your view passing in the argument. Then return the result as context to the template.
If it's only being used in a single app but not tied to a model you can also create a utils.py file in that app and instead do from .utils import get_conventions in your views.py then call it in your view.
If you've defined a model for the datasets and store the dataset id there it's probably better suited for a model method. That way you can use the queryset API in your view to get the instance then simply call instance.get_conventions(). You wouldn't need an argument for this approach since you could simply get the dataset id from self.dataset_id in the method logic.
# view
id = self.kwargs.get('id')
if id:
instance = get_object_or_404(MyModel, id=id)
data = instance.get_conventions()
# return the data as context in an http response
# model method
class MyModel(models.Model):
# model fields
def get_conventions(self):
dataset_id = self.dataset_id
# get the data
I guess the underlying question is how are you storing or obtaining the dataset id? If it's in a separate local database you may need to query it to get the dataset id which I would do in a separate function. Then either pass it into the function as an argument or store it in your Django db as a model instance and use the method approach explained above.
I was able to make this work after reading this, by D. Patel.
It involved adding the following to the file named services.py which was placed an app subfolder titled templatetags. This is where the get_conventions custom function reside:
from django import template
...
register = template.Library()
#register.filter
def get_conventions(dataset_id):
...
return conventions
and in the template, I had to load the services file and add the following to the html template:
{% load services %}
{% block content %}
<p><strong>Conventions:</strong> {{ station.dataset_id | get_conventions }}</p>
...
{% endblock %}
station.dataset_id being the argument for the get_conventions function paired | with the return value
and it worked as intended. It was quite straightforward.

How can I pass a client-side parameter to a server-side route without using forms?

I have a simple Flask web app. My index template has various ways of interacting with clients using javascript and HTML. I am also have a form that, upon submission, routes to another flask process and uses the request.form command to retrieve user-submitted data.
However, I want to do something a little different. I would like to initiate a Flask redirection upon javascript event but include a parameter, and not use form.
For example, my index.html file would display something like this after template rendering:
function startRedirect(parameter) {
window.location.pathname = '/myRedirect';
}
<input type="checkbox" id="sample" name="sample" onChange="startRedirect(parameter);">
And part of my Flask script would have:
#app.route('/myRedirect')
def myRedirectFunction():
# do something with the parameter here
return render_template('index.html')
I realize this can be done with using a form, but I am interested in accomplishing this task without having a form. I was thinking about somehow using request.args, but don't quite understand what to do.
You can use a dynamic route to capture a simple input and pass it to the route's function.
app.route('/myRedirect/<param>')
def myRedirectFunction(param='hello world'):
return render_template('index.html', param=param)
Using this route as a redirect, you can pass a single param (or multiple if you serialize them) that you can use to do something. From there, you can either display or you can redirect again to a common endpoint so the user does not see the param in the url.
There's no need for a form or an explicit redirect, just attach a route and some parameter to the dynamic route.
Let's say you have a model to list the departments in your company:
class Departments(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True)
Now you have a department selection page:
#app.route('/departments_home', methods=['GET'])
def departments_home():
departments = Departments.query.all()
return render_template('departments_home.html',
departments=departments)
On the frontend you might have a variety of selections, each giving a link to the same route but with a different department_id:
{% for department in departments %}
Click to go to {{ department.name }}
{% endfor %}
Now you just need another route to handle this, taking the variable department_id that was passed in the GET request:
#app.route('/load_department/<department_id>', methods=['GET'])
def load_department(department_id):
department = Departments.query.get(int(department_id))
department_data = # do stuff here with the specific department

How to read in user input on a webpage in Flask [duplicate]

This question already has answers here:
Sending data from HTML form to a Python script in Flask
(2 answers)
How to use variables in SQL statement in Python?
(5 answers)
Closed 3 years ago.
I'm trying to create a basic web app that has an HTML form on the root landing page, and then after submission, run a postgresql query with the desired input and redirect the user to a page with a generated matplotlib chart of their input. In my main function, I have the following:
#app.route('/', methods=['POST'])
def main():
return render_template("main.html")
So let's say I have my main html file being rendered by the flask app. I have another route below:
#app.route('/query', methods=['POST'])
def queryPage():
# code to execute query with information
# passed from the main.html template
# then generate chart via matplotlib via returned information
return render_template("query.html")
I'm confused as to how to get my input from the form in main.html to send information back to the application for rendering at the /query endpoint. If someone could elaborate on this, I'd appreciate it. Front end is not my strong suit. Thanks!
You need a form on main.html... maybe like this (note the form action):
<form action = /query method="POST">
<label for="username">USERNAME:</label>
<input type="text" name="username" id="username" size="15">
<input type="submit" name="submit" id="submit"/>
</form>
When that form gets sent (after a user clicks a button lets say), the route in your flask code that matches the action (/query in this case) will get called and execute. Also the name= variables in any of your form elements will be available in your request on the back end (I'm using the variable username as an example). You can get them like this: request.form['username']. Other form variables (like a check box) will be slightly different.
Anyway in your case you need a /query action in your html somewhere in main.html.... It could be called by a button or timed javascript etc...
When this /query action is called on your main.html, you need to
return render_template('query.html, username=username)
and then the username variable will be available on the query.html page.
Keep in mind I only passed a single variable. You can pass a multiple variables, lists, dictionaries etc...
Also keep in mind any variable that you return to query.html can be made extremely dynamic using Jinja templating. You can loop through lists and print different html tags etc and use logic within your html... possible depending on what the values are that get returned to the page.
If I understand your question correctly then you are having difficulty passing the form information from your main function to the separate queryPage function for rendering. This can easily be achieved by providing the values you wish to pass as keyword arguments to the url_for function. These can then be retrieved from request.args within the queryPage function. Given the fact that you are returning query.html from this function and not an image, I assume that you are intending on displaying your chart within an img tag in query.html. In this case you will need another view function to generate and return the image itself. You may also need to disable browser caching for this endpoint to prevent browsers treating your dynamic image as if it were a static image https://stackoverflow.com/a/2068407/10548137.
#app.route('/', methods=['GET', 'POST'])
def main():
form = MyForm(request.form)
if request.method == "POST" and form.validate():
return redirect(url_for("queryPage", **form.data))
return render_template("main.html", form=form)
#app.route('/query', methods=['GET'])
def queryPage():
arguments = request.args.to_dict()
image_url = url_for("make_chart", **arguments)
return render_template("query.html", image_url=image_url)
#app.route('/make_chart', methods=['GET'])
def make_chart():
arguments = request.args.to_dict()
# perform postgres query here using arguments
# generate matplotlib chart here using query results
# ? save chart in BytesIO buffer in png format
response = send_file(file_pointer, mimetype="image/png")
# just return response here if don't need to alter headers
response = make_response(response)
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
return response

Flask URL Routing

If I'm making a blog site and I want to set up routing such that
#app.route('/<username>/<postname>', methods=['GET'])
routes to the post with name 'postname' of the user with name 'username', how do I get the html to recognize this? I've been trying to do something like
<a href={{ url_for('/', username=user.name, postname=post.name) }}>{{post.name}}</a>
I'm also trying to reconcile this with Flask understanding special keywords /login or /about so that it checks if the user is trying to access those first. How can I implement those checks?
The first argument to url_for in your template should be the name of the view function you decorated:
#app.route('/<username>/<postname>', methods=['GET'])
def view_user_post(username, postname):
^^^^^^^^^^^^^^
Now, you can write this in your template:
{{ url_for('view_user_post', username=user.name, postname=post.name) }}
This lets you change the URL in the route without having to update it elsewhere in your codebase.

confusion in url and template in django

My url is :
1. http://localhost:8000/docs/[slug]
2. http://localhost:8000/docs/[slug]/login
1. url calls before number 2. url I want to send
the slug value to the function mapped by the url 2. In template
what should i wrote for form action event.
I agree, this is nearly incomprehensible, but I'm going to give it a go in terms of an answer.
In terms of calling sequence, there is none. A user might first visit url 2 or url 1. You have no way of guaranteeing which they will try to access first because they might directly input the url into their browser. The only thing you can do is set a variable in request.session's dict and test for it with your login url.
In terms of passing slug to another url, if you're having a url with this in it:
urls = ('',
url(r'docs/(?P<slug>\w+)', 'app.views.slug', name='slug-view'),
url(r'docs/(?P<slug>\w+)/login', 'app.views.slug_login', name='slug-login'),
#..
)
Then in your template you can do this:
<form action="{% url slug-login slugname %}" method="POST">
Your views.py would look something like this.
def slug(request, slug):
#
#
return render_to_response('templatename.html', {'slugname':slug}, context_instance=RequestContext(request))
def slug_login(request, slug):
# do something with slug.
This way, when you access the slug view, you pass into the template a variable called slugname, which the template uses with django's url library to resolve a specifically named url in urls.py with one named parameter, slug, which it will assign the value of slugname.
I suggest you try it.
I might also reccoment you read up on the django url dispatcher. Your use of regex without named parameters is acceptable but really not best practice. I'd also suggest django shortcuts (render_to_response) as a quick way to pass variables into templates and the django template language itself.

Categories

Resources