django: search forms and redirect - python

After processing form from POST I should redirect, to prevent user from hitting back. However, I am using form to determine search query on a database, so I need to either pass params to the redirected site or the result of a search. Or maybe there is some other good practice, how to solve this problem? Maybe in this situation I am allowed not to redirect (nothing happens, if user performs search again).

Search queries should probably be GETs, rather than POSTs, because they are not changing anything - they are simply passing parameters to get certain information. POST should be reserved for forms that actually change things in the database, or result in a specific action (eg submitting an email).
To reply to your comment, hiding parameters from URLs is not particularly good practice, but if you really think you need to, this is an instance where it's OK not to redirect after the form submission - again, because you're not affecting anything with the POST.

Related

Flask and WTForms Post-Redirect-Get pattern causing CSRF error

I have an existing Flask application with an external database, and a view that provides users the ability to search one table based on user-defined criteria. Currently, the view renders the search form on a GET request and performs a search on a POST request.
I would like to extend the view to handle URL parameters for the search terms. The goal is for users to be able to bookmark or share a URL containing search results, and in the future, they can navigate directly to that URL without the need to fill out a search form again.
However, I'd like to perform some validation on the URL parameters before performing the search to make sure that they make sense. The logic for these URL parameters will be identical to WTForms validators that already exist on the form fields. To accomplish this behavior without rewriting all of the validation code, I've come up with this design:
User navigates to the page (GET) with no URL parameters. Empty form is displayed with no search info.
User fills out the desired search criteria and submits the form (POST).
Form is validated. If invalid, display the results back to the user (and don't continue this process).
Convert the provided form values to a URL parameter dict. Return a redirect to the same URL with the provided URL parameters. (I'm using HTTP 303 to enforce that after following the redirect, the new request should be a GET.)
Route fills the URL parameters into the Flask form and validates it. If invalid, display the results back to the user (and don't continue).
Perform the search.
Display the results.
With this approach, a user could also enter parameters directly into the URL (or bookmark a URL from a previous search), and the process would begin at step 5. The arguments would still be validated.
As I understand it, this is basically the Post-Redirect-Get pattern.
The problem I'm having is that on the redirected GET request, form validation is failing with the error, "The CSRF token is missing." This doesn't happen on the first validation during the POST, so I know the CSRF token is being submitted with the initial request. It's just not getting transferred during the redirect.
If I pass the CSRF token as a URL parameter during the redirect, I suspect it will work for that single instance, but it will not solve the issue of a user entering search parameters directly.
I've put together a simple example of the design and the resulting CSRF error. In that example, I expect to be able to enter one of the valid values in the input text box and/or select the checkbox, click the submit button, and see my requested results in the bottom section. I also expect the URL to contain query parameters for the field or fields I filled out. Instead, I'm getting the CSRF error described above.
Here are my specific questions:
How can I resolve this CSRF error on a redirect?
Is a CSRF token really valuable in this context?
Is there an easier way to accomplish the behavior I'm looking for?

Multiple forms in one page in DJANGO

I've been developing a django project for 1 month. So I'm new at Django. My current problem with Django is; When I have multiple forms in one page and the page is submitted for a form, the other forms field values are lost. Because they are not posted.
I've found a solution for this problem;
When there is get method, I send the other forms value with the page url and I can handle them from the get request.
When there is post method, I keep the others form fields value in
hidden inputs in HTML side in the form which is posted. Hence I
can handle it from the post request.
Maybe I can keep them in session object. But it may not be good to keep them for whole time which the user logg in. But I dont know. I may have to use this method.
Is there another way which is more effective to keep all forms fields in Django?
Any Suggestion?
Thank!
You can make use of AJAX for a single form submission instead of whole page submit.

Better way of passing form parameters into hidden form in Pyramid

In a previous question, I was trying to figure out the right strategy for to passing data between forms in Pyramid. Based on the answer I received, I decided the approach of using a hidden form.
I started implementing this and think there must be a better way of passing along the data. Specifically, passing parameters through the url results in a tuple that is messy to parse.
I want it to be general enough to not to know what parameters the form has and also it needs to handle file fields as well.
How I'm currently attempting to pass the form data to the confirmation page:
#view_config(renderer="templates/derived/load/error.mak", route_name='process_model_route')
def process_model(self):
#processing logic and validaton, failiure in validation sends user to error.mak
return HTTPFound(route_url('confirm_model_route', self.request, fparams=self.request.POST))
Route: config.add_route('confirm_model_route', 'rnd2/model/confirm/*fparams')
#view_config(renderer="templates/derived/confirm/model.mak", route_name='confirm_model_route')
def confirm_model(self):
form_dict = self.request.matchdict['fparams']
#need to decode and pass to template
return dict({'load_route':load_route, 'form_dict':form_dict})
The confirm/model.mak template would contain the hidden form.
The idea with this method is:
Client visits page.
Server renders the form.
Client fills in form and POSTs to URL.
Server renders a new page that contains a hidden form with all of the data it just received in the POST.
Client POSTs to a URL, confirming the submission.
Server persists the data from the hidden form and redirects.
Now depending on usability, it's up to you to decide how many different URLs you actually want here and how many views in Pyramid. You have to think about what happens with invalid data?
Notice in the outline above, once the user POSTs the form to a URL, that URL must return the confirmation page containing a hidden form. If you try to redirect the user to a confirmation page instead, you must persist the data somehow, either in a session or through the hack you showed in your example (shoving all of the data into the GET). The second solution is very bad because it abuses the true purpose of GET in HTTP.
There is also the convention that every POST should result in a redirect to avoid a client submitting the form multiple times. With this in mind you might consider the simple solution of rejecting POSTs that do not have a "confirmed" flag and simply setting the "confirmed" flag in javascript after prompting the user. This allows you to keep your form handling logic simple.
If you don't want to rely on javascript and you don't want to persist the form data in a session, then you run into the issue of not redirecting after the first POST but other than that it should be simple from the outline above.

Pass variables to a "success" page after processing a form in Django

Is there anyway to pass context variables to a redirect response? I want to redirect a user to a success page after they submit a form, but I don't want the success page to be just a static html file. I need to display extra information based on the form data.
I have looked at this question, but the solution presented there simply renders a different file at the same url. I'd like to redirect the user so that hitting refresh at the page won't submit duplicate entries into the application.
Right now the only thing I have been able to use with some success is redirecting to a url while passing it GET variables as described here. That just seems like a bit of a hack, and was just wondering if there is any better solution...
Thank You
The way I see it you have three options:
Use GET variables in the redirect.
Store something in the session.
If you are creating an object using the form that was submitted, put the id of that object in the redirect url and use it in the new view.
The limitation you are running up against is that http is stateless, not something inherent in django.
How about storing your values in a session, then have the redirected page pick up the values from there?

Pylons/Routes rewrite POST or GET to fancy URL

The behavior I propose:
A user loads up my "search" page, www.site.com/search, types their query into a form, clicks submit, and then ends up at www.site.com/search/the+query instead of www.site.com/search?q=the+query. I've gone through a lot of the Pylons documentation already and just finished reading the Routes documentation and am wondering if this can/should happen at the Routes layer. I have already set up my application to perform a search when given www.site.com/search/the+query, but can not figure out how to send a form to this destination.
Or is this something that should happen inside a controller with a redirect_to()?
Or somewhere else?
Followup:
This is less an actual "set in stone" desire right now and more a curiosity for brainstorming future features. I'm designing an application which uses a Wikipedia dump and have observed that when a user performs a search on Wikipedia and the search isn't too ambiguous it redirects directly to an article link: en.wikipedia.org/wiki/Apple. It is actually performing an in-between HTTP 302 redirect step, and I am just curious if there's a more elegant/cute way of doing this in Pylons.
You can send whatever content you want for any URL, but if you want a particular URL to appear in the browser's address bar, you have to use a redirect. This is independent of whether you use Pylons, Django or Rails on the server side.
In the handling for /search (whether POST or GET), one would normally run the query in the back end, and if there was only one search result (or one overwhelmingly relevant result) you would redirect to that result, otherwise to a page showing links to the top N results. That's just normal practice, AFAIK.
HTML forms are designed to go to a specific URL with a query string (?q=) or an equivalent body in a POST -- either you write clever and subtle Javascript to intercept the form submission and rewrite it in your preferred weird way, or use redirect_to (and the latter will take some doing).
But why do you need such weird behavior rather than just following the standard?! Please explain your use case in terms of application-level needs...!

Categories