returning empty array from post request - python

I am trying to pass some dynamic values from my form, but so far I am only getting empty values.
I have an autocomplete plugin, where I search for "names" from a JSON object, each time I select one they will pass into a list with value="the ID".
I couldn't find a unordered list in WTForms, so I am using SelectMultipleField instead, since it is able to pass many values as an array/list
my form class is looking like this:
class ClassForm(Form):
function_name = StringField('names')
studentid = SelectMultipleField('studentid')
submit = SubmitField('submit')
then in my template I am using it like this
<form id="function_search_form" method="post" action="">
{{ form.csrf_token }}
{{form.function_name.label()}}
{{form.function_name()}}
<!-- then I am not using studentid directly,
but just normal html, so each time you pass
in a name from the json object it will come in like this.
-->
<ol class='student-list'>
<li value="1" name="studentid" id="studentid">test</li>
</ol>
{{ form.submit()}}
</form>
My problem is that it wont get the value from the list, even if I hard code the values directly, instead from the jquery script.
Here is how my view looks like
#app.route('/index', methods=['GET', 'POST'])
def index():
form = ClassForm()
if request.method == 'POST' and form.validate_on_submit():
flash('valid form')
st = form.studentid.data
print(st)#debug
return render_template('index.html', form=form)
Everytime I submit I am printing form.studentid.data I am getting [], which is an empty list.
Even when I try to pass single values and make studentid to a StringField I am still getting an empty value.
I have also tried the request.form['studentid'] but then I am getting Bad Request What am I doing wrong and is there another way to pass "custom" values ?

The reason its not working is because <li> is not a form control, so its data is not sent back with the form request.
You are never rendering the studentid field from your form class, so the form control is never rendered. It would be like expecting the following to work:
<form>
<p name="foo" data="hello">This should be sent</p>
<input type="submit">
</form>
To get the data back to your view method, you need to use a form control - you can test it out like this:
<form id="function_search_form" method="post" action="">
{{ form.csrf_token }}
{{form.function_name.label()}}
{{form.function_name()}}
<select name="studentid" class='student-list'>
<option value="1">test</option>
</select>
{{ form.submit()}}
</form>
Or, simply render the field correctly:
<form id="function_search_form" method="post" action="">
{{ form.csrf_token }}
{{form.function_name.label()}}
{{form.function_name()}}
{{form.studentid.label()}}
{{form.studentid}}
{{ form.submit()}}
</form>

Related

Django input not showing

Im building a site using Django. I am trying to pass an input from index.html and display it in about.html using view.py.
My input seems to get passed as it is in the url at the top the browser.I am trying to store this value in a variable and display the variable in a html paragraph. However it does not show. Instead of seeing the input associated with the variable i just see the string of text with the variable name.
My index.html:
<form action="{% url 'theaboutpage' %}">
<input type="text" name="user_input">
<input type="submit" value="click here now">
</form>
My about.html:
<a href={% url 'thehomepage' %}>go back to the home page</a>
<p>{{'input_from_home_page'}}</p>
My views.py:
def about(request):
Hellothere = request.GET['user_input']
return render(request, 'about.html', {'input_from_home_page':Hellothere})
Just remove the quotation marks around the variable in your template at about.html. It should look like this:
<p>{{ input_from_home_page }}</p>
As side notes:
If the information entered in <input type="text" name="user_input"> is sensitive information, then you should consider passing it using "POST" HTTP method instead of "GET". In which case, remember to include the {% csrf_token %}. To get the passed info, you can use: request.POST.get('user_input') in your view.
By convention, you should name variables with lowercase. In your case, it's nice to have hello_there instead of Hellothere.

Why is my flask form validation returning Not a valid choice?

I have been trying to figure out why my Flask form will not properly validate my select field choices even though the choices are coming from the select field options.
My assumption is that the select option when passed back from the server is unicode and is being compared to the choice which is a string, however, I thought coerce=str would fix that. I printed out the form data and request data which is the output below. Why isn't it working?
My code is attached below, removed csrf token key from the output dict. It seems like a very simple thing, but I can't figure it out.
forms.py
class PlatformForm(FlaskForm):
platform_options = [('test', 'Test'), ('test2','Test2')]
platforms = wtforms.SelectField('Platforms', choices=platform_options, coerce=str, validators=[DataRequired()])
views.py
#app.route('/', methods=['POST', 'GET'])
def index():
form = forms.PlatformForm()
if form.is_submitted():
print form.data
print request.form
if form.errors:
print form.errors
return render_template('home.html', form=form)
index.html
{% extends "base.html" %}
{% block content %}
<h4>Select a Platform</h4>
<form method="POST">
{{ form.csrf_token }}
<select class="custom-select" name="platform">
{% for value, text in form.platforms.choices %}<br>
<option value="{{ value }}">{{ text }}</option>
{% endfor %}
</select>
<button id="submit_inputs" type="submit" class="btn btn-default">Submit</button>
</form>
{% endblock %}
output
{'platforms': 'None'}
ImmutableMultiDict([('platform', u'test')])
{'platforms': [u'Not a valid choice']}
EDIT:
I figured out the problem. It's the way I'm creating the Select drop down through HTML and Jinja. Iterating through the choices and creating option tags doesn't seem to instantiate anything in the form data itself when passed back into Python. Changing that whole for loop to just
{{form.platforms}}
created a select drop down field that actually works.
You have a name mismatch. In the form, you named your select field platforms (plural). In the HTML, you use platform (singular).
I recommend that instead of manually rendering the fields in your template, you let WTForms generate the HTML for you. For the form label, you can use {{ form.platforms.label }}, and for the actual field {{ form.platforms() }}. You can pass any attributes you want to field to have as keyword arguments.
I think something might be going wrong because of the way you are rendering the form in your html file. If my hunch is right, try this:
{% extends "base.html" %}
{% block content %}
<h4>Select a Platform</h4>
<form method="POST">
{{ form.hidden_tag() }}
Select: {{ form.plaforms}}
{{ form.submit(class="btn btn-default") }}
</form>
{% endblock %}
and then try if form.validate_on_submit() in your views.py file
taken from this stack overflow answer by pjcunningham:
"validate_on_submit() is a shortcut for is_submitted() and validate().
From the source code, line 89, is_submitted() returns True if the form
submitted is an active request and the method is POST, PUT, PATCH, or
DELETE.
Generally speaking, it is used when a route can accept both GET and
POST methods and you want to validate only on a POST request."

