how to get data from database to modal form? - python

I am using Flask, Jinja2, Python and SQLAchemy.
I want to retrieve data from my database and display it on my modal.
This is my html code:
{% extends "layout.html" %}
{% block title %}
Admin
{% endblock %}
{% block body %}
<h1> Admin</h1>
<div class="form-group">
<table border=1>
<tr>
<th>usa_order_id</th>
<th>user_id</th>
<th>product_name</th>
<th>cpu</th>
</tr>
{% for usa_order in usa_orders %}
<tr>
<th>{{usa_order.usa_order_id}}</th>
<th>{{usa_order.user_id}}</th>
<th>{{usa_order.product_name}}</th>
<th>{{usa_order.cpu}}</th>
<th>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal">
Open modal
</button>
<!-- The Modal -->
<div class="modal" id="myModal">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" >×</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<div class="form-group">
<input class="form-control" name ="usa_order_id" value={{usa_order.usa_order_id}}>
<input class="form-control" name ="user_id" value={{usa_order.user_id}}>
<input class="form-control" name ="cpu" value={{usa_order.cpu}}>
</div>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</th>
</tr>
{% endfor %}
</table>
</div>
{% endblock %}
When I click to open modal, I want it to display the data from my database. I tried some other quick solutions but none of them seem to applicable in Python.
Question is: How can I retrieve data from my db table and display it on the modal - which generated in a for loop?
here is my python, just show all the db
from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
from model import *
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI']='mysql://root:xxx#localhost:3306/xxx'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']=True
db = SQLAlchemy(app)
#app.route("/admin")
def index():
usa_order = usa_order.query.all()
return render_template("admin.html",usa_order = usa_order)

Since you didn't provide snippets belong to your routes.py, I will have to assume that you are passing the usa_orders data to template without a mistake.
If you want to generate a modal for each usa_order in usa_orders then you would need to create unique modals for each order. Otherwise, you will end up displaying the same modal for many different users/orders. To achieve this, you simply need to provide a unique id for modal, by editing following line:
<!-- The Modal -->
<div class="modal" id="myModal">
To something like:
<!-- The Modal -->
<div class="modal" id={{usa_order.usa_order_id}}>
Or any other unique non-repeating value - preferably from your db.
Let me know if this helps, and if it doesn't work for you: please provide more information on what kind of error(s) you are getting.

Related

pass a value from bootstrap to django modal

Here i am trying to pass the value outside for loop modal using ajax
here is my template.html
{% for compliance in compliance %}
{% complience_category compliance request.user as compliances %}
{% for qualification in compliances %}
.....
.....
<td>
<button data-id="{{ qualification.id }}" type="button" class="btn btn-warning margin-bottom edit-qualification">
edit
</button>
</td>
....
.....
{% endfor %}
{% csrf_token %}
<div class="modal hid fade" id="modal-default">
<form class="form-horizontal" method="POST" enctype="multipart/form-data" action=" {% url 'update_qualifications' qualification.id %}">
{% csrf_token %}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>Update Compliance</h3>
</div>
<div class="modal-body">
<div class="control-group">
<label class="control-label" for="inputdate_{{qualification.id}}">Expiry Date</label>
<div class="controls">
<input type="date" id="inputdate_{{qualification.id}}" name="expiry_date" value="{{qualification.expiry_date|date:'Y-m-d'}}">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal">Close</button>
<button class="btn btn-primary" type="submit">Save</button>
</div>
</form>
</div>
{% endfor %}
This is my AJAX
$(document).on('click','.edit-qualification',function(){
var id = $(this).data('id');
$.ajax({
url:'',
type:'POST',
data:{
'id':id,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
},
success:function(data){
$('#edit_modal .modal-dialog').html($('#edit_modal .modal-dialog',data));
$('#edit_modal').modal('show');
},
error:function(){
console.log('error')
},
});
});
</script>
Here my modal is outside of for loop
Here my problem is when i click the edit button my modal popup is not working (it means when clicking the edit button nothing works for me )
Here my modal is outside of for loop
From the looks of this, the modal is still inside of a for loop.
{% for compliance in compliance %}
...
...
# your modal is here...
{% endfor %}
I'd suggest placing the modal outside of the loop.
Another thing to note is that using Ajax here is really unnecessary unless I'm not understanding what's your purpose for using it.
However, for the edit button, I'd suggest that you add two attributes; data-toggle and data-target. Example:
<button data-toggle="modal" data-target="#myModal">
edit
</button>
In your case, you have for the modal div an id: id="modal-default". This should be assigned to your button's data-target attribute. data-target="#modal-default".
So what you should have instead for the edit button is:
<button data-toggle="modal" data-target="#modal-default" data-id="{{ qualification.id }}" type="button" class="btn btn-warning margin-bottom edit-qualification">
edit
</button>
Ideally, that should work to display the modal.
As for the question of how to pass the values to the modal if a specific edit button is clicked, then I'd suggest you see this answer here as it might be very useful.

