I am new to AJAX and Flask. I asked here about how to pass values and show them on the same page without the need to refresh the page and I was told about Ajax. However, after modifying the example given. I am getting a weird error.
Here's my HTML side:
<script type=text/javascript>
$(function() {
$('a#show').bind('click', function() {
$.getJSON('/show', {
a: $('input[name="node"]').val(),
b: $('input[name="parameter"]').val()
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
<form action=""> <!-- method = 'post'-->
<label for="node">Node</label>
<select name="node">
{% for o in data_nodes %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
<label for="parameter">Parameter</label>
<select name="parameter">
{% for o in data_para %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
<!-- Write your comments here -->
<button type="submit" class="btn btn-default" href="javascript:void(0);" id="show" >Send</button>
</form>
My Flask side:
#app.route('/dashboard/', methods=['GET', 'POST'])
def dashboard():
return render_template("dashboard.html", data_nodes = [{'name': 'c9 - Office'}, {'name': 'f20 - Home'}, {'name': 'f21 - School'}],
data_para = [{'name': 'Temperature'}, {'name': 'RSSI'}, {'name': 'LQI'}]) # data_nodes, data_para
#app.route('/show')
def show():
a = request.form.get('node')
b = request.form.get('parameter')
print a
print b
result = a+b
return jsonify(result) # I only intend to print the result on the page here for now.
And here's the response I get. The page refreshes too.
127.0.0.1 - - [24/Apr/2016 23:41:10] "GET /dashboard/?node=f20+-+Home¶meter=RSSI&time=t3 HTTP/1.1" 200
127.0.0.1 - - [24/Apr/2016 23:41:05] "GET /static/js/ie10-viewport-bug-workaround.js HTTP/1.1" 404 -
I tried different variations but I am not exactly sure why the result isn't appearing.
Based on your log file, you are making the following request to your webserver:
127.0.0.1 - - [24/Apr/2016 23:41:10] "GET /dashboard/?node=f20+-+Home¶meter=RSSI&time=t3 HTTP/1.1" 200
This means, the parameters you are looking for are coming in as request parameters, not as form elements. You can reference this post for a more thorough discussion, but I would just change form to args.
#app.route('/show')
def show():
a = request.args.get('node')
b = request.args.get('parameter')
Update:
It looks like there are also errors in your HTML file:
<script type=text/javascript>
$(function() {
$('#myForm').submit(function(ev) {
$.getJSON('/show', {
node: $('input[name="node"]').val(),
parameter: $('input[name="parameter"]').val()
}, function(data) {
$("#result").text(data.result);
});
ev.preventDefault();
});
});
</script>
<form id='myForm' action=""> <!-- method = 'post'-->
<label for="node">Node</label>
<select name="node">
{% for o in data_nodes %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
<label for="parameter">Parameter</label>
<select name="parameter">
{% for o in data_para %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
<!-- Write your comments here -->
<button type="submit" class="btn btn-default" href="javascript:void(0);" id="show" >Send</button>
</form>
Related
I was trying to make a basic banking system using Django, in which a user can transfer money to other user. But when I tried to transfer money nothing happens, probably because transfer function in views.py is not being executed.
Here are transaction.html file and transfer function from views.py :
transaction.html
{% extends 'bank/base.html' %}
{% block content %}
<div class="container">
<h2>Transfer Money</h2>
<form action="{% url 'transaction' %}" method="post">
{% csrf_token %}
<label for="s_acc">Select sender details</label>
<select name="s_acc" required>
<option value="select">Select</option>
{% for cust in customer %}
<option value="{{cust.account_number}}">{{cust.name}}: {{cust.account_number}} : {{cust.balance}}</option>
{% endfor %}
</select>
<br>
<label for="amt">Enter amount</label>
<input type="number" name="amt" required>
<br>
<label for="r_acc">Select receiver details</label>
<select name="r_acc" required>
<option value="select">Select</option>
{% for cust in customer %}
<option value="{{cust.account_number}}">{{cust.name}}: {{cust.account_number}} : {{cust.balance}}</option>
{% endfor %}
</select>
<br>
<button type="submit" name="button">TRANSFER</button>
</form>
</div>
{% endblock %}
transfer function from views.py:
def Transfer(request):
customer = Customer.objects.all();
if request.method=="POST":
s_acc = request.POST.get('s_acc')
amt = request.POST.get('amt')
r_acc = request.POST.get('r_acc')
print(s_acc)
print(amt)
print(r_acc)
amt = int(amt)
if((s_acc=='select')or(amt=='select')or(r_acc=='select')or(s_acc==r_acc)):
messages.warning(request,"Account not selected or both the accounts are same")
elif(amt<=0):
messages.warning(request,"Enter valid amount")
else:
for c in customer:
if(c.account_number == s_acc):
s_name = c.name;
if(amt>c.balance):
messages.warning(request,"Insufficient balance")
break
for x in customer:
if(x.account_number == r_acc):
r_name = x.name
r_bal = x.balance
break;
for c in customer:
if c.account_number == s_acc and r_acc!=s_acc and r_acc!= 'select' and amt<=c.balance and amt>0:
q1 = Transaction(sender_name = s_name, amount = amt, receiver_name = r_name )
q1.save()
acc_bal = c.balance - amt
q2 = Customer.objects.filter(account_number = s_acc).update(balance = acc_bal)
q2.save()
acc_bal = r_bal+amt
q3 = Customer.objects.filter(account_number = r_acc).update(balance = acc_bal)
q3.save()
messages.success(request,"Transfer Complete")
return redirect('tranfer_list')
return render(request,'bank/transaction.html',{'customer':customer})
urls.py
from django.urls import path
from bank import views
urlpatterns = [
path('',views.AboutView.as_view(),name = 'about' ),
path('about/', views.AboutView.as_view(),name = 'about'),
path('customers/', views.CustomerListView.as_view(),name = 'customer_list'),
path('transfer_list/', views.TransactionListView.as_view(), name = 'transfer_list'),
path('transaction/', views.Transfer, name = 'transaction'),
path('customers/new', views.CustomerCreateView, name = 'customer_new'),
]
Thanks in advance :)
I am developing a web application with Flask Python.
In my application, there is a report page that displays a table of results with Flask pagination.
At the bottom of my table, I added a "select options" field called "number_result_per_page" to let the user choose the "per_page" parameters (number of items per page).
I made a javascript that reloads the page with the "per_page" parameters updated according to this user's choice.
PROBLEM :
My problem is if the user is almost on the last page, and he decides to display "100" results per page, and there are less than 100 results to display, I get a 404 error.
I am sure I am not the only one who faced this kind of issue. Is there any solution to avoid this scenario?
Or can I catch this particular error 404 to display an "error" popup and reload the previous page which was correct?
Here is sample of my code:
# Route
#app.route('/task_user_report/<int:task_user_id>/view', methods=['GET', 'POST'])
#login_required
def task_user_report(task_user_id):
args = request.args
per_page_task_report = args.get("per_page_task_report", 1, type=int)
actions = db_mysql.session.query(Action).filter_by(id_task_user=task_user_id) \
.filter(Action.id_contact==Contact.id) \
.paginate(page=page_task_report, per_page=per_page_task_report)
render_template(render_html,title='Task Report',legend='Task Report',actions=actions, \
per_page_task_report=per_page_task_report)
# Template
<div class="row justify-content-center" style="width: 95%;">
<div class="col-7 my-auto text-right">
<div class="text-center mt-4" style="display: inline-block;">
<nav aria-label="Pagination">
<ul class="pagination justify-content-center">
{% for page_num in actions.iter_pages(left_edge=1,right_edge=1,left_current=1, right_current=2) %}
{% if page_num %}
{% if actions.page == page_num %}
<li class="page-item active"><a class="page-link" href="{{ url_for('task_user_report',task_user_id=task_user_id,page_task_report=page_num,per_page_task_report=per_page_task_report) }}">{{ page_num }}</a>
{% else %}
<li class="page-item"><a class="page-link" href="{{ url_for('task_user_report',task_user_id=task_user_id,page_task_report=page_num,per_page_task_report=per_page_task_report) }}">{{ page_num }}</a>
{% endif %}
{% else %}
<li> ... </li>
{% endif %}
{% endfor %}
</ul>
</nav>
</div>
</div>
</div>
<div class="col-5 my-auto text-right">
<select style="width:180px!important;display: inline-block;" id="number_result_per_page" class="form-control form-control-sm w-25 text-right" onchange="change_per_page()">
<option value="10"
{% if per_page_task_report== 10 %}
selected
{% endif %}
>10 results per page</option>
<option value="25"
{% if per_page_task_report== 25 %}
selected
{% endif %}
>25 results per page</option>
<option value="50"
{% if per_page_task_report== 50 %}
selected
{% endif %}
>50 results per page</option>
<option value="100"
{% if per_page_task_report== 100 %}
selected
{% endif %}
>100 results per page</option>
</select></div>
</div>
# Javascript
<script>
function change_per_page() {
var url = window.location.href;
// we get value of per page option
var per_page_option = document.getElementById("number_result_per_page");
var per_page_option_value = per_page_option.value;
// We check if parameter exist in url
var is_per_page = url.includes("per_page_task_report");
if (is_per_page == true){
var queryParams = new URLSearchParams(window.location.search);
// new value of "page_task_report" is set to "per_page_option_value"
queryParams.set('per_page_task_report', String(per_page_option_value));
// change the search property of the main url
history.pushState(null, null, "?"+queryParams.toString());
var new_url = window.location.href;
}
else{
// There are 2 cases :
// [1] url doesn't have any parameters (ex:http://127.0.0.1:5000/task_user_report/2/view)
// [2] url has a parameter (ex:http://127.0.0.1:5000/task_user_report/2/view?page_task_report=2)
var is_page_task_report = url.includes("page_task_report");
if (is_page_task_report == true){
var new_url = url + '&per_page_task_report=' + per_page_option_value;
}
else{
var new_url = url + '?per_page_task_report=' + per_page_option_value;
}
}
window.location.replace(new_url);
}
</script>
In your paginate() call, set error_out=False like this, paginate(..., error_out=False)
The default is True. This behavior is not always desired though. If you are using Flask as an API backend, this can sometimes cause errors. It's up to you how you want to handle it by either providing an empty result set or by defaulting the page back to one when no results are returned.
After select2 manipulates the dropdown field the regular use of form.owner_id.data yields None. How can I extract the selected option from a select2 field.
If I disable the select2 javascript, wtforms will work just fine and I will be able to use form.owner_id.data. (but it looks ugly)
screenshot of rendered form
forms.py
class ProjectForm(FlaskForm):
owner_id = SelectField('Owner:', [validators.Required()], choices=[], render_kw={"placeholder": "Owner company *"})
(rest of form has been truncated for simplicity)
views.py
#app.route('/new_project/<int:company_id>', methods=('GET', 'POST'))
def new_project(company_id):
membership = Membership.query.filter_by(user_id=current_user.id, company_id=company_id).first()
company = Company.query.filter_by(id=company_id).first_or_404()
form = ProjectForm()
form.owner_id.choices = [(str(comp.id), repr(comp)) for comp in Company.query.all()]
form.client_id.choices = [(str(comp.id), repr(comp)) for comp in Company.query.all()]
form.contractor_id.choices = [(str(comp.id), repr(comp)) for comp in Company.query.all()]
form.membership_id.choices = [(str(comp.id), repr(comp)) for comp in Company.query.all()]
if request.method == 'POST':
flash(str(form.owner_id.data), 'success')
if form.validate_on_submit():
project = Project()
connection = Assignment()
# PROJECT DETAILS
project.title = form.title.data
project.description = form.description.data
project.owner_id = int(form.owner_id.data)
_macro
{% macro render_select_field(field, placeholder='Select...', label='Select an option below') %}
<div class="form-group">
<label>{{ label }}</label>
<select data-placeholder="{{ placeholder }}" class="select-size-xs">
<option></option>
{% for choice in field.choices %}
<option value="{{ choice[0] }}">{{ choice[1] }}</option>
{% endfor %}
</select>
{% if field.errors %}
{% for error in field.errors %}
<span class="help-block text-danger"><i class="icon-cancel-circle2 position-left"></i>{{ error }}</span>
{% endfor %}
{% endif %}
</div>
{% endmacro %}
html
<form method="POST" action="{{ url_for('new_project', company_id=company.id) }}" enctype="multipart/form-data" role="form">
<div class="panel panel-body login-form">
<div class="text-center">
<div class="icon-object text-muted"><i class="icon-plus2"></i></div>
</div>
{{ form.hidden_tag() }}
<div class="text-center form-group"><span>Project details</span></div>
{{ new_render_field(form.title, icon="icon-quill2", class_='form-control') }}
{{ new_render_field(form.description, icon="icon-stack-text", class_='form-control', rows=10) }}
{{ render_select_field(form.owner_id, placeholder="Who's doing the work?", label="Select the owner company") }}
{{ render_select_field(form.client_id, placeholder="Where do the bills go?", label="Select the client company") }}
{{ new_render_field(form.default_client_rate, icon="icon-price-tag", class_='form-control') }}
{{ new_render_field(form.default_contractor_rate, icon="icon-price-tag", class_='form-control') }}
<!-- THE REST OF FORM HAS BEEN TRUNCATED FOR SIMPLICITY -->
<div class="form-group">
<button type="submit" class="btn bg-pink-400 btn-block">Create</button>
</div>
</div>
</form>
So after analyzing the select field in the inspect window, it became apparent that the select field is missing the name="{{ field.name }}" that wtforms requires in order to validate the form. The simple change made in my _macro was from this:
<select data-placeholder="{{ placeholder }}" class="select-size-xs">
To this:
<select data-placeholder="{{ placeholder }}" class="select-size-xs" name="{{ field.name }}">
With this addition wtforms can now validate, and find the selected option returning the proper id for form.owner_id.data.
Sorry for the long title, but this is what happens:
I submit word "hello", then hello gets displayed. Then in a form, it still says "hello". I'm not sure why this is happening. Is this django problem or some javascript code is needed for this...?Here's my full code I think is causing this problem.
in my views.py
def post(request, slug):
user = get_object_or_404(User,username__iexact=request.user)
try:
profile = MyProfile.objects.get(user_id=request.user.id)
# if it's a OneToOne field, you can do:
# profile = request.user.myprofile
except MyProfile.DoesNotExist:
profile = None
post = get_object_or_404(Post, slug=slug)
post.views += 1 # increment the number of views
post.save() # and save it
path = request.get_full_path()
comments = Comment.objects.filter(path=path)
#comments = post.comment_set.all()
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
parent_id = request.POST.get('parent_id')
parent_comment = None
if parent_id is not None:
try:
parent_comment = Comment.objects.get(id=parent_id)
except:
parent_comment = None
comment_text = comment_form.cleaned_data['comment']
new_comment = Comment.objects.create_comment(
user=MyProfile.objects.get(user=request.user),
path=request.get_full_path(),
text=comment_text,
post = post,
parent = parent_comment)
for c in comments:
c.get_children()
context_dict = {
'post' :post,
'profile' :profile,
'comments' : comments,
'comment_form':comment_form
}
return render(request, 'main/post.html', context_dict)
and in my post.html I have
<table class='table'>
{% for comment in comments %}
<tr><td>{{ comment.get_comment }}
<br/><small>via {{ comment.user }} | {{ comment.timestamp|timesince }} ago </small>
{% if not comment.is_child %}
<ul>
{% for child in comment.get_children %}
<li>{{ child.get_comment }}
<small>via {{ child.user }}</small>
</li>
{% endfor %}
</ul>
<a href='#' class='reply_btn'>Reply</a>
<div class='reply_comment'>
<form method="POST" action=''>{% csrf_token %}
<input type='hidden' name='parent_id' value='{{ comment.id }}' />
{{ comment_form.as_p }}
<input type='submit' class='btn btn-default' value='Add reply'/>
</form>
</div>
{% endif %}
</td></tr>
{% endfor %}
</table>
</div>
<div class = "col-sm-3">
</div>
{% include 'footer.html' %}
<script>
{% block jquery %}
$('.reply_btn').click(function(e){
e.preventDefault();
$(this).next(".reply_comment").fadeToggle();
// $(".reply_comment").fadeToggle();
})
{% endblock %}
</script>
{% endblock %}
Can someone please direct me where I should look.....thank you
Before you check if a form is valid or not you assign the form to include the POST data so it will still include this when you return back to the form (in case it needs to show errors). The easiest fix would be to reassign the form after your valid logic is done.
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
.. Valid logic ..
comment_form = CommentForm()
I have my main layout named layout.html.twig. In this layout I made the block content for extends him, see the code:
<!DOCTYPE html>
<html>
<!-- my html code, meta header, body ... etc -->
<body>
<div class="col-md-12 panel panel-default" id="content">
{% block content %}
{# extend content to the principal layout #}
{% endblock %}
</div>
</body>
</html>
In fact, each time I woul like to load another page content in my {% block content %}, I extends the layout like this for example in indexEquipement.html.twig:
{% extends "MyBundleBundle::layout.html.twig" %}
{% block content %}
<div class="page-header">
<h4>add an equipement</h4>
</div
<select id="selectEquipement">
<option selected disabled>choose an equipement</option>
<option value="{{ path('addEquipement1') }}">Equipement 1</option>
<option value="{{ path('addEquipement2') }}">Equipement 2</option>
</select>
{% endblock %}
This is the route file for the index:
indexEquipements:
path: /manageEquipements
defaults: { _controller: MySpaceMyBundle:ManageEquipements:indexEquipements }
requirements:
methods: GET
As you can see, in this example, I have a select tag with url for the values option. I try to render the view of my select choice with ajax without reloading the browser page.
Each url for the option values are in fact a view with a different form. I choose an option in my select, and ajax return in the same page the form matching with my choice.
See my controller for the Equipement 1 addAction():
/**
*
* #Route("/manageEquipement/addEquipement1", name="addEquipement1")
* #Method("get")
*/
public function addEquipements1Action() {
$eq = new Equipement1;
$form = $this->createForm(new Equipement1Type(), $eq);
$request = $this->getRequest();
if ($request->isMethod('POST') | ($form->isValid())) {
$form->bind($request);
$em->persist($eq);
$em->flush();
return $this->redirect($this->generateUrl('indexEquipements'));
} else {
return $this->render('MySpaceMyBundle:MyFolder:addEquipement1.html.twig', array('form' => $form->createView() ));
}
}
And now the route file for this action:
addEquipement1:
path: /manageEquipement/addEquipement1
defaults: { _controller: MySpaceMyBundle:ManageEquipements:addEquipement1 }
requirements:
methods: GET
addEquipement1_process:
path: /manageEquipement/addEquipement1/process
defaults: { _controller: MySpaceMyBundle:ManageEquipements:addEquipement1 }
requirements:
methods: POST
How can I render the form for addEquipement1Action() in ajax?
## EDIT ##
This the controller action for addEquipement1Action():
/**
*
* #Route("/manageEquipement/addEquipement1", name="addEquipement1")
* #Method("get")
*/
public function addEquipement1Action() {
$request = $this->getRequest();
$eq = new Equipement1;
$form = $this->createForm(new Equipement1Type(), $eq);
if($request->isXmlHttpRequest()){
$template = $this->forward('MySpaceMyBundle:MyFolder:addEquipement1.html.twig', array('form' => $form->createView() ))->getContent();
$json = json_encode($template);
$response = new Response($json, 200);
$response->headers->set('Content-Type', 'application/json');
if ($request->isMethod('POST') | ($form->isValid())) {
$form->bind($request);
$em->persist($eq);
$em->flush();
return new Symfony\Component\HttpFoundation\RedirectResponse($this->generateUrl('indexEquipements'));
} else {
return $response;
}
}
}
This is the index of equipement:
{% extends "MyBundleBundle::layout.html.twig" %}
{% block content %}
<div class="page-header">
<h4>add an equipement</h4>
</div
<select id="selectEquipement">
<option selected disabled>choose an equipement</option>
<option value="{{ path('addEquipement1') }}">Equipement 1</option>
<option value="{{ path('addEquipement2') }}">Equipement 2</option>
</select>
<div id="formEquipement"></div>
<script type="text/javascript">
$('#selectEquipement').change(function(event) {
$.ajax({
type: "GET",
url: "{{ path('addEquipement1') }}",
dataType: "html",
success: function(data){
$("formEquipement").append(data);
}
});
});
</script>
{% endblock %}
When I apply a choice in my select tag, I can see when I debug in my browser that the ajax call works, but it returns me this error concerning my view I would like to display:
The controller must return a response (null given). Did you forget to
add a return statement somewhere in your controller?
And this is my twig view for addEquiment1.html.twig:
<form action="{{ path('addEquipement1_process') }}" method="POST" {{ form_enctype(form) }}>
<div class="col-md-5">
<br>
<div>
{{ form_errors(form) }}
</div>
<br>
<div>
{{ form_widget(form) }}
</div>
<br>
<div>
{{ form_rest(form) }}
</div>
<br>
<div>
<input type="submit" value="Add" class="btn btn-small btn-success"/>
</div>
</div>
</form>
How can I render this view properly in my actually page without refreshing the browser with Ajax?
How can I render the form for addEquipement1Action() in ajax?
you could put your form in a div container and parse it out via JS in the client
or you separate twig views. include the form yourform.html.twig in the block of indexEquipement.html.twig and add an if statement in your controller that if a parameter onlyForm is set, then render yourform.html.twig else indexEquipement.html.twig. You can of course also use different routes and controler action to drop the if statement, which might would be nicer to read.