Submit form that uses Intercooler in Django StaticLiveServerTestCase - python

I am trying to write a selenium test for the submission of a form, which uses intercooler.js when it is submitted. The main problem I am having, is that when I navigate to the page, the form has class="disabled", which is not expected behaviour, and I can't submit the form. The relevant part from the intercooler docs says:
By default, intercooler will apply the disabled class to the element
that triggers an intercooler request. This can be used to give a
visual hint to the user that they should not click or otherwise
trigger the request again, and is Bootstrap-friendly.
However, it seems to me that the disabled class is being added to the form element before I actually submit the form, and as I understand it should only be added after a request is in-flight.
The form currently looks like this:
<form ic-post-to="/dashboard/calculate/2/exports/" ic-select-from-response="#content" ic-target="#content" method="post" ic-src="/dashboard/calculate/2/exports/" ic-verb="POST" ic-trigger-on="default" ic-deps="ignore" class="disabled">
<input type="hidden" name="csrfmiddlewaretoken" value="...">
<input type="submit" name="new" value="New" class="btn btn-primary float-right ml-1" id="submit-id-new">
</form>
I have tried adding explicit and implicit waits so that the entire page will load but the problem is still there.
Any help with this would be much appreciated.

Try to use Javascript to click on the button:
submit = driver.find_element_by_id("submit-id-new")
driver.execute_script("arguments[0].click();", submit)

It turns out that I was waiting in the wrong place. Instead of waiting when the page loads, I should have been waiting after the form was submitted to allow the page content to be updated.

Related

Django: How do you use an image in your template and let your views.py know it was pressed like a button?

I want to show an image that the user can click on that will act like a button and return the data to my views.py.
For example,
<input type="submit" value="Add Selected Other Service to Included Service" class="button" name="Add Other Service"/>
will create a very long button which I can "grab" in my views.py with:
add_other_service = request.POST.get('Add Other Service')
I can then test add_other_service and term if that was the button pressed. Hence, I can have multiple buttons on the page and determine which one as pressed.
I know I can use the tag with the type="image" to click on the image, but I cannot find a way to get name of the button in the views.py.
After trying different variations to see if I could get:
<input type="image" src="{% static 'addAndReturn.svg' %}" width="48" height="48"
name="addAndReturn" class="btTxt submit" title="Add & Return to Edit"/>
to work and looking for:
add_and_return = request.POST.get('addAndReturn')
in my views.py, I finally printed out the request.POST and found:
'addAndReturn.x': ['36'], 'addAndReturn.y': ['30']
I do not know html enough to realize I'd get this type of data. When I searched for getting the data from the image, I found out about getting the actual image and not to determine if the image was clicked so I added this to help others look to use the image like a button that returns to the views.py post() and not just use another URL.

Using flask form result to generate a URL?

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.

How to secure URLs in Django