should django modal have a separate view?

I am working on an application that has a page with a table that is a list of users. Each user has a button which pops up a modal that does something. I am trying to create the modal that will get the user information when the button in clicked, additionally, I need in that modal some logic based on the user information and some more data. I am thinking that it makes sense to create a separate view for it but since each view must be on a different url, the concept of the modal does not makes sense (for me), because I want it to be on the same url.
Is it possible to have a modal with a different view that will have a separate logic? Since the modal is included in the template and it is basically just one template, then, can one template have 2 views?
What is the best way to implement the modal using django? And how to pass data to it, I need the data from the row that was selected + some more data from db. I have the data from the context but manipulating that using JQuery for me is a bad idea - because I am using a python session on the backend that communicates with another api, so when user clickes the modal save button, a python request needs to be done, this means I need a view for it.
Here is my try
Modal
{% block modal %}
{% load static %}
{% load authorization_tags %}
<!-- Modal -->
<div class="modal fade" id="{{modal_id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="row">
<div class="col-lg-5">
<img class="d-block w-100"
src="{% static 'app/assets/images/security-shield1.png' %}"
alt="First slide">
Use access control to deauthorize user
</div>
<div class="col-lg-7">
<h2 class="h2-responsive product-name">
<strong>{{user.real_name}}</strong>
</h2>
<h4 class="h4-responsive">
<span class="green-text">
{{user.username}}
</span>
</span>
</h4>
<div class="accordion md-accordion" id="accordion1" role="tablist" aria-multiselectable="true">
<div class="card">
<div class="card-header" role="tab" id="heading2">
<a data-toggle="collapse" data-parent="#accordion1" href="#collapse1" aria-expanded="true"
aria-controls="collapse1">
<h5 class="mb-0">
Current Authorizations <i class="fas fa-angle-down rotate-icon"></i>
</h5>
</a>
</div>
<div id="collapse1" class="collapse show" role="tabpanel" aria-labelledby="heading1"
data-parent="#accordion1">
<div class="card-body">
{% for right in people_rights|user_active_rights:user.id %}
{{right.room}}
{% endfor %}
</div>
</div>
</div>
</div>
<div class="accordion md-accordion" id="accordion2" role="tablist" aria-multiselectable="true">
<div class="card">
<div class="card-header" role="tab" id="heading2">
<a data-toggle="collapse" data-parent="#accordion2" href="#collapse2" aria-expanded="true"
aria-controls="collapse2">
<h5 class="mb-0">
Available Authorizations <i class="fas fa-angle-down rotate-icon"></i>
</h5>
</a>
</div>
<div id="collapse2" class="collapse show" role="tabpanel" aria-labelledby="heading2"
data-parent="#accordion2">
<div class="card-body">
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="text-center">
<button type="button" class="btn btn-lg" data-dismiss="modal"><span>Close</span></button>
<button class="btn btn-lg"><span>Authorize</span></button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock modal %}
a piece of table and the modal included in the same template
{% include 'app/admin/users/user_authorize.html' with modal_id="authModal" user=row areas=areas rooms=rooms people_rights=people_rights %}
{%if row.empno %}
<td class="txt-oflo">{{ row.empno }}</td>
{% else %}
<td class="txt-oflo">-</td>
{% endif %}
<td>
{% with "#"|add:row.id as buttonId %}
<button id={{buttonId}} class="btn btn-lg" data-toggle="modal" data-target="#authModal">
<span>Authorize</span>
</button>
{% endwith %}
</td>
</tr>
You can try the following steps to solve the issue.
Create a separate view for the modal on a separate url.
Take the modal data out of this html file and place it in another html file. The empty modal in the original html should look something like this.
<div id="SampleTable" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">sample title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" id="sample_datatable">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
Render that new html from the new view that was created. The ajax request that will be activated from the button that gives us the modal table might look something like this.
#app.route('/sample_data', methods=['GET', 'POST'])
def modal_table():
try:
data = request.args.get('arg')
database_data = Table.query.filter(Table.data == data).all()
result_dict = [u.__dict__ for u in database_data]
except Exception as e:
traceback.print_exc()
return render_template('modal_table.html', data=enumerate(result_dict))
The button in the original html that results in the modal table should be configured in a way using ajax requests that it renders data from the new html into the old modal data like this.
function get_sample_data() {
$.ajax({
url:"/sample_data", //the page containing python script
type: "GET", //request type,
data: {},
async:true,
beforeSend: function(){
$('#sample_datatable').html('Loading..');
},
success:function(result){
$('#sample_datatable').html(result);
}
});
}
You can call this javascript function from the button that is supposed to show the modal table.

