Remove URL Params after page loads in Google App Engine - python

First of all, I am very new to web programming in general. I've read lots of tutorials online and it's a little confusing since some of them are out-dated. So if I'm heading down a path that is fundamentally wrong, I'd love suggestions about The Right Way. Having said that, this is just a for-fun project, so if there is a simple (<10 lines) way to get what I want, I'd love to see quick results today even if long-term I end up fundamentally changing things.
So anyway, I'm using Google App Engine (Python 2.7) to do a simple calculation. I show a form, the user fills in values, clicks a button at the bottom, and some result text is displayed with the result of the calculation. This part is working fine.
For my own testing, it's annoying to always re-type the input values, and some of the fields aren't totally obvious to users, so I'd like to be able to send a link to someone off the form "http://buggyapp.appspot.com/calculation?input1=100&input2=200" and have it fill in those two parameters. This part works, too.
The problem is when the user loads that sort of link, changes one of the values in the form, and then clicks the button. Instead of using the (changed) form value, it uses the value in the URL. I'd like to fix that, and ideally I'd like the URL bar to not even show the URL Params after the page loads.
I'm not sure what info is necessary to help you find what I'm doing wrong.
Here's a section from the HTML, which is a Jinja2 template:
<form method="post">
<input value="{{ input1 }}" name="input1">
<input value="{{ input2 }}" name="input2">
<button class="submit" type="submit">Calculate</button>
</form>
<p>{{ result }}</p>
Here's the python code:
def get(self):
input1 = request.get('input1')
input2 = request.get('input2')
# some irrelevant(?) code to set default values on the initial load if there are no URL Params
result = str(input1+input2) #actual calculation slightly more complicated than this
template = JINJA_ENVIRONMENT.get_template('calculation.html')
self.response.write(template.render(vars()))
It seems to behave the same no matter whether I put the calculation in the get handler or post handler. At the moment, I'm simply calling one from the other:
def post(self):
input1 = request.get('input1')
print input1 # even this shows the URL value (if one exists), not form value
return self.get();
So, my next step to make it work would be to change the names of the form inputs so they are different from the URL Params, and add extra code to merge the potential input sources. But that won't really get me what I want, which is for the URL Params to disappear once they have populated the input forms. It also complicates the part of the code that generates those sorts of links.
I'd prefer a Python-only solution, but I'm open to using javascript. There are other stack overflow questions/answers that imply a javascript mechanism will do what I want, but I don't understand any of them well enough to apply them to my problem.
I'm sure I'm doing lots of other things wrong, and I may have over-simplified things or not posted enough info. I'm here to learn, so fire away. And thanks in advance for the help.
tl;dr- How do I use the URL Params once and then remove them from the URL so they don't override form inputs on subsequent posts?

HTTP GET requests usually include the parameters in the URL since there's no other place to pass them (there's no request body).
HTTP POST requests usually include the parameters in the request body, though it's still possible to include the parameters in the URL.
It looks like you're using some sort of Javascript to post the form. Make sure you are using a POST request and putting the parameters in the POST body. Most libraries automatically put params in the POST body as long as you're issuing a POST request.
EDIT:
Forms usually issue POST requests by default. Usually the <form> element will have an action attribute that specifies the URL to send to. However, if it doesn't have an action attribute it'll issue a POST request to the current URL.
In your case the current URL contains parameters and those are submitted again with your request. You should have a few options.
Specify the action in your form so you aren't submitting to the same url with parameters already attached.
In your request handler, read the data out of the post body (request.POST['input1']) instead of the url.

Related

Django: how to get HTML input field value to django view?

i want to get a value in an input to a django views, this is not a form as i am not trying to submit any form, i just need the value that is in the input field
this is the html template
<input readonly name="ref_code" type="text" value="{{ request.user.profile.recommended_by.profile.code }}">
this is my django views
code = str(request.GET.get('ref_code'))
print("This is the code:" + code)
It keep printing This is the code: None
NOTE: i have also tried using request.POST.get("ref_code") it did not still work
What might be the issue?
You definitely need something like a form.
So, let's walk you step by step on how your code works.
First, there is a view which prepares the data to be rendered with the template. This view responds to the first request from the user's browser and returns the rendered template as a static html page to the user.
Second, user's browser displays received html page to the user.
Third, user enters something into the input field.
At this stage the data user has entered is still on their side in their browser.
At this point you have two options:
Use a form to let user send the data to some view on the server which will then process the received data.
Use JavaScript to capture the entered data and send it to the server using an AJAX request.
I guess, you would like to use the second option in this case.
I'd recommend to read a guide on AJAX basics. The Ajax guide on MDN is a good place to start.

Variable URLs and passing non-input value from html to Python (Flask)