Is there a way to prevent users from accessing some (or all) URLs in application? For example, I am following Django tutorial and one of the examples has a URL:
#music/album/<pk>/delete
url(r'image/(?P<pk>[0-9]+)/delete/$', views.ImageDelete.as_view(), name='image-delete'),
that deletes database entry give pk as a parameter. Of course, now it is possible to delete this entry with just copy-pasting the URL with any existing primary-key, so what is the best practice to avoid it? Thanks
EDIT. Based on the replies and comments, I decided to elaborate a bit more. I am actually using DeleteView and forms with POST request as #solarissmoke suggested in answer.
<form action="{% url 'album:image-delete' image.id%}" method="post" style="display: inline;">
{% csrf_token %}
<input type="hidden" name="image_id" value="{{ image.id }}"/>
<button type="submit" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-trash"></span>
</button>
</form>
and in my views.py:
class ImageDelete(DeleteView):
model = Album
# if you successfully delete the object, page redirects to <homepage>
success_url = reverse_lazy('album:index')
So, there were few suggestions on checkin whether the user is verified to delete URL entry (e.x. the image) and to add pop up/notification to verify if the user indeed wants to delete the entry. However, it does not feel like a complete solution. In the comments I brought example of Facebook, where you can not delete imeage/post by just copy-pasting the delete URL. Surely I'm not asking for Facebook-like security, however, I'm really curious how can secure URLs so that it's nearly impossible for regular user to delete entry with simple copy-pasting. Thanks again!
Best practice is that you should not be allowing modification of data like this through HTTP GET requests, which are intended (as the name suggests) for getting data rather than updating it.
You should use forms and POST requests to perform actions like deleting objects etc. Django provides lots of helper views for doing this. For example DeleteView:
A view that displays a confirmation page and deletes an existing object. The given object will only be deleted if the request method is POST. If this view is fetched via GET, it will display a confirmation page that should contain a form that POSTs to the same URL.
The advantages of using these views are:
You can make sure the user has permissions to edit an object before making any changes. Django will perform the basic checks (e.g., CSRF) for you. You can augment the views to perform additional checks like making sure a user is logged in or checking any other permission.
You can enforce Cross-Site Request Forgery Protection.
It is not possible to accidentally delete an object by visiting a URL a second time (as the documentation above explains).
there are many ways.. e.g:
user = request.user
if user.is_authenticated() and user.profile.can_delete_image(image_pk):
# only then, image can be deleted by this user
# can_delete_image(image_pk) is defined by you
else:
raise DeletePermissionDenied # you can define your own Exception, just for fun

flask error sending POST and GET to same function,

This is a function which (in a GET request) receives a case_url and case_key and serves the corresponding case (using mongoDB) to a html template called detail_case.
Im trying to add a feature where when a form is filled(on this same page detail_case) and it is submitted, it should submit a POST request to the same function and the code under 'if request.method=="POST"' should get executed.
#app.route('/case/<case_url>/<case_key>', methods=["GET","POST"])
def serve_case(case_url,case_key):
"""for saving a comment in db.comments"""
if request.method == "POST":
text=request.form['comment_text']
#code which inserts it in the database
return redirect(url_for('serve_case', \
case_url=case_url,\
case_key="Highlights"))
"""
Function serves the case as per the key indicated in the URL
"""
#corresponding code here which fills values of variables and sends it to another page
return render_template('detail_case.html')
The problem is that I don't think the POST request is ever executed. This is the html code on the template page detail_case-
<textarea placeholder="Please enter your comments here" action="{{ url_for('serve_case',case_url=case_url,case_key=case_key)}}" method="POST" name="comment_text" rows="6"></textarea><br />
The problem i think is the action field. I don't know how should I send the variable comment_text to my function. Infact, the code under POST does not get executed when I submit.
Basically the issue is that during a GET request, it sends 2 variables which are needed in the parameters of the function serve_case. During my POST request, well, I don't know how to exactly frame the action field. If I send no parameters, its an error. If I don't send it to the same function, then how will it execute the POST code? Could someone please suggest sumthing?
i'm pretty new to flask, i'm editing someone else's code
You need to submit the POST request (for example through form) like below:
<form action="{{ url_for('serve_case',case_url=case_url,case_key=case_key)}}" method="POST">
<input type="text" placeholder="Please enter your comments here">
<input type="submit" name="comment_text" rows="6"><br />
</form>

How to stop a Python subprocess using a HTML form button?

I'm using Django as the web framework. On a single HTML page, I have a form whose purpose is to start a Python subprocess when I click on its submit button. The fields of the form will be filled with parameters which will be passed to this subprocess.
Now, I want to be able to stop the same subprocess using another form button, preferably within the same form. How can I do this? It's probably not that difficult, but I don't know at the moment how to accomplish that in a simple but elegant way. Any ideas? Thank you very much!
You have to keep a reference to the process in the session. Give each submit button a different "name" attribute, and if this signals stop, kill the subprocess.
from subprocess import Popen
if 'start' in request.POST:
p = Popen("do something")
request.session['myprocess'] = p
elif 'stop' in request.POST:
request.session['myprocess'].kill()
Form:
<form method="POST">
<input type="submit" name="start" value="start"/>
<input type="submit" name="stop" value="stop"/>
</form>
Whether this works depends on your session-storage backend though.

Categories

Resources