How can I group two same products to show them as one in django?

*Now as u see in the database table. In this table, we have two oranges but have different prices because pack size. From this table, I want to get data in this way that on my project product view list oranges shows only once and if we go to the product detail we have a combo box where we can display the different size of pack from the table like oranges. Oranges should we shown once from this table to the product list by grouping them and after going to oranges details we can see both the oranges packaging in a combo box. *
You can have a model Products that will have a product name field and ProductDetails model that will reference Product model to get the name of the product, the ProductDetails mode will have description, price and other information about the product.
Lets hope this will help.
Happy coding
For this, we can use the Django HTML tag "regroup" as shown in the code below, and u also have to change the query in the view.py and just order it by productID. For the given table in the image, I will post the code below.
HTML
{% regroup transaction by productID as ProductList %}
{% for productID in ProductList %}
<div class="col-sm-3" id="productID" >
<div class="product">
<a href="{% url 'main:product-detail' productID.grouper.id %}" class="img-prod"><img class="img-fluid" src={{productID.grouper.product_image.url}} alt="" height="200px">
<span class="status">%</span>
<div class="overlay"></div>
</a>
<div class="text py-3 pb-4 px-3 text-center">
<h3>{{productID.grouper}}</h3>
<div class="d-flex">
<div class="pricing">
<p class="price"><span class="mr-2 price-dc" id='id_price'>Rs. </span><span class="price-sale" id='id_sale_price'>Rs. </span></p>
</div>
</div>
<select class="tranactionID" style="width: 250px;">
<!-- <option value="none" selected disabled hidden>
Select an Option
</option> -->
{% for val in productID.list %}
<option price={{val.Price}} sale_price={{val.get_sale}} class="price_value" >{{val.AUID}} - {{val.Description}}</option>
{% endfor %}
</select>
<!-- <div class="bottom-area d-flex px-3">
<div class="m-auto d-flex"> -->
<form action="{% url 'cart:cart_add' productID.grouper.id %}" class="d-inline" method="post">
<input type="submit" class="btn btn-primary shadow px-5 py-2" value="Add To Cart">
{{cart_product_form}}
{% csrf_token %}
<!-- <button type="submit" class="btn btn-primary shadow px-5 py-2">Add to Cart</button> -->
</form>
<!-- </div> -->
<!-- </div> -->
</div>
</div>
</div>
{% endfor %}
views.py
transaction = transactions.objects.filter(status='Enable').order_by('productID')

Django form validation on clicking Next instead of Submit

