I have code which should handle multiple post request on the same page. It has two button in the HTML page inside a single form. It is taking the post value from first button, but not the second. I have no idea why, when I include the second button and click on first, it says the second button is not found. Can anyone please help me solve this issue?
index.html
<input type="submit" style="background-color:#FFFF66;color:black;width:150px; height:40px;" name="ServerDetails" value="ServerDetails"/>
<br><br>
<input type="submit" style="background-color:#FFFF66;color:black;width:150px; height:40px;" name="GenerateFile" value="GenerateFile"/>
views.py
if request.method == 'POST':
if request.POST['ServerDetails']:
print("Inside Server Details")
if request.POST['GenerateFile']:
filename = request.POST['patchconfigofpg3']
print(filename)
models.py
class course(models.Model):
patchconfigofpg3 = models.CharField(max_length=100)
When I click on the first button it is throwing me the below error:
just change
if request.POST['ServerDetails']:
to
if request.POST.get('ServerDetails',False):
and do same for other button
The problem is you write request.POST['ServerDetails'] and request.POST['GenerateFile']. Bascially you try to access the key on the QueryDict but this key does not exist giving you an error. Instead you should use the in operator:
if request.method == 'POST':
if 'ServerDetails' in request.POST:
print("Inside Server Details")
if 'GenerateFile' in request.POST:
filename = request.POST['patchconfigofpg3']
print(filename)
Related
I have created a contact page and there I created a form that looks like this
Now I want that 'submit' button to check whether all the input fields are entered. If it is then I want to activate the
<form action="{% url 'contact' %}" method="post">
Otherwise no post method activation. Instead a popup message box will appear telling that "Please enter all the fields"
If post method is activated, I want to go to the {% url 'contact' %} that is my views.py file and check if the system is able to retrieve all the values from the input fields.
If it does it will render a fresh contact page return render(request, 'myapp/contact.html')
And popup another message box to the browser that "Form is submitted successfully"
Else another message box to the browser that "There is an error occurred submitting the form"
I really can't find a way to implement this. I am a newbie is Django and Python. I have very minimum experience in html and CSS. But no JavaScript, though I used a small JavaScript code, I wish to completely use Python, HTML and CSS. Is that possible to implement this scenario using my desired language?
If yes, please tell me how.
You can do it on client side validation. To validate, none of the fields should be left blank. Like this
function validateForm() {
var isValid = true;
$('.form-field').each(function() {
if ( $(this).val() === '' )
confirm("All Fields are required!");
isValid = false;
});
return isValid;
}
I would recommend using Django forms. https://docs.djangoproject.com/en/3.0/ref/forms/api/#django.forms.Form
Create a contact form with the form class, pass it into the template. It will make the HTML tags for default browser validation. It will also allow backend validation when the form is submitted. You can then redirect the user if the form is valid or invalid.
If you wanted to do it from scratch you could do the following.
In your HTML make sure you add required attribute to the required input fields
<input name="name" type="text" ... required ... >
Import messages from django messages, check user input is valid, add message and render contact.html . This will send a message into the template. You can style the message with HTML and CSS.
In views.py
from django.contrib import messages
if request.method == POST:
...
name = request.POST.get('name')
if name == '':
messages.warning(request, 'Please enter name !')
return render(request, 'myapp/contact.html')
...
look at my code if simple just make an if statement like I did look the second and third line
def signup(request):
if request.method == 'POST':
if request.POST['password1'] == '':
return render(request, 'accounts/signup.html', {'error': 'Please fill all the fileds '})
elif request.POST['password1'] == request.POST['password2']:
(YOUR CODE HERE )
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.
PROBLEM STATEMENT
I'm working on a Flask web app that displays a list of items in a table. The user can select a row and hit a Delete button to delete the item. However, before the item is deleted from the database, the user is first routed to a confirmation screen where some item details are displayed as well as a Confirm button. The url for the confirmation page follows this pattern: dashboard/confirm-delete/<id> and the url for the actual delete page follows this pattern: dashboard/delete/<id>. See admin/views.py below for more details.
While the system works, the problem I have is that a user can simply skip the confirmation page by typing dashboard/delete/<id>, where <id> is substituted by an actual item id, into the address bar.
QUESTIONS
Is there a way to prevent users from accessing dashboard/delete/<id> unless they first go to dashboard/confirm-delete/<id> (the confirmation screen)? Alternatively, is my approach wrong and is there a better one available?
CURRENT CODE:
Function in my dashboard.html page called when a row is selected and the delete button is pressed:
$('#remove').click(function () {
var id = getId();
window.location.href="/dashboard/confirm-delete" + $.trim(id);
});
Confirm button in confirm-delete.html (the delete confirmation page):
<a class="btn btn-default" href="{{ url_for('admin.delete_item', id=item.id) }}" role="button">Confirm Delete</a>
My admins/views.py:
#admin_blueprint.route('dashboard/confirm-delete/<id>')
#login_required
#groups_required(['admin'})
def confirm_delete_item(id)
item = Item.query.get_or_404(id)
return render_template('admin/confirm-delete.html', item=item, title="Delete Item")
#admin_blueprint.route('dashboard/delete/<id>', methods=['GET', 'POST'])
#login_required
#groups_required(['admin'})
def delete_item(id)
item = Item.query.get_or_404(id)
db.session.delete(item)
db.commit()
return redirect(url_for('home.homepage'))
SOLUTION
Based on the answer marked as accepted I solved the problem as follows:
First, I created a new form to handle the Submit button in the confirm-delete.html page:
admin/forms.py:
from flask_wtf import FlaskForm
from wtforms import SubmitField
class DeleteForm(FlaskForm):
submit = SubmitField('Confirm')
I substituted the Confirm Button code with the following to confirm-delete.html:
<form method="post">
{{ form.csrf_token }}
{{ form.submit }}
</form>
Finally, I merged both of the functions in app/views.py as follows:
#admin_blueprint.route('dashboard/confirm-delete/<id>', methods=['GET', 'POST'])
#login_required
#groups_required(['admin'})
def confirm_delete_item(id)
form = DeleteForm()
item = Item.query.get_or_404(id)
if form.validate_on_submit():
if form.submit.data:
db.session.delete(item)
db.commit()
return redirect(url_for('home.homepage'))
return render_template('admin/confirm-delete.html', item=item, form=form, title="Delete Item")
This way, a user can't bypass the delete confirmation screen by typing a specific link in the address bar, plus it simplifies the code.
As already mentioned in comments, one way of solving your problem is checking for a certain cookie as the user sends a request. But personally I would not recommend this method, because such cookies can very likely be compromised unless you come up with some sort of hashing algorithm to hash the cookie values and check them in some way.
To my mind, the most easy, secure and natural way of doing it is protecting /delete route with CSRF-token. You can implement it with Flask_WTF extension.
In a word, you have to create something like DeleteForm, then you put {{form.csrf_token}} in your confirm-delete.htmland validate it in delete_view() with form.validate_on_submit()
Check out their docs:
http://flask-wtf.readthedocs.io/en/stable/form.html
http://flask-wtf.readthedocs.io/en/stable/csrf.html
I would make the delete page POST-only. The browser may skip a GET request or try it many times, you have no control over it. A crawler could follow an anonymous delete link and delete all your wiki articles. A browser prefetcher could prefetch a logout link.
REST purists would insist you use GET, POST, DELETE and PUT methods for their intended purposes.
https://softwareengineering.stackexchange.com/questions/188860/why-shouldnt-a-get-request-change-data-on-the-server
So,
In HTML
<form action='/dashboard/delete/{{id}}' method='post'>
In Flask
#app.route('/dashboard/delete/<int:id>', methods=['POST'])
def delete(id):
I think there's a mistake in parenthesis.
#groups_required(['admin'})
Shouldn't it be ??
#groups_required(['admin'])
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.
I am trying to allow a user to favorite a video while they're signed out. I'm using #login_required to force the user to sign in, have the video saved to their account, and then redirect back to the page they were on. I am getting an error saying
UnboundLocalError at /loggedout_fav/
local variable 'video' referenced before assignment
on the return line under loggedout_fav. I put a print statement in the view but nothing is printing to the console either. Any advice on how to fix it?
html
<form method = 'post' action = '/loggedout_fav/'> {% csrf_token %}
<input type="hidden" value="{{video}}" name = "video_add_loggedout"/>
<input type='submit' value='Login to plate this video'/>
</form>
url
url(r'^loggedout_fav/', 'loggedout_fav'),
view
def loggedout_fav(request):
if 'video_add_loggedout' in request.POST:
video = request.POST['video_add_loggedout']
print video
print 'test'
try:
s = Everything.objects.get(profile = request.user, video = request.POST['video_add_loggedout'], playlist = 'Favorites', platform = 'youtube')
print "already exists"
except:
p = Everything(profile = request.user, video = request.POST['video_add_loggedout'], playlist = 'Favorites', platform = 'youtube')
p.save()
return HttpResponseRedirect(reverse('reserve.views.trending_video_player', kwargs={'author':'youtube', 'video':video}))
def trending_video_player(request,author,video):
...
You used 'id' instead of 'name' ! So this is never True:
if 'video_add_loggedout' in request.POST:
And video is never set, so it chokes on (you should have posted the stacktrace BTW, I had to reverse that):
return HttpResponseRedirect(reverse('reserve.views.trending_video_player', kwargs={'author':'youtube', 'video':video}))
First things first you could fix your Python, like this:
def loggedout_fav(request):
if 'video_add_loggedout' not in request.POST:
return HttpResponseBadRequest()
video = request.POST['video_add_loggedout']
try:
s = Everything.objects.get(profile = request.user, video = request.POST['video_add_loggedout'], playlist = 'Favorites', platform = 'youtube')
print "already exists"
except:
p = Everything(profile = request.user, video = request.POST['video_add_loggedout'], playlist = 'Favorites', platform = 'youtube')
p.save()
return HttpResponseRedirect(reverse('reserve.views.trending_video_player', kwargs={'author':'youtube', 'video':video}))
Of course, fix your HTM too, this:
<input type="hidden" value="{{video}}" id = "video_add_loggedout"/>
Should be:
<input type="hidden" value="{{video}}" name="video_add_loggedout"/>
Save the values to the session. https://docs.djangoproject.com/en/dev/topics/http/sessions/
Make sure you grab the values in the session before logging the user in so you won't lose them.
I believe what jpic said might well be the problem. I post a separate answer to clarify the problem: The UnboundLocalError is a python error meaning that somewhere in your code you access a variable prior to defining it.
In your 'loggedout_fav' view code, I see you attempt to access the 'video' variable in the last line. When the if 'video_add_loggedout' in request.POST statement is not True (and therefore the nested block not executed), 'video' variable will not be initialized and this Exception will be raised. Practically this means that you attempt to access your view without posting a 'video_add_loggedout' variable. I would reorganize the view as follows:
def loggedout_fav(request):
try:
Everything.objects.get_or_create(profile=request.user, video=request.POST['video_add_loggedout'], playlist='Favorites', platform='youtube')
except KeyError:
#do something when the variable is not set. eg:
return HttpResponseBadRequest()
return HttpResponseRedirect(reverse('reserve.views.trending_video_player', kwargs={'author':'youtube', 'video':request.POST['video_add_loggedout']}))
The above also demonstrates how you can use the get_or_create method to clean up your code a little.