How to determine which button is pressed - html

I have a table in which I populate with data from the database. Some of this I have an extra feature of the delete button. But I can't understand how I get that what delete button is pressed in django
<tbody>
{% for i in list %}
<tr>
<td>{{i.id}}</td>
<td>{{i.reason}}</td>
<td>{{i.starting_date}}</td>
<td>{{i.ending_date}}</td>
<td>{{i.accept_or_not}}</td>
{% if i.accept_or_not == 'pending'%}
<td><input type="button" name="data_delete_{{i.id}}" value="delete"></td>
{%endif%}
</tr>
{% endfor%}
</tbody>
def profile_view(request):
if 'data_delete_id' in request.POST:
# i don't know how to determine the id in the button

This might be straight-forward. You can get the name of the button first and then parse the id such as:
def profile_view(request):
delete_button_id = ""
for name in request.POST.values():
if name.startswith('data_delete_'):
delete_button_id = int(name.split('_')[2])
break
# you now have the id in your delete_button_id variable

According to Django Docs for HttpRequest.POST your python code has to work as expected because POST returns a dict like Object.
def profile_view(request):
if 'data_delete_id' in request.POST:
But in your template you are using <input type="button" .
This will only trigger a client side operation. If you are not having any JS code which you have not shown here, I would say you need to use <input type="submit" instead.
I would also recommend to use <button type="submit" instead of input because from a semantic perspective it is more accurate.

Related

Flask-SQLAlchemy MySQL application won't write to database [duplicate]

