I'm having a tough time figuring out how to parse my URL GET data and send it to the right view.
I have a search view that only has a search input:
template/search.html
<form action="http://domain.com/schools/search/" method="GET" >
<input type="text" name = "q_word">
<input type="submit" value="Search"/>
</form>
When a user enters in a search term, I want to send that data to another view to parse and use in a geocoding function I wrote. Here is a look at my urls.py:
url(r'^schools/search/$', school_views.find_school, name="find_school"),
url(r'^schools/search/(?P<address>[\w ]+)$', school_views.geo_locate, name="geo_locate"),
I want to grab the GET data from a URL (after they've entered in the info), and pass it as an address argument to my school_views.geo_locate function.
This set up works great when you manually type out a URL like: schools/search/150%20main%20Street
But when a user submits any form data, the URL passed is /schools/search/?q_word=150+west+main and I'm just kicked back to my search template.
I think my regex needs to be tweaked in my second url argument, but I just keep returning to the search page after submission, with no data going to my geo_locate view. Is this a URLs problem?
GET data is not passed in the URL parameters. Don't try to capture it in the regex. Just get it from request.GET inside the view.
Related
I'm trying to update a database value called ''favorites'' for the logged in user of a Flask web app using a button click. Essentially, the favorites column is a single string that looks like this: Apples, Bananas, Oranges where on the button click, I would want to append a value (say Cherries) by breaking apart the string into a list in my #app.routes(), appending the value, and rejoining it back into a string before committing the changes. I'm not sure what the proper way is to do this, here's what I have:
HTML snippet
<button action="{{ url_for('add') }}" method="post" type="submit">Favorite</button>
#app.routes()
#app.route('/add', methods=['POST'])
def add():
star_faves = current_user.favorites
star_faves_list = star_faves.split(', ')
star_faves_list.append('Cherries')
', '.join(star_faves_list)
current_user.favorites = star_faves_list
db.session.commit()
return render_template('add.html')
The problem is that I don't really understand how the HTML is communicating with Python/Jinja, if anybody can help clear that up I would greatly appreciate it.
It looks like you have some elements confused.
If you want to submit a POST request to the /add page, the easiest way is to create a form. (Buttons do not have an action or method attribute, forms do.) When you create the form, you also specify the HTTP method to use when submitting the form. So in your case, it should look something like this:
<form action="{{ url_for('add') }}" method="POST">
<input type="submit" value="Favorite">
</form>
You can use a button instead of an input with type submit, they are interchangeable.
If you don't want the page to reload while submitting the request, a more advanced technique you can use with JavaScript is something called AJAX.
This example code sends the same POST request to the /add page:
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
// this method gets called if the state of the request changes (it succeeds of fails)
// you will probably want to update your page accordingly here
};
request.open('POST', '/add');
request.send();
I am creating an app that does some analysis, given a user enters in some IDs into the form. For example, if a user types 12345, 23456 into the TextField form, the app will run some analysis on these IDs and then display the results. My problem is that currently, when the user clicks "Submit" and the data analysis completes, it always redirects the user to www.website.com/results. I need to create unique url's like www.website.com/results/12345+23456 so that 1) I can have multiple users and 2) users can send this link to people to re-generate the analysis.
Now, there are some questions on StackOverflow that are similar to my question but they are not the same and did not help me. So first, let me show some code before discussing that.
I have a home page which contains the the form:
<div>
<form action="https://website.com/results/" class="form-inline" method="post">
<div class="form-group">
<label for="PubmedID">Pubmed ID(s)</label>
<input type="text" class="form-control" id="PubmedID" name="pmid" value="{{request.form.pmid}}">
</div>
<button type="submit" id= "myButton" class="btn btn-default" data-toggle="modal" data-target="#myModal">Submit</button>
</form>
</div>
As you can see, the value for the form is request.form.pmid. My Flask-Wtform for this is here:
class pmidForm(Form):
pmid = TextField('PubmedID')
Since the action of this form points towards website.com/results that triggers my Flask function to be called:
#app.route('/results/', methods=["POST"])
def results():
form = pmidForm()
try:
if request.method == 'POST':
#entry = request.form or request.data doesn't help me...
entry = form.pmid.data #This is the user input from the form!
# DO LOTS OF STUFF WITH THE ENTRY
return render_template('results.html')
except Exception as e:
return(str(e))
As you can see I am using POST and form.pmid.data to get the data from the textfield form.
Again, I don't want to just redirect to /results, I'd like to expand on that. I tried to modify my form so that the form action pointed to https://website.com/results/{{request.form.pmid}}/ and then update the results function to be
#app.route('/results/<form_stuff>', methods=["POST"])
def results(form_stuff):
But this never worked and would re-direct me to a 404 not found page. Which I believe makes sense because there is no form data in the action when the HTML is first rendered anyway.
Now, the other post that mine is similar to is: Keeping forms data in url with flask, but it quite doesn't answer or solve my problem. For tthis post, the key point that people made was to use POST (which I already do), and to obtain and return the data with return request.args['query']. For me, I'm already processing the form data as I need to, and I have my return render_template() exactly how I want it. I just need to add something to the results URL so that it can be unique for whatever the user put into the form.
What do I need to add to my form in the html and to my Flask /results function in order to have the form data added into the URL? Please let me know if there's any other information I can provide to make my problem more clear. I appreciate the help! Thanks
This isn't really a question about Flask.
If you want the data to show in the URL when you submit the form, you should use method="get" rather than "post". Then the URL will be in the form https://website.com/results/?pmid=12345.
I have a Flask site that has a 'search bar' where you type in the location ID of a particular location and then click Submit to be taken to the page for that location, if it exists. Here's the current form action:
<form id="locationinfo" action="{{ url_for('location') }}">
When you click Submit you are taken to /location?info=SITEID and that works just fine. What I want to do is change this behavior slightly so that when a user clicks Submit they are taken to /location/SITEID/ instead. I have the decorator set up in my main Flask routes file, but I'm struggling to put the pieces together to get this simple form together.
#app.route("/location/<locationid>/")
def locations(locationid):
...
return locationid
Any direction would be greatly appreciated!
[Edit with current full form code]
#app.route("/location")
def location():
location_id = request.args.get("info")
<form id="info" action="{{ url_for('location') }}">
<input type="text" name="info" id="locationfield">
<button type="submit">Go!</button>
</form>
You can't change how HTML forms submit their fields, they will always be in the query string or body (POST). One option is to use JavaScript to override the submit event to perform your own submit and re-render with the results.
A simpler solution is to redirect to the nice url after submit. This keeps the "search" action separate from the "show" action (even if they are handled by the same view).
#app.route('/location/')
#app.route('/location/<int:id>/')
def location(id=None):
# redirect to the second form if the id wasn't in the path
# raises 400 error if id wasn't in the query
if id is None:
return redirect(url_for('location', id=request.args['info']))
# id is in the path, continue
...
You can expand this later if you want to search by something besides id. Perform the search then redirect to the found id.
Let's say I have the following pointless example view:
def foo(request, input):
return HttpResponse()
and in a template I have a form:
<form method="get" action="{% url 'foo' ??? %}">
<input id="myinput" type="text" name="myinput">
...
</form>
Finally, I have the following url in my URLconf:
urlpatterns = [
url(r'^foo/(.+)/', views.foo, name='foo'),
]
What I would like to do, is pass the value entered by the user into the input with the id of #myinput to the foo() view function. To put it another way, you should be able to enter bar in the html input, and when you submit the form it will take you to foo/bar/.
I know that within the foo view I could access the value of the input easily with request.GET['myinput'], but I want it to show up in the url as well.
This seems like it should be a fairly common task, but I have not been able to come up with a solution yet. Any suggestions would be appreciated. My Frankenstein's Monster of a first Django site is almost complete, and this is one of last pieces I am missing.
The source of my misunderstanding
Although I did not make this clear in an attempt to simplify my example and avoid using app-specific code, my use case is a simple search view. The view was actually one of the first views I wrote in the start of my Django journey, and I mistakenly was POSTing my data instead of GETing it. This was making it so that if I was searching for the item foo, it would take me to the detail page for foo, but the url would be mysite/search/ (i.e., the search query is not included in the url though it is included in the request), and I can't return to those search results by visiting the url mysite/search/.
While I was using a GET request in my toy example in this question, I didn't realize that I had been using a POST in my app, and that with some minor tweaking I can get the functionality I want for free very easily. I know that all of this is extremely obvious to veteran and even intermediate web developers, but for someone starting from scratch without web or cs experience, things like HTTP can be a little confusing. At least for me it is. Thanks so much to #Two-Bit Alchemist for explaining this in a way that I can understand.
Applying all this to my toy example
I would get rid of the passed parameter in my view:
def foo(request):
# If I want to do something with the search query, I can access it with
# request.GET['search_query']
return HttpResponse()
change my form in my template to:
<form method="get" action="{% url 'foo' %}">
<input id="myinput" type="text" name="search_query">
...
</form>
and change my url to:
urlpatterns = [
url(r'^foo/search/', views.foo, name='foo'),
]
As #Two-Bit Alchemist said: "The rest will happen like magic". If a user enters bar in the input and submits the form, they will be taken to foo/search/?search_query=bar. This is what I was looking for.
My form submits as follows
<form class="form-signin" role="form" action="{% provider_login_url "facebook" method="js_sdk" next="/next"%}">
I overrode the DefaultAccountAdapter with my own AccountAdapter with method
def get_login_redirect_url(self, request):
print request.GET['next']
...
But request loses the next parameter and the print returns an error because there is no "next" in request.GET.
Why can't I access the next parameter?
I was originally using get_login_redirect_url to handle different url redirects after creation of social versus username/password users. Now, I need to be able to specific the next parameter in the URL for another variant of behavior for social user login but am unable to access the next parameter because it does not seem to be passed.
I am not sure whether I could give the precise solution for your issue. But I think got the point.
To access the next parameter from url,
The url should be,
http://127.0.0.1:8000/index?next=2
If you have to form the url in this above manner,you can get access to the next argument from request object in your corresponding view method
print request.GET.get('next')
So, please make sure to format request url with proper querystring refer
To your case,
I have no idea about {% provider_login_url %} template tag
I am assuming after your tag rendered it yields the url index, then i am appending my querystring next
<form class="form-signin" role="form" action="/index?next=someValue">
you may try additionally,
{% provider_login_url "facebook" next=next %}
source
I came across with a similar problem. However, I wasn't signing in with facebook. request.GET was always empty.
I think you could try using jQuery to manually append next parameter to the action attribute of <form>. See this question. It solves my problem.