According to this question you can only send data from input forms from html to Python with POST. I'm trying to figure out how to pass a value (that's actually originally contained in a dictionary that I passed in from Python) from html to Python.
My two approaches I considered (and have not figured out how to do successfully) are:
Taking a look at the Flask quickstart, this should be quite simple. I'm just not sure what the syntax should look like on the html side to pass in this parkCode.
#app.route('/park/<parkCode>', methods =['GET', 'POST'])
def park(parkCode):
return render_template('park.html', parkCode = parkCode)
Alternatively, is there some way to simply send a string from html to Python without using an input form? I have yet to find a way to do this.
For reference, this is the line where I'm sending over the ```parks`` dictionary:
return render_template('search_results.html', parks=parks)
Then, in my search_results.html file:
{% for park in parks %}
<div method = "POST" action = "/park">{{park["fullName"]}}</div>
{% endfor %}
But I want the to send the park["fullName"] to my Python code.
.route decorator always handles only URL paths. Since form action is a static value in HTML, the only way to change it is to use JavaScript (for example, by changing the action attribute at submit time). If you're going to use JavaScript, you might as well then just use JavaScript to submit the request itself, which leads us to
Yes, you can use AJAX to send a request. Since ES6, the easiest way to do this is fetch. The choice of whether to use a form or whether to use AJAX depends on what you want to happen after the request: a submitted form results in a new page being rendered, while an AJAX request cannot change the current page, only trigger JavaScript code (although obviously you can change the page in JavaScript should you so wish).
Basically, you can't do what you want without JavaScript. The third option that does work without JavaScript is using the form as it was meant to be used. On flask side, it involves not naming the parameter inside the route, but using request.args (for GET forms) or request.form (for POST forms):
#app.route('/park', methods =['POST'])
def park():
parkCode = request.form.get('parkCode')
return render_template('park.html', parkCode = parkCode)
with the accompanying HTML:
<form action="/park" method="POST">
<input name="parkCode"/>
<input type="submit" value="Submit"/>
</form>

How to request URL of previous page visited and store parts of it as a ForeignKey?

I have a django application that has an architecture:
Myapp.com/< uniquepageURL>
For posting new things to the page:
Myapp.com/< uniquepageURL>/post
For commenting on a specific post on a page
Myapp.com/< uniquepageURL>/comment< ID# of the post>
For example, if you are commenting on the post that is stored in the database with an ID of 22 then the URL should look like:
Myapp.com/< uniquepageURL>/comment22
This is how I’ve decided to map my urls. Is there a better/simpler way to do it? The < uniquepageURL> is also the ForeignKey of the post object in my models. What I am having trouble with is figuring out how to use the request function in django to look at what the url was so as to gain information from it, namely for posts which uniquepage it is from so I can save the value into the foreignkey field and for comments which post ID it is a comment of. What would the views for each look like?
Here is my urls.py.
For posts:
url(r'^(?P<uniquepageURL>[a-zA-Z0-9_.-]*)/post/$', views.post, name='post')
for comments:
url(r'^(?P<uniquepageURL>[a-zA-Z0-9_./-]*)/comment(?P<post.pk>[0-9]*)/$', views.comment, name='comment')name='comment')ts:
I think the post.pk part is wrong since it returns an error. But I can fix that myself hopefully if I can figure out how to get the post section working at least.
Be aware that the ?P<uniquepageURL> syntax indicates a named group; Django will attempt to match that name to arguments in the view function (see https://docs.djangoproject.com/en/1.7/topics/http/urls/#named-groups).
So, to get that value passed to your view function, simply include a parameter uniquepageURL. Note that means post.pk won't work as a name; consider post_id as an alternative. (Though I supposed you could use a **kwargs construct and get at the value with kwargs['post.pk']).
That said, if you do need other information from the request URL, I think the value you are looking for is request.path (which presumes your signature is def comment(request, uniquepageURL, post_id) or similar.

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.

What is the best secure way to allow a user to delete a model instance that they added to the db?

I would like to give users access to delete a model instance that they added to the db. In the django docs it says allowing someone to delete from the template is not a good practice. Is there a secure way to let a user click a "delete this" link from the template and remove that model instance? How should I go about doing that?
Check out this question for discussion related to what you are asking about.
Essentially, when you normally click on a link on the page the browser makes a GET request to the server to get the next page's contents. Just like there is a lot of pushing towards semantically relevant CSS layouts, it is also important that your page requests are semantically relevant. The problem with using links to remove items is that it is making a GET request to DELETE something in the database. From this comes the problem that some search engines might index your links and accidentally erase content. There also comes the problem of cross-site request forgeries which can make an unsuspecting user make a command to a website without being aware. So the proper way to handle this is by following the rule that any request that modifies state in the server should be processed via POST. As such, instead of doing this:
Delete Item
It is better to do this:
<form action='{% url remove_item %}' method='POST' id='form'>
<input type='hidden' name='action' value='delete'>
<input type='hidden' name='id' value='{{ item.id }}'>
<input type="submit" value="Delete Item">
</form>
If you would like to keep your links while maintaining the POST, you'd have to resort to Javascript:
Delete Item
Unsightly, yes, but it's for the best. Your Django view would then do something like this:
def remove_item(request):
if request.method == 'POST':
## remove item
Furthermore, as Scott mentions, Django has some built in stuff to help you avoid the cross-site request forgeries I mentioned above, since it is still possible to do it even if you are doing a POST (just slightly harder). The way to avoid this is to have some kind of token tied to the form that needs to be validated server side before allowing the action to be taken. Check out the CsrfMiddleware class for more details on that. It will essentially automate some of that work out of it for you.
Additional Reading
URIs, Addressability, and the use of HTTP GET and POST
9.1.1 Safe Methods, HTTP 1.1, RFC 2616
Architecture of the World Wide Web, Volume One
Using POST with a regular link
Cross-Site Request Forgeries and You
Have the user submit a POST request to delete that model instance. These kinds of changes should never be possible via GET requests, so that people can't link each other to unwittingly performing changes on the site.
In your view, check that request.user is the same as the author of that particular model instance. You could also check that the HTTP_REFERRER is not set to another site if you were really worried.
Your security issue here is Cross Site Request Forgery. Django provides CsrfMiddleware which will actually add security to your forms to prevent this kind of attack. But it only works as long as you're not allowing permanent changes to take place via GET requests.

Categories