Flask form appends form data to url on submit - python

I have a HTML form
<form>
<input type="text" name="keyword" id="keyword" />
<input type="submit">Submit</input>
on submits I want to pass form data to flask app
requests.py
def search_news(keyword):
search_news_url = 'https://newsapi.org/v2/everything?q={}&language=en&apiKey={}'.format(keyword,api_key)
with urllib.request.urlopen(search_news_url) as url:
search_news_data = url.read()
search_news_response = json.loads(search_news_data)
search_news_results = None
if search_news_response['articles']:
search_news_list = search_news_response['articles']
search_news_results = process_search_results(search_news_list)
return search_news_results
def process_search_results(search_news_list):
news_results = []
for search_results_item in search_news_list:
author = search_results_item.get('author')
title = search_results_item.get('title')
description = search_results_item.get('description')
url = search_results_item.get('url')
urlToImage = search_results_item.get('urlToImage')
publishedAt = search_results_item.get('publishedAt')
content = search_results_item.get('content')
totalResults = search_results_item.get('totalResults')
if content:
news_results_object = Everything(author,title,description,url,urlToImage,publishedAt,content,totalResults)
news_results.append(news_results_object)
return news_results
views.py
from ..requests import get_everything,search_news
....
#main.route('/')
def index():
everything = get_everything()
title = 'News Highlight'
searching_news = request.args.get('keyword')
if searching_news:
redirect(url_for('.search',keyword = searching_news))
return render_template('index.html',title = title,everything = everything)
....
#main.route('/search/<keyword>')
def search(keyword):
keyword_list = keyword.split(" ")
keyword_format = '%20'.join(keyword_list)
searched_news = search_news(keyword_format)
title = f'Search results for {keyword} '
return render_template('search.html',searched_news = searched_news)
on form submits it appends form data to url like this:
http://127.0.0.1:5000/?keyword=game+of+thrones
I've tried using post methods but i get a server does not support method error. Can anyone help please.
but when I append link like this:
http://127.0.0.1:5000/search/game%20%of%thrones
the app searches and displays results

By default, form data is submitted via the URL's query string if you don't tell it to behave differently (the method defaults to GET).
Post
If you want to send the data to flask using a POST request, this is the way to go:
Make sure to tell your HTML form to use method="POST" and tell it where to send it's data via the action attribute:
<form method="post" action="/search">
<input type="text" name="keyword" id="keyword" />
<input type="submit">Submit</input>
</form>
Tell your server-side view to accept POST requests and fetch the sent data from request.form. In views.py use
#main.route('/search/' methods=['POST'])
def search():
keyword = request.form['keyword']
...
Get
If you want to use a GET request and pass the data via query string set your form's method to get
<form method="get" action="/search">
<input type="text" name="keyword" id="keyword" />
<input type="submit">Submit</input>
</form>
On submit, your browser will append the values entered in the input field to the URL as a query string: ?keyword=<whatever-you-entered>.
Tell your server-side view to accept GET requests and fetch query string data via request.args.get(). In views.py use
#main.route('/search/' methods=['GET'])
def search():
keyword = request.args.get('keyword')
...
MDN has got a nice article with more details around sending and retreiving form data with HTML and Python, this might be worth a read, too.

Related

Pass HTML for value to django view (for querying database models)

I have a form input inside my HTML page and want the figure ID that is entered inside the form to be passed into my django view to do a query, displaying info for the figure with the matching ID.
My form:
<form metohd="GET" id="figure_choice_form">
<label for="figure_id">Enter ID</label>
<input type="text" id="figure_id" name="figure_id">
<button> Submit </button>
</form>
My views.py
def from_DB(request):
#request being the ID entered from the form
figures_list = Figure.objects.filter(id=request)
context = {"figures_list":figures_list}
return render(request,"app1/data_from_DB.html",context)
Firstly , Update your html code snippet to correct form attribute "metohd" to "method" .
You are sending data via GET request . So you can access it via request.GET method .
Code snippet of django view.
def from_DB(request):
id = request.GET.get("figure_id")
figures_list = Figure.objects.filter(id=id)
context = {"figures_list":figures_list}
return render(request,"app1/data_from_DB.html",context)
This does your required work .

Understanding request.data in django view

I tried looking at different resources on the internet regarding this request and request.data in django, but I couldn't fully understand it.
Why this request parameter is kept inside the function? What are we passing in this request parameter?? Also, what does this request. data do??
def index(request):
content = {
'Blogdata': Blog.objects.all(),
}
return render(request, 'index.html', content)
def somefunction (request):
data=request.data
As you can see I have two functions above both of them have request paramter inside the function. Also, I need the explanation on this request.data as this has to be used multiple times.
First, you should understand about HTTP Request(Header, Body). When you type in form and send to server, browser get data with name and add values into body request. In the backend server, we will get data from body with name.
Example:
I have form fill your name:
<form action="/signin" method="get" name="myForm">
<label for="name">Your name:</label>
<input type="text" id="name" name="name"><br><br>
<input type="button" value="Send form data!">
</form>
You type name : "Khoa", browser get values "Khoa" from input and add key:values with name in . Like this: "name": "Khoa"
In server django, you can get data with using request.data.get("name") = "Khoa"
request.data is body HTTP send to servere, "name" is key part of body have values is "Khoa"

Flask putting form into URL