I have this HTML page in which I have 2 forms. Django is not checking the form validation for the first form. I want it to check form validation for the first form.
This is the Html Code
<form method="post" id="msform">
{% csrf_token %}
<!-- progressbar -->
<ul id="progressbar">
<li class="active" id="conf"><strong>Configuration</strong></li>
<li id="auth"><strong>Authentication</strong></li>
</ul>
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuemin="0" aria-valuemax="100"></div>
</div> <br> <!-- fieldsets -->
<fieldset>
<div class="form-card">
<div class="row">
<div class="col-7">
<h2 class="fs-title">Cluster Configuration</h2>
</div>
<div class="col-5">
<h2 class="steps">Step 1 - 2</h2>
</div>
</div>
{{ form1|crispy }}
</div> <input type="button" name="next" class="next action-button" value="Next" />
</fieldset>
<fieldset>
<div class="form-card">
<div class="row">
<div class="col-7">
<h2 class="fs-title">Authentication</h2>
</div>
<div class="col-5">
<h2 class="steps">Step 2 - 2</h2>
</div>
</div>
{{ form2|crispy }}
</div><button type="submit" class="btn btn-primary" >Next</button>
</fieldset>
</form>
If I use submit button instead of next for the first form then neither the second form opens nor the form is submitted (i.e. nothing is happening).
So I think the issue might be because you have over arching form tag, and two forms sent inside it from your view function:
What you have right now is this:
<form>
<fieldset>
<! –– Your Code ––>
{{ form1|crispy }}
</div> <input type="button" name="next" class="next action-button" value="Next" />
</fieldset>
<fieldset>
<! –– Your Code ––>
{{ form2|crispy }}
</div><button type="submit" class="btn btn-primary" >Next</button>
</fieldset>
</form>
As you can see from the above simplification of your original post, you have two forms templated inside one form tag. Thus when you hit either of the the submit button both of them gets sent.
If I am not mistaken, I believe what you might want is something along the lines of this:
<form name="form1" id="form1">
<fieldset>
<! –– Your Code ––>
{{ form1|crispy }}
</div> <input type="button" name="form1" class="next action-button" value="Next1" />
</fieldset>
</form>
<form name="form2" id="form2">
<fieldset>
<! –– Your Code ––>
{{ form1|crispy }}
</div> <input type="button" name="form2" class="next action-button" value="Next2" />
</fieldset>
</form>
Take note of the fact that I have changed your button names, as well as added ids and names to each form, you might also want to add ids as well as unique values to your buttons as well. This would help you build if conditions to determine which form has been submitted and so on, in your view function
Here is a link that describe how to handle this in more depth.
Which is all nice and dandy, but the conclusion of that linked page is this:
Let me know if it helps, feel free to ask more if you do not understand/the changes does not work

WTForms when rendering two forms on the same page with a Recaptcha fields only one is displayed

