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
Related
I'm testing API CRUD with browsable web API flask implementation, but the browser seems to send unexpected requests to the API.
Here is the code I'm testing :
from flask import request, url_for
from flask_api import FlaskAPI, status, exceptions
app = FlaskAPI(__name__)
notes = {
0: 'do the shopping',
1: 'build the codez',
2: 'paint the door',
}
def note_repr(key):
return {
'url': request.host_url.rstrip('/') + url_for('notes_detail', key=key),
'text': notes[key]
}
#app.route("/", methods=['GET', 'POST'])
def notes_list():
"""
List or create notes.
"""
if request.method == 'POST':
note = str(request.data.get('text', ''))
idx = max(notes.keys()) + 1
notes[idx] = note
return note_repr(idx), status.HTTP_201_CREATED
# request.method == 'GET'
return [note_repr(idx) for idx in sorted(notes.keys())]
#app.route("/<int:key>/", methods=['GET', 'PUT', 'DELETE'])
def notes_detail(key):
"""
Retrieve, update or delete note instances.
"""
if request.method == 'PUT':
note = str(request.data.get('text', ''))
notes[key] = note
return note_repr(key)
elif request.method == 'DELETE':
notes.pop(key, None)
return '', status.HTTP_204_NO_CONTENT
# request.method == 'GET'
if key not in notes:
raise exceptions.NotFound()
return note_repr(key)
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True)
When I tried to delete a specific note given its id (key) from the example notes list, the browser sends a POST method instead of DELETE, which is not supported by the route.
The base render template is located here where you can see DELETE button statement at line 104.
I edited the library code and move the form method there from POST to DELETE thinking it could solve the problem this way :
104 {% if 'DELETE' in allowed_methods %}
105 <form class="button-form" action="{{ request.url }}" method="DELETE" class="pull-right">
106 <!-- csrf_token -->
107 <input type="hidden" name="_method" value="DELETE" />
108 <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the resource">DELETE</button>
109 </form>
110 {% endif %}
But not, the browser is now sending a GET request with the query string _method=DELETE, instead of DELETE request/method.
Everything is OK when sending request to the API using curl
Can one of you guys with good flask html rendering skills check this out and test on its side?
Never seen form with method=DELETE. MDN documentation says it should be either a GET or POST. Also see this old stackoverflow question that also says it is not supported in forms.
The template you're referencing is for an API so it seems to me that they are supporting direct calls to the API (such as with curl or Postman which would support PUT, DELETE, etc) and calls via forms (which would only be GET or POST)
You should keep your original code where method = POST and add POST as a method for def notes_detail i.e. you should have
#app.route("/<int:key>/", methods=['GET', 'POST', 'PUT', 'DELETE'])
def notes_detail(key):
I abandoned the effort of making the browser sending DELETE requests, and dealed with form hiden inputs, keeping POST as the form's method
104 {% if 'DELETE' in allowed_methods %}
105 <form class="button-form" action="{{ request.url }}" method="POST" class="pull-right">
106 <!-- csrf_token -->
107 <input type="hidden" name="_method" value="DELETE" />
108 <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the resource">DELETE</button>
109 </form>
110 {% endif %}
Objective:
** To pass input value itemnumbervalue to itemnumber() in views.py
> Error occured:
method object is not subscribable line 17 in view.py
Already applied techniques to correct the problem:
case 1: Tried [] and () both braces
if (request.POST.get['submitted']):
c = request.POST['ENTER_ITEM_NUMBER']
I tried many options like "remove get", and tried braces() and []
views.py:
from .forms import InputForm
def home_view(request):
context1 ={}
context1['form'] = InputForm(request.POST)
return render(request, "input.html", context1)
def itemnumber(request):
if (request.POST.get['submitted']):
c = request.POST['ENTER_ITEM_NUMBER']
print("input valuegiven by user",c)*
cursor = connection.cursor()
try:
itemnumbervalue = c
C=cursor.execute(f"EXEC ValidateBusinessrule '0000000000{itemnumbervalue}'")
result_set = cursor.fetchall()
result_set1= [' {} '.format(x) for x in result_set]
context = {"row": result_set1}
return render(request, "home.html", context)
finally:
cursor.close()
forms.py
class InputForm(forms.Form):
regex = re.compile('^([1-9]{8})$', re.UNICODE)
ENTER_ITEM_NUMBER= forms.RegexField(max_length=8, regex=regex,help_text=("Required 8 digits between {0-9}."))
home.html
<body>
<table>
<tr>
<ul>
<th>(column 1,column 2)</th>
</ul>
<tr>
<ul >
{% for row in row %}
<td style= "text-align: center;">
{{ row }}
</td>
</ul>
</tr>
{% endfor %}
</tr>
</table>
</body>
input.html
<body>
<form action = "{% url 'item'%}" method = "POST">
{% csrf_token %}
{{form}}
<input type="submit" value=Submit" name="submitted">
</form>
</body>
problem details:
To get input from user and give this input to itemnumbervalue in itemnumber() in view.py. I already validated by putting itemnumbervalue='12345678' (without input value from user) is working fine and getting resultant table.
Try using request.POST.get('submitted') or request.POST['submitted'].
The difference is just that .get method returns None if key is not found in the dictionary, But with directly indexing key if the key is not found then it will throw KeyError exception.
To see what values you are getting in request.POST you can print it.
Also in print("input valuegiven by user",c)* remove the * at the end that's an invalid syntax.
P.S. I tried reproducing this issue and was successful in resolving by above specified method.
I have a form with a few fields, the first being where a person enters their ID #, which is tied to a separate model for verification. I made this function get_employee_name, which returns the name based on the ID from the other model, but I'm not sure how to display it in the page, right on the top, without refreshing after the person tabs/clicks out? I'm not too familiar with html, but I was reading an ajax GET request would do the trick, but I'm not sure how to approach this.
This is basically so the person knows that the ID # they entered matches their name before proceeding to fill the rest out.
views.py
class EnterExitArea(CreateView):
model = EmployeeWorkAreaLog
template_name = "enter_exit_area.html"
form_class = WarehouseForm
def form_valid(self, form):
emp_num = form.cleaned_data['adp_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
if 'enter_area' in self.request.POST:
form.save()
return HttpResponseRedirect(self.request.path_info)
elif 'leave_area' in self.request.POST:
form.save()
EmployeeWorkAreaLog.objects.filter(adp_number=emp_num, work_area=area, station_number=station).update(time_out=datetime.now())
return HttpResponseRedirect(self.request.path_info)
def get_employee_name(request):
adp_number = request.POST.get('adp_number')
employee = Salesman.objects.get(adp_number=adp_number)
employee_name = employee.slsmn_name
return employee_name
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_name = models.CharField(max_length=25)
adp_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False) #(max_length=50, help_text="Employee #", blank=False)
...
def __str__(self):
return self.adp_number
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'adp_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('adp_number').remote_field, site),
}
fields = ('adp_number', 'work_area', 'station_number')
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" data-stations-url="{% url 'operations:ajax_load_stations' %}" novalidate >
{% csrf_token %}
<div>
<div>
{{ form.adp_number.help_text }}
{{ form.adp_number }}
</div>
<div>
{{ form.work_area.help_text }}
{{ form.work_area }}
</div>
<div>
{{ form.station_number.help_text }}
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
<button type="submit" name="leave_area" value="Leave">Leave Area</button>
</div>
</div>
</form>
{% endblock main %}
We'll use ajax, with jQuery so be sure you have jQuery before you read.
first, you've to create an endpoint to GET, go to urls.py & add an endpoint say
path('/myserver/getID/', views.get_employee_name, name="whatever")
now, this calls get_employee_name right? Let's now call it in JS without refreshing.
here's the basic syntax ->
$.ajax({THIS IS A SIMPLE DICT})
ajax takes parameters
type which is the request type
url which is the request URL which we just made above (not the full url, you're specifying the endpoint from where you're located on the website so you just use /myserver/getID/)
it also takes data which is a dictionary with your posted data (yes a dictionary inside the bigger ajax dictionary
it CAN take success which is a function to call after getting the response with status 200 (success) and that success function can have the parameter response which is your response
it CAN take error which is a function that gets called after an error & takes error as argument
enough talking...
$.ajax({
url: 'myserver/getID',
type: 'GET',
data: // don't specify this, we're not posting any data,
success: function (response) {console.log(response.data)}, //this will be what returned from python
error: function (error){console.log(error)}
})
this is a simple ajax request
NOTE, if you return a redirect from python & accept it from ajax, it won't work, ajax can't redirect, be sure to remember that because most of the time people ask why redirect('mylink') doesn't work after I return it from ajax.
Another NOTE is the when dealing with post requests with ajax, you must include the csrf token which can be included by
csrfmiddlewaretoken: '{%csrf_token%}'
You can use Fetch API too if you want, or even normal XMLhttprequest.
Sounds like you have a few questions and should split them up but just to answer the main question in your title, "How to render a field request without refreshing the page?", this is how you do that part with some DOM manipulation. This is basic HTML and JavaScript you would need to fit into your project.
Once you get the name back from your lookup, you just need to insert the value into the DOM and that will render it, not requiring a refresh. Here's a simple example:
var clickMe = function() {
var element = document.getElementById('heading');
// Do your AJAX and lookup something...
element.textContent = "Name Lookup From Server Request";
}
<div>
<h1 id="heading"></h1>
<button onclick="clickMe()">Click Me</button>
</div>
Started learning django about a week ago and ran into a wall. Would really appreciate any enlightenment...
models.py
class data(models.Model):
course = models.CharField(max_length = 250)
def __str__(self):
return self.course
html
Converted the objects in models.course to schlist
<link rel="stylesheet" type="text/css" href="{% static '/chosen/chosen.css' %}" />
<form action={% views.process %} method="GET">
<div>
<h4 style="font-family:verdana;">First Course: </h4>
<select data-placeholder="Course" style="width:350px;" class="chosen-select" tabindex="7">
<option value=""></option>
{% for item in schlist %}
<option> {{ item }} </option>
{% endfor %}
</select>
</div>
</br>
<div>
<h4 style="font-family:verdana;">Second Course:</h4>
<select data-placeholder="Course" style="width:350px;" class="chosen-select" tabindex="7">
<option value=""></option>
{% for item in schlist %}
<option> {{ item }} </option>
{% endfor %}
</select>
</div>
</br>
<input type="submit" value="Compare!" />
</form>
urls.py (having my doubts if this works..)
urlpatterns = [
url(r'^(\d+)/(\d+)$',views.process, name = 'process'),
]
view.py
def process(request,q1 ,q2):
obj1= get_object_or_404(Schdata, course = q1)
obj2= get_object_or_404(Schdata, course = q2)
........
Was wondering if it is possible for the form action to direct the action to
(1) view.py or (2) url.py (and eventually to a view.py) with 2 arguments selected?
If so how should the form action be? {{view ?}} or {{url ?}}. Am I missing out the definition of my arguments in my HTML?
Directing to views.py:
User input is CharField, could use get_object_or_404 to get the model pk. However when defining my urls.py I would get a Noreverse error as my url arguments is the primary key.
Directing to urls.py:
Url arguments is primary key. From the way I see it, I need to magically convert my User input Charfield to a pk before passing it to urls.py
Is there a (or) function for get() in django? E.g get_object_or_404(pk = q1 or course = q1)?
Would really appreciate any advice. Been staring at this for hours.
You are trying to use the reverse resolution of urls in Django.
In your html file correct form action url to the following and method should be POST:
<form action={% url 'process' %} method="POST">
In case you are trying to pass parameters along then use this:
<form action={% url 'process' request.user.id 4 %} method="POST">
Reference:
https://docs.djangoproject.com/en/1.10/topics/http/urls/
Yes i'm late but it can help others for better understanding how Django processes the request.
Django 3.0 pattern
How Django processes the request
Basic :
First Django check the matching URL.
If URL is matched then calling the defined view to process the request. (Success)
If URL not matched/found the Django invokes error Page Not Found
In detail reading :
Official Django Documentations How Django processes a request
These are your URL patterns :
urlpatterns = [ path('profile/edit/<int:pk>/',views.editprofile, name='editprofile'),]
Third argument in urlpatterns is for if you want to change the url pattern from current to this :
urlpatterns = [ url('profile/edit/user/id/<int:pk>',views.editprofile, name = 'editprofile'),]
You don't need to redefine url pattern in all Templates where you using url name.
For Example :
This is my template profile.html where i used the url name instead of hard coded url.
<a class="item" href="{% url 'editprofile' user.id %}" >Edit profile </a>
Solution of your problem :
.html
Only use url name instead of hard coded url in your templates and pass arguments.
<form action={% process no_of_arguments %} method="POST">
views.py
Here you can process your request
def process(request,no_of_arguments):
Become good django developer
You can also use Django ModelForms for your model.
Using model forms or simple form you can do multiple things
Modular approach
Write server side validation in related form instead of doing in views.py
Readable code - Clean code
I am rather new to Django so this may be an easy question. I have 2 modelForms where there is a ForeignKey to another. My main goal is to save Indicators with a link to Disease (FK), such that for a particular disease, you can have multiple indicators.
With the code below, I get an error when I hit submit that says 'ManagementForm data is missing or has been tampered with'. Also, the code in views.py does not seem to be validating at the 3rd 'if' statement where there is a return HttpResponseRedirect. However, when I check my database, the values from the form have been written. Any ideas on why the error has been raised? and how to fix it?
My code is below:
models.py
#Table for Disease
class Disease(models.Model):
disease = models.CharField(max_length=300)
#Tables for Indicators
class Indicator(models.Model):
relevantdisease = models.ForeignKey(Disease)
indicator = models.CharField(max_length=300)
forms.py
class DiseaseForm(forms.ModelForm):
class Meta:
model = Disease
class IndicatorForm(forms.ModelForm):
class Meta:
model = Indicator
DiseaseFormSet = inlineformset_factory(Disease,
Indicator,
can_delete=False,
form=DiseaseForm)
views.py
def drui(request):
if request.method == "POST":
indicatorForm = IndicatorForm(request.POST)
if indicatorForm.is_valid():
new_indicator = indicatorForm.save()
diseaseInlineFormSet = DiseaseFormSet(request.POST, request.FILES, instance=new_indicator)
if diseaseInlineFormSet.is_valid():
diseaseInlineFormset.save()
return HttpResponseRedirect('some_url.html')
else:
indicatorForm = IndicatorForm()
diseaseInlineFormSet = DiseaseFormSet()
return render_to_response("drui.html", {'indicatorForm': indicatorForm, 'diseaseInlineFormSet': diseaseInlineFormSet},context_instance=RequestContext(request))
template.html
<form class="disease_form" action="{% url drui %}" method="post">{% csrf_token %}
{{ indicatorForm.as_table }}
<input type="submit" name="submit" value="Submit" class="button">
</form>
You have neither diseaseFormSet nor diseaseFormSet's management form in your template, yet you try to instantiate the formset. Formsets require the hidden management form which tells django how many forms are in the set.
Insert this into your HTML
{{ diseaseFormSet.as_table }}
{{ diseaseFormSet.management_form }}