I've been working on a form that sends data to a scraper and simultaneously generates a URL from form input. The returned templates works flawlessly, but the URL change ends up giving me the entire form in the URL and I can't figure out why.
The URL ends up looking like this:
http://localhost/options/%3Cinput%20id%3D%22symbol%22%20name%3D%22symbol%22%20type%3D%22text%22%20value%3D%22%22%3E
I'd like it to look like this:
http://localhost/options/ABC
Form class:
class OptionsForm(Form):
symbol = StringField('Enter a ticker symbol:', validators=[Required(), Length(min=1, max=5)])
submit = SubmitField('Get Options Quotes')
Views:
# Where the form data ends up
#app.route('/options/<symbol>', methods=['GET', 'POST'])
def options(symbol):
# Created this try/except so I could test functionality - for example, I can do 'localhost/options/ABC' and it works
try:
symbol = request.form['symbol']
except:
pass
return render_template('options.html', symbol=symbol, company_data=OS.pull_data(symbol, name=True))
# Where the form lives
#app.route('/', methods=['GET', 'POST'])
def index():
form = OptionsForm()
print(form.errors)
if form.validate_on_submit():
return redirect(url_for('options', symbol=form.symbol.data))
return render_template('index.html', options_form=form)
Template:
<div id="options_div">
<form method="POST" name="symbol_form" action="{{ url_for('options', symbol=options_form.symbol) }}">
{{ options_form.hidden_tag() }}
{{ options_form.symbol(size=10) }}
{{ options_form.submit(size=10) }}
</form>
Any help would be appreciated.
Try adding enctype='multipart/form-data' to the form tag. It looks like your form is using application/x-www-form-urlencoded, the default.
Edit OK so check this out. When your template is being rendered there is no value in that data attribute (In the url_for call). When not referencing the data attribute (as your original question shows), you're referencing the actual form element (which is why you see all of that html being passed in the url). Here are your options (that I see):
Use some kind of frontend javascript to bind the form's action attribute to the value in the input box. Something like angular would help for this (but is overkill if you don't use any of its other features).
Just have the form POST to /options (no symbol in url). Then, grab the symbol attribute from the form data.

What does this error mean: ValueError: renderer was passed non-dictionary as value

Right now I have a pyramid (python) app with a home page that is reached via this route in the init.py file:
config.add_route('home_page', '/')
in my views.py file I have:
#view_config(route_name='home_page', renderer='templates/edit.pt')
def home_page(request):
if 'form.submitted' in request.params:
name= request.params['name']
body = request.params['body']
page=Page(name,body)
DBSession.add(page)
return HTTPFound(Location=request.route_url('view_page',pagename=name))
and in my edit.pt template I have
<form action="${save_url}" method="post">
<textarea name="name" tal:content="page.data" rows="10"
cols="60"/><br/>
<textarea name="body" tal:content="page.name" rows="10"
cols="60"/><br/>
<input type="submit" name=form.submitted value="Save"/>
</form>
So basically the goal is to have the homepage show this edit.pt template which contains a form for submitting two pieces of information, a page name and page body. Upon submitting the form, the return HTTPFound function should redirect to the view_page created which shows the page name page body on a new permanent url.
I am not sure what I should add after the if statement in my home_page view_config. If the form hasn't been submitted I don't want anything to happen, it should just continue to show that edit.pt template. Right now I am getting an error when I try to visit the home page: ValueError: renderer was passed non-dictionary as value.
It looks like you are missing a condition
#view_config(route_name='home_page', renderer='templates/edit.pt')
def home_page(request):
if 'form.submitted' in request.params:
name= request.params['name']
body = request.params['body']
page=Page(name,body)
DBSession.add(page)
return HTTPFound(Location=request.route_url('view_page',pagename=name))
# form was not submitted here, need to return context
# ValueError: renderer was passed non-dictionary as value < beacuase you were
# not returning context for this case before
return {} # template contenxt here

POSTing forms in Django's admin interface

I'm writing a Django admin action to mass e-mail contacts. The action is defined as follows:
def email_selected(self,request,queryset):
rep_list = []
for each in queryset:
reps = CorporatePerson.objects.filter(company_id = Company.objects.get(name=each.name))
contact_reps = reps.filter(is_contact=True)
for rep in contact_reps:
rep_list.append(rep)
return email_form(request,queryset,rep_list)
email_form exists as a view and fills a template with this code:
def email_form(request,queryset,rep_list):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('email','noreply#localboast'),['redacted#email.com'],
)
return HttpResponseRedirect('thanks')
else:
form = EmailForm()
return render_to_response('corpware/admin/email-form.html',{'form':form,})
and the template exists as follows:
<body>
<form action="/process_mail/" method="post">
<table>
{{ form.as_table }}
</table>
<input type = "submit" value = "Submit">
</form>
</body>
/process_mail/ is hardlinked to another view in urls.py - which is a problem. I'd really like it so that I don't have to use <form action="/process_mail/" method="post"> but unfortunately I can't seem to POST the user inputs to the view handler without the admin interface for the model being reloaded in it's place (When I hit the submit button with , the administration interface appears, which I don't want.)
Is there a way that I could make the form POST to itself (<form action="" method="post">) so that I can handle inputs received in email_form? Trying to handle inputs with extraneous URLs and unneeded functions bothers me, as I'm hardcoding URLs to work with the code.
You can use django's inbuilt url tag to avoid hardcoding links. see...
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#url
Chances are you'd be better off setting up a mass mailer to be triggered off by a cron job rather than on the post.
Check out the answer I posted here
Django scheduled jobs
Also if you insist on triggering the email_send function on a view update perhaps look at
http://docs.djangoproject.com/en/dev/topics/signals/

Categories