I have Django model that contains data ("Name" and "Status") for 10 people. I display this model in a DataTable with checkboxes and if I check a box it changes the Class into selected, e.g. <tr role="row" class="odd selected"
The user makes a selection from the DataTable and press a button ("Send selection by email"). The selection will go into a Django View. I will write code that filter out those contains a selected, obtain its PK and send an email with info.
However, I have problem in the first step, to get the DataTable object into the view.
A similar approach would have been to use a Multiselect, but here I want the look of a table and not a form box.
My attempted solution is to put form tags around the DataTable and then read the table to filter out all that are selected.
Problem I currently have is that I cannot get the DataTable into the views.py.
So my question is: How do I access the table in Django View?
I have tried both POST and GET within below, but request object returns as None
$('#datatable').DataTable({
responsive: true,
columnDefs: [{
orderable: false,
className: 'select-checkbox',
targets: 0
}],
select: {
style: 'multi',
selector: 'td:first-child',
// selectRow: true
},
order: [
[1, 'asc']
],
language: {
searchPlaceholder: 'Search...',
sSearch: '',
lengthMenu: '_MENU_ items/page',
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/select/1.3.1/js/dataTables.select.min.js"></script>
</head>
<body>
<div class="container">
<form method="post" action="{% url 'product-send2' %}">
{% csrf_token %}
<div class="table-wrapper">
<table class="form-control table display responsive" id='datatable' style="width:100%;">
<thead>
<tr>
<th>Send</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for object in object_list %}
<tr>
<td></td>
<td>{{ object.name }}</td>
<td>{{ object.status }}</td>
<tr>
{% endfor %}
</tbody>
<tfoot>
</tfoot>
</table>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>
In the Urls
path('product/send/', send_product2, name='product-send2')
and in the views
def send_product2(request):
recipients = request.POST.get('datatable')
print(recipients)
return HttpResponseRedirect('/product/list')
I have tried variants such as
recipients = request.GET.get('datatable')
and
recipients = request.POST.get('datatable', False)
I cannot get hold of the "object" and don't really know how to debug it.
In the end, it should work as a multiselect for the form (that will be sent by email).
I hope I can help with some hints how to progress.
Thanks!
When I print(request.__dict__) it looks like below
: <SimpleLazyObject: <function AuthenticationMiddleware.process_request.<locals>.<lambda> at 0x7f907ab5f680>>, '_messages': <django.contrib.messages.storage.fallback.FallbackStorage object at 0x7f907ab82b10>, '_body': b'csrfmiddlewaretoken=xxx_length=10', '_post': <QueryDict: {'csrfmiddlewaretoken': ['xxxxx'], 'datatable_length': ['10']}>, '_files': <MultiValueDict: {}>, 'csrf_processing_done': True}
you can not submit an entire table in a form, especially based on id tag. From the code you posted it looks like you configured your datatable without event handlers.
From: https://editor.datatables.net/examples/inline-editing/submitButton.html
dom: "Bfrtip",
ajax: "../php/staff.php",
columns: [
{
data: null,
defaultContent: '',
className: 'select-checkbox',
orderable: false
},
{ data: "first_name" },
{ data: "last_name" },
{ data: "position" },
{ data: "office" },
{ data: "start_date" },
{ data: "salary", render: $.fn.dataTable.render.number( ',', '.', 0, '$' ) }
],
order: [ 1, 'asc' ],
select: {
style: 'os',
selector: 'td:first-child'
},
buttons: [
{ extend: "create", editor: editor },
{ extend: "edit", editor: editor },
{ extend: "remove", editor: editor }
]
and then added a submit button that actually tries to pull all the elements with name tag in the form dom sub-tree and sends those to the back-end.
In order to achieve what you want you need to create a custom event handler in the front-end that is hooked to the checkbox(or use functions integrated in the DataTable plugin) then generate a form on the fly with javascript where you add the relevant information(the pk of the selected record/s I assume). Submitting the newly generated form will result in a valid POST request that you can handle in the view.
Given all above, I would recommend to avoid using such mechanics in general. POST/GET that are not REST based lead to the regeneration of the entire page which is a bad thing to do when dealing with large interactive tables.
Related
I have a table of ETF tickers, ETF names, and their Index names. I want each entry in the table to be clickable and linked to their associated images in the static/images folder. Each image is named after each ETF's index ticker. So when a user clicks on, for example, the first entry ETF ticker "PSCD" links to 'static/myapp/images/sample_regression_S6COND.jpg'. The substring in the link "S6COND" is the part I need as a relative link. Each ETF ticker has a different Index ticker. These Index tickers are in a model associated with the table.
In my table.html page, when I hardcode the link to the static image <td>{{i.etf_ticker}}</td> (see how I typed "S6COND" into the link?), it works, but not when I try to turn it into a relative link {{i.index_ticker}} like <td>{{i.etf_name}}</td>. My static files in correctly placed inside my app folder and includes images, css, and js folders. All images are inside the static/myapp/images folder.
table.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ETF Table</title>
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'myapp/css/table_style.css' %}">
<style type="text/css"></style>
</head>
<body>
<div class="container">
<table id="table" class="table table-dark table-hover table-striped table-bordered table-sm">
<thead>
<tr>
<th data-sortable="true">ETF Ticker</th>
<th>ETF Name</th>
<th>Index Name</th>
</tr>
</thead>
<tbody>
{% if d %}
{% for i in d %}
<tr>
<td>{{i.etf_ticker}}</td>
<td>{{i.etf_name}}</td>
<td>{{i.index_name}}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
<script>
$('#table').DataTable({
"bLengthChange": true,
"lengthMenu": [ [20, 50, 100 -1], [20, 50, 100, "All"] ],
"iDisplayLength": 20,
bInfo: false,
responsive: true,
order: [[4, 'desc']],
});
</script>
</div>
</body>
</html>
views.py
def table(request):
data = Table.objects.all().values()
context = {'d': data}
return render(request, 'table.html', context)
models.py
class Table(models.Model):
etf_ticker = models.CharField(max_length=10)
etf_name = models.CharField(max_length=200)
index_name = models.CharField(max_length=200)
index_ticker = models.CharField(max_length=10)
Create your url in the following manner:
{{i.etf_name}}
I've broken your url into 3 parts. The first and last parts are simple strings. The middle part contains your ticker. The | is used to denote that we're using one of Django templating language's filters. The template we're using is add. It is used to add stuff to whatever comes before the pipe. If we were dealing with numbers, it would add them mathematically, but since we're using strings, it concatenates the 2 strings instead. Similarly the last part is concatenated to the first two parts and ends up generating the url you require.
The reason why your method wasn't working was that you were trying to pass a variable {{i.index_ticker}} to a string. This was creating a url that literally contains a formatterd version of {{i.index_ticker}}. Which is why you were probably getting %7B%7Bi.index_ticker%7D%7D in the url.
I have a table that I sort by date, it works fine in EDGE and Chrome, but the order is messed in Firefox. A series of rows that should be on top got moved down to the bottom.
HTML:
<div class="row mt-4">
<div class="col-12">
<div class="card">
<h6 class="card-header">Change Log Items</h6>
<div class="card-body">
<table id="changes" class="table table-striped table-hover table-bordered table-sm">
<thead class="table-dark">
<tr class="sticky">
<th>Title</th>
<th>Component</th>
<th>Date Committed</th>
<th>Jira Link</th>
<th>Details</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<tr>
<td>{{log.title}}</td>
<td>{{log.component}}</td>
<td>{{log.date_added}}</td>
<td>{% if log.jira_number %}<a class="general" href="https://jira.kinaxis.com/browse/{{log.jira_number}}" target="_blank">{{log.jira_number}}{% endif %}</a></td>
<td>{% if log.details %}{{log.details}}{% elif not log.details and log.jira_number %}See Jira ticket{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
View:
#login_required
def change_log(request):
logs = ChangeLog.objects.all().order_by('date_added')
return render(request, 'help\changelog.html', {'logs': logs})
Any information helps! :)
Update:
I realized the problem was caused by the jQuery corresponding to the HTML element:
<script type="text/javascript">
$(document).ready(function () {
const exceptions = $('#changes').DataTable({
"order": [[ 2, "desc" ]],
"pageLength": 50,
"columnDefs": [{"type": "date", "targets": [2],}], // Sort by Date properly
});
});
</script>
Seems like DataTable has some issues with FF?
Changing the order in "order": [[ 2, "desc" ]]" to asc doesn't work for FF.
Most likely, the date format you are using is not supported by Firefox, as "the date formats supported by each browser vary significantly". In such cases, one could use the "ultimate" date / time ordering plug-in for DataTables, as suggested here. To do that, include the following libraries in your HTML file, as described in the above link:
<script type="text/javascript" charset="utf8" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.4/moment.min.js"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/plug-ins/1.11.5/sorting/datetime-moment.js"></script>
Next, register the date format(s) that you wish DataTables to detect and order using the $.fn.dataTable.moment(format) method. For instance:
$(document).ready(function() {
$.fn.dataTable.moment( 'HH:mm MMM D, YY' );
...
DataTables will automatically detect the column(s) with date data, by checking to see if the data in a column matches any of the given types. You can register multiple date formats, if a DataTable contains more than one date columns.
Try explicitly adding "ordering: true" to your DataTable() instantiation, like so:
<script type="text/javascript">
$(document).ready(function () {
const exceptions = $('#changes').DataTable({
ordering: true, # add this line
"order": [[ 2, "desc" ]],
"pageLength": 50,
"columnDefs": [{"type": "date", "targets": [2],}], // Sort by Date properly
});
});
</script>
More a suggestion than an answer, but didn't want to paste this code in the comments.
I am trying to write a small web application using Flask. But I have a question while creating selection list. For example, there are 2 selection lists in the web. The first one is "keyID" and second one is "fieldName". When user clicked an item in selection list "keyID" then a corresponding selection list "fieldName" appears.
Here's the HTML:
<div class="control">
<label for="keyid">KeyID of parking spots:</label><br>
<select name ="keyidList" id="keyid_list" style="width: 400px">
{% for keyid in keyIDs %}
<option value ="{{keyid}}">{{keyid}}</option>
{% endfor %}
</select><br><br>
</div>
<div class="control">
<label for="fieldname">FieldName of parking spots:</label><br>
<select name ="fieldnameList" id="fieldname_list" style="width: 400px">
{% for fieldname in fieldnames %}
<option value ="{{fieldname}}">{{fieldname}}</option>
{% endfor %}
</select><br><br>
</div>
Javascript code in HTML file
<head>
<meta charset="utf-8">
<script>
$(function(){
var select_keyid = $('#keyid_list');
var selected_keyid = select_keyid.val();
select_keyid.on('change', function(){
$.ajax({
type: 'POST',
data: JSON.stringify({
'selected_keyid': selected_keyid,
}),
url: "{{ url_for('auth.get_fieldname_list') }}",
success: function(data) {
$("#fieldname").empty();
for (var i = 0; i < data.length; i++) {
$("#fieldname").append('<option value="' +fieldname+ '">' + fieldname + '</option>');
}
}
});
});
});
</script>
</head>
And here's the Python Flask code
#auth.route('/get_fieldname_list', methods=['POST'])
def get_fieldname_list():
req = request.json
keyid = req.get('selected_keyid')
fieldname = get_field_name_from_keyid(keyid)
return fieldname
I personally think that, the problem is with ajax code but I don't know why and how to fix it. Any advice will be really appreciated.
i came across get_template_attribute in the flask api and thought of it as a great way to achieve partial page rendering.
so the goal was
create a 2 layer menu-page layout
menu should be static
on click of
menu, page should be shown dynamically without refreshing the entire
page
so i did something like so
LandingPage.html
<html>
<head>
<style>
html,body{
height:100%;
}
</style>
<script type="text/javascript">
function finalConfirmationToStartJob() {
alert('in here')
$.ajax({
type : 'GET',
url : 'home2',
data : ''
});
};
</script></head>
<body>
<table border=1 height=100% width=100%>
<tr><td colspan='2'/></tr>
<tr>
<td width=15%>
<ul>
<li>ABC</li>
<ul>
<li>ABC1</li>
<li>ABC2</li>
<li>ABC3</li>
</ul>
<li>DEF</li>
<li>GHI</li>
<li>JKL</li>
</ul>
</td>
<td>
{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
how are you darlings!!! {{hello()}}
</td>
</tr>
<tr><td colspan='2'/></tr>
</table>
</body>
</html>
Controller
#app.route("/home")
def goto_home():
return render_template('/LandingPage.html')
#app.route("/home2")
def try_out_macro():
print 'hellooo elloo'
hello = get_template_attribute('/LandingPage.html', 'hello')
return hello('World')
However when i do click on the action link, even though the controller actually returns perfect data , the data is not displayed on the screen.
When i hit the url /home2 i do get perfect data, however on click on action link nothing happens on the html
Can someone please help me, i am not sure what i am doing wrong
PS : i got it to work using innr html and div combination, however i would still like to know why the above doesnt work.
I have looked through previous questions involving the same error, but have not managed to find a working solution for my problem. I have a form in my html code (as part of a single-page-application) that I wish to submit to my python server through ajax.
the form in details.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>A Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">`
<link href= {{ url_for("static",filename="css/bootstrap.min.css") }} rel="stylesheet" media="screen">
</head>
<body>
<div>
<form method="post">
<label for="address">Address</label>
<input class="form-control" type="text" name="address">
<label for="postalcode">Postal Code</label>
<input class="form-control" type="text" name="postalcode"><br>
<label for="city">City</label>
<input class="form-control" type="text" name="city">
<label for="country">Country</label>
<input class="form-control" type="text" id="country_indicator" placeholder="Country" name="country">
<button id="submitForm" type="submit">Submit</button>
</form>
</div>
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<script src={{ url_for("static", filename="js/bootstrap.min.js") }}></script>
<script src={{ url_for("static", filename="js/details.js") }}></script>
</body>
</html>
If I remove 'method="post"' from the html form, the page empties the forms and reloads, but with it I get the above mentioned error, even when the form is fully filled out. My guess is that something between the form and the JS is not working as the request.json always returns NoneType objects.
details.js:
$(document).ready(function() {
$("#submitForm").click(function(){
var address = $("#address").val();
var postalcode = $("#postalcode").val();
var city = $("#city").val();
var country = $("#country").val();
console.log(address);
var details = {
"address" : address,
"postalcode" : postalcode,
"city" : city,
"country" : country
}
console.log(details.city);
$.ajax({
type: "POST",
url: "/details",
data: JSON.stringify(details, null, '\t'),
contentType: 'application/json;charset=UTF-8',
success: function(result) {
console.log(result)
}
})
})
})
Note: I added the console.log for troubleshooting, but no text appears in the js-console, which is why I believe the problem appears already before this point.
the relevant app.route in my .py file: I am not yet using the values from details.js, I just wish to see that something is actually sent. This is why I only return "ok" for now.
#app.route('/details', methods=['GET', 'POST'])
def details():
if request.method == "POST":
print(request.json['address']) # . TypeError: 'NoneType' object has no attribute '__getitem__' - crash appears here.
return "ok")
return render_template("details.html")
So because of some problem in the previous steps, the object sent to the .py file is NoneType I assume. I am very new to python and JS, so any pointers would be greatly appreciated. Thank you in advance!
Edit: I also encountered "uncaught ReferenceError: $ is not defined" now from the javascript console, but moving the jquery- to the head solved that problem
Doh! The data isn't being sent to the server properly! I've rewritten some of your code below. I hope you don't mind, but the form will now submit with ordinary post variables instead of JSON.
#app.route('/details', methods=['GET', 'POST'])
def details():
# This is our new method, notice it's a bit streamlined.
if request.method == "POST":
# We can get the post data using request.form.get(). The first variable is the name="" attribute in HTML, and the second is the default value if it wasn't found in the data.
return "The address was %s" % request.form.get('address', 'not provided! :O')
return render_template("base.html")
Now for the Javascript!
$(document).ready(function() {
$("#submitForm").click(function(e){
// Prevent the HTML page from submitting the form since we're doing this by AJAX. This would cause duplications and other issues.
e.preventDefault();
// Get a FormData object from the page
var data = new FormData($('form')[0]);
// You don't HAVE to send your data by JSON, and in this instance it will be easier not to
$.ajax({
type: "POST",
url: "/details",
data: data,
processData: false,
contentType: false,
success: function(result) {
console.log(result)
}
})
})
})
Hope this helps! Don't forget to mark the answer as solved if this fixes it for you. :)