I'm using Flask-WTF:
Here is my form:
from flask.ext.wtf import Form, TextField
class BookNewForm(Form):
name = TextField('Name')
Here is the controller:
#book.route('/book/new', methods=['GET', 'POST'])
def customers_new():
form = BookNewForm()
if form.is_submitted():
print "submitted"
if form.validate():
print "valid"
if form.validate_on_submit():
flash("Successfully created a new book")
return redirect(url_for('.books_show'))
return render_template('views/books_new.html', form=form)
Now the problem is, if you look at my print statements, it always prints submitted, but it NEVER prints valid and validate_on_submit() is never executed. Why?
You're not inserting the CSRF field in the HTML form.
<form method=post>
{{ form.csrf_token }}
{{ form.name }}
<input type=submit>
</form>
After adding form.csrf_token to the template (docs), the form will validate as expected.
Add print(form.errors) after validating the form to see the errors that were raised. errors will be empty before validation. In this case, there is an error about missing
#book.route('/book/new_no_csrf', methods=['GET', 'POST'])
def customers_new_no_csrf():
form = BookNewForm()
print(form.errors)
if form.is_submitted():
print("submitted")
if form.validate():
print("valid")
print(form.errors)
if form.validate_on_submit():
flash("Successfully created a new book")
return redirect(url_for('.books_show'))
return render_template('books_new.html', form=form)
{}
submitted
{'csrf_token': [u'CSRF token missing']}
127.0.0.1 - - [29/May/2012 02:01:08] "POST /book/new_no_csrf HTTP/1.1" 200 -
127.0.0.1 - - [29/May/2012 02:01:08] "GET /favicon.ico HTTP/1.1" 404 -
I created an example on GitHub.
you can print errors
print form.errors
or
app.logger.debug(form.errors)
and if you got csrf-error, you should set form.csrf_token in your template.
I came across this when trying to render a FormField being iterated over my FieldList in my template. I had to embed two hidden_tag elements one for the FieldList form and one for the FieldForm form, search the template comments for keyword "HIDDEN TAG"
class ParamRangeForm( FlaskForm ):
minX = FloatField( )
maxX = FloatField( )
class ParamRangesForm( FlaskForm ):
paramRanges = FieldList( FormField( ParamRangeForm ) )
submit = SubmitField( 'Submit' )
def loadParams( self ) :
for paramName in ["p1" , "p2" , "p3", "p4"] :
prf = ParamRangeForm( )
prf.minX = -100.9#float('-inf')
prf.maxX = 100.5#float('-inf')
self.paramRanges.append_entry( prf )
...
<form action="" method="POST" enctype="multipart/form-data">
{{ rangesForm.hidden_tag() }} <!--#### HIDDEN TAG #1 -->
<table>
<!--Print Column Headers-->
<thead>
<tr>
<th class="ColumnHeader">Parameter</td>
<th class="ColumnHeader">Min</td>
<th class="ColumnHeader">Max</td>
</tr>
</thead>
<!--Print Parameter Rows-->
<tbody>
{% for paramRange in rangesForm.paramRanges %}
<tr>
{{ paramRange.hidden_tag() }} <!--#### HIDDEN TAG #2 -->
<td>p{{ loop.index }}</td>
<td>{{ paramRange.minX }}</td>
<td>{{ paramRange.maxX }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{{ rangesForm.submit() }}
</form>
insert this after the tag in template html file:
{{ form.csrf_token }}
I was clearing the flask session if I wasn't logged in before every request. This was causing this issue.
#main.before_request
def before_request():
if not current_user.is_authenticated():
# TODO clean sessions may cause CSRF missing issue
session.clear()
print "Session Cleared"
return redirect(url_for('auth.login'))
I think the API has changed.Maybe try changing
from flask.ext.wtf import Form
to:
from flask_wtf import Form
I spent a several hours debugging a validation issue with Flask-WTF. The issue like many others was a CSRF validation issue. However, mine was not caused by any of the common issues I have found.
The standard Flask-WTF implementation of CSRF requires two things be delivered to the browser.
One: The hidden CSRF form field e.g.
<input id="csrf_token" name="csrf_token" type="hidden" value="ImYzODdmZTdhYTRlMmNkYWRjYmRlYWFmZjQxMDllZTQ1OWZmYzg3MTki.XKvOPg.gUCkF9j-vg0PrL2PRH-v43GeHu0">
Two: The session cookie HTTP response header e.g.
Set-Cookie: session=eyJjc3JmX3Rva2VuIjoiZjM4N2ZlN2FhNGUyY2RhZGNiZGVhYWZmNDEwOWVlNDU5ZmZjODcxOSJ9.XKvOPg.a3-W62MHvaGVkv2GYCi-dgpLE3Y; HttpOnly; Path=/
If either of these are missing the browser will fail to send the proper CSRF validation. Of course, this in turn causes the form validation to fail.
If the csrf_token hidden field is present in the form but the session cookie is missing, you will receive the following response when the form is submitted...
Bad Request
The CSRF session token is missing.
In my case the session cookie was missing because of a bug in my code. I needed to serve a custom HTTP header across the entire Flask site. I included it like this...
class LocalFlask(Flask):
def process_response(self, response):
response.headers['my-header'] = 'My Header Value'
return response
app = LocalFlask(__name__)
This however causes anything that rellys on the the Flask.response.headers method to fail. One of those is Flaks-WTF setting the session cookie HTTP header.
This can be solved by adding the super() method to the LocalFlask class so that it inherits methods form the Flask class.
class LocalFlask(Flask):
def process_response(self, response):
response.headers['my-header'] = 'My Header Value'
#LocalFlask inherits methods from Flask
super(LocalFlask, self).process_response(response)
return response
app = LocalFlask(__name__)
Well I tried all the solutions mentioned
form.hidden_tag()
form.csrf_token
form.csrf
with
app.secret_key=""
app.config["SECRET_KEY"]=""
but form.validate_on_submit() always returned false.
None of these seem to work for me, so I used the basic method and this method
import request
request.method="POST"
or
form.is_submitted()
These two worked for me

Query input data from HTML to flask mysql

I am both new to flask and posting here.
I am trying to query a MySQL database with a variable input from HTML to Flask.
If I fix the query from the python code, it works. However, trying to query my database using the input variable doesn't give any result.
Here is my HTML code for the input variable :
<div class="search">
<form action="{{ url_for('search') }}" method="POST">
<input type=text name="search"></br>
<div class="actions"><input type=submit value="Search"></div>
</form>
</div>
Here is the python code for the search query:
#app.route('/search', methods=["POST" , "GET"])
def search() :
if request.method == "POST" :
search = request.form["search"]
cursor=mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute( '''SELECT * FROM sites WHERE SITE_CODE=(%s)''', (search,))
data=cursor.fetchall()
return render_template("search.html", data=data , search=search )
else :
return render_template("search.html")
here is the jinja2 code for the output :
<table>
<td>Site Code</td>
<td>OM VLAN id</td>
<td>IuB VLAN id</td>
<td>Abis VLAN id</td>
<td>S1X2 VLAN id</td>
</tr>
{% for sites in data %}
<tr>
<td>{{sites.SITE_CODE}}</td>
<td>{{sites.OM_VLAN_id}}</td>
<td>{{sites.IuB_VLAN_id}}</td>
<td>{{sites.Abis_VLAN_id}}</td>
<td>{{sites.S1X2_VLAN_id}}</td>
</tr>
{% endfor %}
</table>
I have a fully ready to use database(I do not have to change anything in it, I just need to query searched data from it) note that everything is working fine when I try to show all the data from the table :
#app.route("/")
#app.route('/IP_PLAN',methods=['GET','POST'])
def IP_PLAN():
cursor=mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute("SELECT * FROM sites")
data=cursor.fetchall()
return render_template("IP_PLAN.html",data=data)
UPDATE:
everything actually works, I just need to type the exact name of the "SITE_CODE". is there any way to get a result without doing it? I want to get as an output all the sites that contain an A in their SITE_CODE for example.
Thanks
UPDATE 2:
I finally achieved what I wanted to do, I just had to change the cursor.execute command :
cursor.execute( 'SELECT * FROM sites WHERE SITE_CODE LIKE %s', ['%' + search + '%'])

How do I access a single field while using For Loop to iterate through all the fields of a ModelForm in Django Template?

I have a model which has four ForeignKey fields, so they are dropdown fields in the form.
class Package(models.Model):
patient=models.ForeignKey(Patient, on_delete=CASCADE)
diagnosis=models.ForeignKey(Diagnosis, on_delete=CASCADE)
treatment=models.ForeignKey(Treatment, on_delete=CASCADE)
patient_type=models.ForeignKey(PatientType, on_delete=CASCADE)
date_of_admission=models.DateField(default=None)
max_fractions=models.IntegerField(default=None)
total_package=models.DecimalField(max_digits=10, decimal_places=2)
The forms.py:
class PackageForm(ModelForm):
class Meta:
model=Package
fields='__all__'
widgets={
"patient_type" : forms.Select(attrs={"onblur":"mf();"}),
"max_fractions" : forms.NumberInput(attrs={"onfocus":"mf();", "onblur":"tp();"}),
"total_package" : forms.NumberInput(attrs={"onfocus":"tp();", "onblur":"onLoad();"}),
'date_of_admission': DateInput(attrs={'type': 'date'}),
The views.py:
def package_view(request):
if request.method=='POST':
fm_package=PackageForm(request.POST, prefix='package_form')
if fm_package.is_valid():
package=fm_package.save()
IpdReport.objects.create(patient=package.patient, package=package)
fm_package=PackageForm(prefix='package_form')
return render (request, 'account/package.html', {'form5':fm_package})
else:
fm_package=PackageForm(prefix='package_form')
return render (request, 'account/package.html', {'form5':fm_package})
The Template:
<form action="" method="post" novalidate>
{% csrf_token %}
{{form5.non_field_errors}}
{% for fm in form5 %}
<div>
{{fm.label_tag}}
{{fm}}
<span>{{fm.errors|striptags}}</span><br><br>
</div>
{% endfor %}
<button type="submit" id="savebtn">Save</button>
</form>
Now, what I want is to insert an Anchor Tag next to all the foreign_key fields, in the template, to add a new object into the original table. For example, an Add Patient option next to the Patient's dropdown field, when clicked, a new, small window would show up with Patient form. The user enters the new patient's data, saves it and the same name shows up in the dropdown.
But as I am using a For Loop in the template, how would I be able to access those foreign key fields and apply the options? Any suggestions, please?
If it isn't a problem I would move away from rendering all of the fields with the 'forloop'. Instead I would use notation: form.field to render different fields. So it would look like:
{{ form.patient.label_tag }}
{{ form.patient }}
It should be much easier to navigate through fields this way, but of course it will require more typing :)

Pass html form action parameters to django view

I'm making an app to save mileage of a truck per state. I've already passed required data to my view, and then I thought to change my urls to more logical. And after that I faced a problem.
I don't know what should be instead of "unit.unit_number", and it is required, in my html file for it to work correctly. I didn't find anything that could explain how to deal with it.
If I try to access mywebsite.com/core/units/1/locations/add/ I get next error message:
"NoReverseMatch at /core/units/1/locations/add/"
But if I put just a number (1 for example) instead of "unit.unit_number" it loads the page normally, but I get an error after trying to post the data:
"TypeError at /core/units/1/locations/add/
_reverse_with_prefix() argument after * must be an iterable, not int"
<form action="{% url 'Core:unit_add_location' unit.unit_number %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="container">
<div class="inner-container border-full">
<button type="button" class="button button-normal" onclick="AddRow()">Add</button>
<input type="submit" class="button button-submit" name="count-ifta" value="Save">
<div class="inner-container border-top-bottom">
<table id="myTable" name="state-miles-data">
<thead>
<th class="text-blue">State</th>
<th class="text-blue">Miles</th>
</thead>
</table>
<br>
</div>
</form>
<button type="button" class="button button-normal" onclick="AddRow()">Add</button>
</div>
</div>
def unit_data(request, unit_number):
return HttpResponse(unit_number)
def unit_add_location(request, unit_number):
if "GET" == request.method:
return render(request, 'Core/add_location.html')
elif "POST" == request.method:
states_request = request.POST.getlist('states')
miles_request = request.POST.getlist('miles')
return HttpResponseRedirect(reverse('Core:unit_data', args=(unit_number)))
urlpatterns = [
path('units/', views.units_all, name = 'units_all'),
path('units/<int:unit_number>/', views.unit_data, name = 'unit'),
path('units/<int:unit_number>/locations/', views.unit_locations, name = 'unit_locations'),
path('units/<int:unit_number>/locations/add/', views.unit_add_location, name = 'unit_add_location'),
path('units/<int:unit_number>/locations/<int:report_id>', views.unit_location, name = 'unit_location'),
]
So the thing I want to make is post the data and redirect to url "mywebsite.com/units/1/locations/" that is processed by the view "unit_locations"
To the second part about the iterable: (reverse('Core:unit_data', args=(unit_number))) <<< you want (unit_number,) which is a tuple not an int. Spot the , it is important.

django - change list in POST request

I have a tuple list and wanted to delete or add tuples in it depending on what button has been pressed. Adding tubles is functioning fine but my problem is, that for some reason if Im clicking on the button to delete a tuple, the list resets back the time to the state before the delete happened.
For example I have a list:
ctestformat = [('sung', 4, 15), ('ren', 3, 27), ('lexe', 4, 39)]
after deleting the number 15 I get:
ctestformat = [('ren', 3, 27), ('lexe', 4, 39)]
But after getting another POST request to delete or add, the list resets to the first state as if nothing got deleted
Here is my view to add and delete tuple depending on which button was clicked:
def editorstart(request, ctestformat=[]):
if request.method == 'POST':
"""If clicked on create gap button, create a new gap and put it in ctestformat"""
if 'create_gap' in request.POST:
selectedgap = request.POST['sendgap']
startindex = int(request.POST['startpoint'])-13
ctestformat.append((selectedgap, len(selectedgap), startindex))
ctestformat.sort(key=operator.itemgetter(2))
"""if clicked on deletegap, delete the gap from ctestformat"""
elif 'deletegap' in request.POST:
deleteindex = request.POST['deletegap']
test = [t for t in ctestformat if t[2] != int(deleteindex)]
ctestformat = test
# This function doesnt change anything to ctestformat
modifiedtext = createmodifiedtext(ctestformat)
return render(request, 'editor_gapcreate.html', {"text": modifiedtext, 'ctestformat': ctestformat})
If you have any other questions, just ask :)
EDIT:
added return in my view
my template:
{% extends "base_generic2.html" %}
{% block content %}
<form action="editorgapcreate" id=create method="POST">
<input type="hidden" name="sendgap" id="sendgap">
<input type="hidden" name="startpoint" id="startpoint">
<script src="../static/textselector.js"></script>
<div id="thetext" onmouseup="getSelectionText()">
<h1>{{ text|safe }}</h1>
</div>
{% csrf_token %}
<p></p>
<b>Your current selected gap:</b>
<p id="currentgap"></p>
<input type="hidden" name="text" id="text" value="{{ text }}">
<button type="submit" name="create_gap" id="gapcreate">Create gap</button>
</form>
{% endblock %}
Using a mutable value for a default argument in Python (a list in this case) is not normally a good idea. The list is created once when the function is defined, which means any changes you make to it are visible in subsequent function invocations. However, it seems as though this may be intended in your case.
The reason why you're not seeing the list change, is that the assignment you're making ctestformat = test after filtering out an item has no effect. You need to mutate the original list rather than reassigning, by first finding the index of the item within that list, and then using pop() to remove it. For example:
elif 'deletegap' in request.POST:
deleteindex = request.POST['deletegap']
for i, t in enumerate(ctestformat):
if t[2] == int(deleteindex):
ctestformat.pop(i) # Modify original list
break
...
I would still recommend not using a mutable default argument to achieve this. If you need to share data across requests, you'd be better to use a cache or a database, or possibly session state, depending upon your application requirements.