Can't pass a variable with Jinja in Flask

The form's action sends the input to a route named soldfor. I am trying to pass the variable ID into soldfor. When I hit the submit button I get this error:
TypeError: soldfor() takes exactly 1 argument (0 given)
Rendering of register_results.html:
return render_template('register_results.html', results=results, ID=ID)
register_results.html:
<form role="form" method="POST" action="{{ url_for('soldfor', ID={{ ID }}) }}">
<div class="input-group">
<input type="number" class="form-control" placeholder="Sold For" autocomplete="off" name="soldfor">
<span class="input-group-btn">
<button class="btn btn-default" name="soldfor" value="soldfor" type="submit">Sell!</button>
</span>
</div>
</form>
soldfor Route:
#app.route("/soldfor", methods=["POST"])
def soldfor(ID):
soldfor = request.form['soldfor']
print(soldfor)
g.db = connect_db()
g.db.execute("UPDATE yardsale SET SF = ? WHERE ID = ?", (soldfor, ID,))
g.db.commit()
g.db.close()
return redirect(url_for('index'))
You need to change this:
<form role="form" method="POST" action="{{ url_for('soldfor', ID={{ ID }}) }}">
to this:
<form role="form" method="POST" action="{{ url_for('soldfor', ID=ID) }}">
Also from the documentation:
When you define a route in Flask, you can specify parts of it that
will be converted into Python variables and passed to the view
function.
#app.route('/user/<username>')
def profile(username):
pass
Whatever is in the part of the URL labeled will get passed
to the view as the username argument. You can also specify a converter
to filter the variable before it’s passed to the view.
#app.route('/user/id/<int:user_id>')
def profile(user_id):
pass
Your error is being caused by the route expecting a parameter, but you are not passing one in. You should probably use something like that last example to accomplish what you're trying to do.
It's also sometimes desirable to pass a default value into the view function in case you need to use that URL without passing an ID. An example would look like this:
#app.route('/user/id/<int:user_id>')
def profile(user_id=0):
pass

Send args from URL together with form in Flask

Here's my problem, in short:
I have an URL that looks like this: domain.tld/results?search_for=music
On that page, I have a form with a select field, which I then submit to Flask to do some SQL with.
Basically, the form looks like this:
<form action="/filter" method="POST">
<select name="sel_store">
{% for store in stores %}
<option value="{{ store }}">{{ store }}</option>
{% endfor %}
</select>
<button type="submit">SEND</button>
</form>
And in the back, it looks like this:
#app.route("/filter", methods=["POST", "GET"])
def filter():
search_for = request.args.get("search_for")
store = request.form["sel_store"]
return redirect(url_for("results", search_for=search_for, store=store))
(the results function does some SQL work with search_for and store, btw)
However, it doesn't seem to actually get any args from the URL, because it keeps giving me a NoneType object error.
What am I doing wrong here?
Thank you!
Not sure if this is the proper way to do it, but I managed to get it working by doing this:
<form action="{{ url_for('filtrare', search_for=request.args.get('search_for')) }}" method="POST">

Django: Template {% url %} tag, how to pass parameter from form

There is template:
<form action="{% url 'nfoapp.views.kinoscrap' <I WANT MOVIE_ID THERE> selectshort.id %}" method="post">
<input type="text" class="form-control" name="MOVIE_ID">
<button type="submit" class="btn btn-danger">Kinopoisk Search by ID</button>
</form>
There is my urls.py:
(r'^kinoscrap/(?P<kinoid>\d+)/(?P<shortid>\d+)/$', kinoscrap),
I want pass to kinoscrap two parameters - text field from form (MOVIE_ID) and 'selectshort.id' variable. The problem is that i can't put simple MOVIE_ID in first line of template, I got error. But when I try put instead MOVIE_ID other variable, for example selectshort.id, program work without error.
How I can trasmit text field value to view?
p.s I use bootstrap, if it has some importance.
You could have the form action empty, so to the same view, and then in the view redirect using the POST data from the form.
<form action="" method="post">
<input type="text" class="form-control" name="MOVIE_ID">
<button type="submit" class="btn btn-danger">Kinopoisk Search by ID</button>
</form>
And then in the view
def searchView(request):
if request.method == 'POST':
# get variables from form and redirect
else:
# do your normal rendering
(r'^kinoscrap/(?P<kinoid>\d+)/(?P<shortid>\d+)/$', kinoscrap),
Your urls.py accepts to integer values in the url (something like kinnoscrap/12/21), if you pass anything beside integers it'll throw an error. If you want to pass a text field you'll have to change the regular expression.
Try out your regexes at regex101 here to see if they'll work.

Categories