I am using Flask with WTforms. I am also using the WTFRecaptcha plugin in order to use Captcha fields.
Turns out I need to use two forms on the same page. When I assign a captcha field on each form, one of the captchas is not rendered on the .html page. This is because the captcha is ALWAYS created with the same ID:
Captcha and forms declaration on my forms.py file:
from wtforms import PasswordField, StringField, validators, widgets, RadioField
from wtforms.form import Form
from wtfrecaptcha.fields import RecaptchaField
class FirstForm(Form):
"""First Form"""
#Omitting fields here
captcha_1 = RecaptchaField('Captcha', [], public_key='OMITTING_PUBLIC_KEY', private_key='OMITTING_PRIVATE_KEY', secure=True)
class Secondform(Form):
"""Second Form"""
#Omitting fields here
captcha_2 = RecaptchaField('Captcha', [], public_key='OMITTING_PUBLIC_KEY', private_key='OMITTING_PRIVATE_KEY', secure=True)
Route declaration:
#!/usr/bin/env python
from flask import Flask, render_template, request
from flask.ext.assets import Environment
from forms import FirstForm, SecondForm
from flask import request
from flask import jsonify
#app.route('/test')
def test_form():
"""Test."""
form_1 = FirstForm(request.form, captcha_1={'ip_address': request.remote_addr})
form_2 = SecondForm(request.form, captcha_2={'ip_address': request.remote_addr})
if request.method == 'POST' and (form_1.validate() or form_2.validate()) :
return "Instructions have been sent to your e-mail"
return render_template(
'test-form.html',
title='Get Started',
form_1=form_1,
form_2=form_2
)
test-form.html
{% extends "base.html" %}
{% block content %}
<div class="container block-form">
<div class="row first">
<div class="col-xs-12 col-md-7 border-right">
<h1 class="title">{{ title }}</h1>
<p>{{ description }}</p>
<div class="form-area">
<form method="post">
{% for field in form_1 %}
<div class="form-group{% if field.errors %} has-error has-feedback{% endif %}">
<div class="row">
<div class="col-xs-12 col-md-4">
{{ field.label(class="control-label") }}
</div>
<div class="col-xs-12 col-md-8">
{{ field(class="form-control") | safe }}
</div>
</div>
{% if field.errors %}
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
{% endif %}
{% for error in field.errors %}
<p class="help-block text-danger">
<span class="glyphicon glyphicon-remove"></span>
{{ error }}
</p>
{% endfor %}
</div>
{% endfor %}
<br>
<button type="submit" class="btn btn-gradient">Submit</button>
</form>
</div>
</div>
</div>
<div class="row second">
<div class="col-xs-12 col-md-7 border-right">
<h1 class="title">{{ title }}</h1>
<p>{{ description }}</p>
<div class="form-area">
<form method="post">
{% for field in form_2 %}
<div class="form-group{% if field.errors %} has-error has-feedback{% endif %}">
<div class="row">
<div class="col-xs-12 col-md-4">
{{ field.label(class="control-label") }}
</div>
<div class="col-xs-12 col-md-8">
{{ field(class="form-control") | safe }}
</div>
</div>
{% if field.errors %}
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
{% endif %}
{% for error in field.errors %}
<p class="help-block text-danger">
<span class="glyphicon glyphicon-remove"></span>
{{ error }}
</p>
{% endfor %}
</div>
{% endfor %}
<br>
<button type="submit" class="btn btn-gradient">Submit</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
Code rendered for captcha in form_1 (Up to the div element):
<script src="https://www.google.com/recaptcha/api/challenge?k=6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp" type="text/javascript">
//Other code here omitted
<script src="https://www.google.com/recaptcha/api/js/recaptcha.js" type="text/javascript">
//Other code here omitted
<div id="recaptcha_widget_div" class=" recaptcha_nothad_incorrect_sol recaptcha_isnot_showing_audio">
Code rendered for captcha in form_2 (Up to the div element):
<script type="text/javascript" src="https://www.google.com/recaptcha/api/challenge?k=6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp">
<script type="text/javascript" src="https://www.google.com/recaptcha/api/js/recaptcha.js"/>
<div id="recaptcha_widget_div" style="display: none;"/>
<noscript><iframe src="https://www.google.com/recaptcha/api/noscript?k=6LeCJvUSAAAAAAvqwJEueVdV0wyNLPtX6KWSTdXp" height="300" width="500" frameborder="0"></iframe>
<br> <textarea name="recaptcha_challenge_field" rows="3" cols="40"> </textarea> <input type="hidden" name="recaptcha_response_field" value="manual_challenge"></noscript>
RESULT: Only one captcha is shown.
... Therefore if I have two captcha fields (Possible on two different forms), one won't display.
Any solutions/suggestions?
This is well a well documented limitation of Recaptcha
Currently, the Google captcha mechanism offer only one captcha form per page
I would encourage you to rethink the way you are organizing your page. Forms in HTML are simple by design. Most of the tooling built around them assumes that a page does one thing and submits the result to the server in a single form submission.
Disclaimer: I don't really know anything about your code. Proceeding regardless: it smells like your design might be a too clever. What I mean by this is that if you haven't seen it done somewhere else and google's tooling doesn't support it the issue is probably with your approach.
If you need to commit the result of a single stateless transaction then a <form> is appropriate and WTForms is a great tool to generate it. If you need something richer you might consider the following:
Break your forms out into multiple pages. A simple set of hyperlinks can provide an easily navigable hierarchy.
Build your DOM with javascript and submit to a RESTful endpoint(you can even use WTForms for validation by converting the request body into a MultiDict and Recaptcha supports AJAX)
Build your <form> dynamically with javascript and switch the action to correspond to the correct form processor on your server.
This is not possible with reCAPTCHA.
See the related ASP.NET question: Multiple reCAPTCHAs in one ASP.Net page
And for possible workarounds: How do I show multiple recaptchas on a single page?

Categories

Resources