I want to dynamically filter my querySet using the value of my input field with Ajax.
Right now I’m trying something like this:
Template:
<div class="card mt-3">
<div class="card-body">
<form action="" form="get">
<input data-url="{% url 'klantbeheer' %}" class="zoekklanten" name="q" type="text" placeholder="Zoek op contactnaam...">
</form>
{% if object_list %}
<div class="single-table">
<div class="table-responsive">
<table class="table text-center">
<thead class="text-uppercase bg-dark">
<tr class="text-white">
<th scope="col">Bedrijfsnaam</th>
<th scope="col">Contactnaam</th>
<th scope="col">Locatie</th>
<th scope="col">Actie</th>
</tr>
</thead>
<tbody>
{% for user in object_list %}
<tr>
<td>{{ user.bedrijfsNaam }}</td>
<td>{{ user.contactNaam }}</td>
<td>{{ user.adres }}</td>
<td>
<i class="fas fa-edit"></i>
<i class="fas fa-trash-alt"></i>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% else %}
<p>Geen gebruikers gevonden</p>
<p>
Maak klant aan
</p>
{% endif %}
</div>
</div>
View:
class ManageUserView(SuperUserRequired, ListView):
model = User
template_name = 'webapp/klant/beheerklant.html'
#Query based upon contactName
def get_queryset(self, query):
#Contactnaam bevat QUERY EN superuser is vals.
object_list = self.model.objects.filter(contactNaam__icontains=query).exclude(is_superuser=True)
return object_list
def post(self, request, *args, **kwargs):
query = self.request.POST.get('query', '')
print(query)
self.get_queryset(query)
Jquery:
$('.zoekklanten').on('keyup', function(){
var url = $(this).data('url')
var query = $(this).val();
$.ajax({
url:url,
type:'POST',
data:{
'query':query
},
dataType: 'json',
beforeSend: function(xhr, settings){
if(!csrfSafeMethod(settings.type) && !this.crossDomain){
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
success: function(result){
},
error: function(data){
}
})})
I’m getting "get_queryset() missing 1 required positional argument: ‘query’ " error. I assume calling the get_queryset function inside a post request function isn’t the way to go about this.
What would be the best way to filter dynamically using Ajax in Django?
Your problem is that the method ListView.get_queryset() is normally defined without any extra parameter, so when it's called by Django implicitly, it does not pass any value to the new extra parameter query you have added there.
So either use self.request.POST.get('query', '') in get_queryset() directly instead of passing it there as a parameter or add a default value to the parameter.
However using a user input and passing it directly into SQL query is risky as it might lead to an SQL injection (even though Django tries to sanitize the value).
def get_queryset(self, query=None):
# either set default value for `query` here^
if not query:
return super().get_queryset()
# or take the value from `POST` here instead of passing it as a parameter to the method
query = self.request.POST.get('query')
object_list = self.model.objects.filter(contactNaam__icontains=query).exclude(is_superuser=True)
return object_list
Related
I've got a large template file here that I'm accessing in a for loop to print a table with some values in it. When a user clicks on one of those values I'd like it to carry over just that 1 json block to another page in order to have it prefill some fields. I'm having trouble getting my program to select that single entry in the json dict in order to render the next page. Here's the code involved:
views.py
#apps.route('/add')
#login_required
#admin_required
def view_apps():
""" View available apps """
template = Template.query.all()
dir_path = os.path.dirname(os.path.realpath(__file__))
json_content = combine_json_templates()
print(json_content)
print(len(json_content))
return render_template('apps/add_app.html', apps=json_content)
def combine_json_templates():
master_list = []
cwd = os.getcwd()
print(cwd)
json_storage = 'app/storage/templates/json/'
for file in os.listdir(json_storage):
with open(json_storage + file) as json_path:
json_content = json.load(json_path)
for item in json_content:
master_list.append(item)
return master_list
#apps.route('/add/<int:app_id>')
#apps.route('/add/<int:app_id>/info')
def app_info(app_id):
app = json_content[""+app_id+""]
print(app)
add_app.html (code that displays the table and contains the onclick:
<div style="overflow-x: scroll;">
<table class="ui searchable sortable unstackable selectable celled table">
<thead>
<tr onclick="window.location.href = '{{ url_for('apps.app_info', app_id=apps.pop(['name'])) }}';">
<th class="sorted ascending">Title</th>
<th>Desctiption</th>
<th>Catagory</th>
<th>Platform</th>
</tr>
</thead>
<tbody>
{% for a in apps | sort(attribute='title') %}
<tr>
<td>{{ a.title }}</td>
<td>{{ a.description }}</td>
<td>{{ a.categories }}</td>
<td>{{ a.platform }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
The part that I'm having trouble with is getting a unique value in order to pass it onto the next page and reference the correct json.
<tr onclick="window.location.href = '{{ url_for('apps.app_info', app_id=apps.pop(['name'])) }}';">
Should I be using a different method for referencing the json file? Should I be modifying it when loading the json files in order to add some kind of key?
This is my first flask project so I'm not sure the best way to go about this.
I think I have an approach, the question is whether you like it.
# ./__init__.py
def create_app():
app = Flask(__name__,
# default: ./var/app-instance/
instance_relative_config=True,
)
app.config.from_mapping(
SECRET_KEY=b'change-me\n',
)
try:
os.makedirs(app.instance_path)
except OSError:
pass
# init endpoints/blueprints here
from .apps import create_module as apps_create_module
apps_create_module(app)
return app
# ./app/apps/view.py
# TODO: move this to the apps configuration file
JSON_TEMPLATES_DIR = 'storage/templates/json'
def load_json_data(path):
data = []
for fname in glob(os.path.join(path, '*.json')):
with open(fname) as fp:
dataset = json.load(fp)
data.extend(dataset)
return data
#apps.route('/add')
#login_required
#admin_required
def list_apps():
'''View available apps'''
apps = load_json_data(
os.path.join(current_app.instance_path,
JSON_TEMPLATES_DIR))
return render_template('apps/add_app.html', **locals())
#apps.route('/add/info', methods=['POST'])
#csrf.exempt
def app_info():
'''View selected app'''
ident = request.form.get('ident', None)
if ident:
try:
app = json.loads(ident)
print(app)
except: pass
return render_template('apps/app_info.html', **locals())
<!-- "./app/templates/apps/add_app.html" -->
<div style="overflow-x: scroll;">
<table class="ui searchable sortable unstackable selectable celled table">
<thead>
<tr>
<th class="sorted ascending">Title</th>
<th>Description</th>
<th>Catagory</th>
<th>Platform</th>
</tr>
</thead>
<tbody>
{% for a in apps | sort(attribute='title') %}
<tr id="app-entry-{{loop.index}}">
<td>
<div class="namecell">
<span class="nametext" style="display: block;">{{ a.title }}</span>
<span class="btn-group" role="group" style="margin-top: 1rem">
<form method="post" action="{{url_for('apps.app_info')}}">
<input type="hidden" name="ident" value='{{app | tojson | safe}}' />
<button type="submit">Setup</button>
</form>
</span>
</div>
</td>
<td>{{ a.description }}</td>
<td>{{ a.categories }}</td>
<td>{{ a.platform }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
I'm having a hell of a time here trying to get this to work. I'm pretty sure I've done almost all the research I can trying to solve this, and I just can't figure out where I'm going wrong. So, that being said, thanks in advance for any help you guys can offer.
I'll start by posting my code:
HTML:
{% block main %}
<form action="{{ url_for('index') }}" method="post">
<fieldset>
<div id ="total" class="form-inline">
<div id="itemBackground" class="col-sm-6">
<div id="itemToolBar" class="row">
<div class="col-sm-6">
<label>
Show:
<select name="list_length" class="form-control input-sm">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</label>
</div>
<div class="col-sm-6" align="right">
<label>
Search:
<input class="form-control input-sm" autocomplete="off" type="textbox" name="q" id="q" placeholder="">
</label>
</div>
</div>
<table id="itemTable" name="itemTable" class="table table-hover table-inverse">
<thead>
<tr id="head">
<th>ID</th>
<th>Item</th>
<th>Buy</th>
<th>Sell</th>
</tr>
</thead>
{% for item in items %}
<tr id="rows">
<td scope="row">{{ item[0] }}</td>
<td>{{ item[1] }}</td>
<td>{{ item[2] }}</td>
<td>{{ item[3] }}</td>
</tr>
{% endfor %}
</table>
</div>
<div id = "vehicleBackground" class="col-sm-6">
<table id="vehicleTable" class="table table-hover table-inverse">
<thead>
<tr id="head">
<th>ID</th>
<th>Item</th>
<th>Buy</th>
</tr>
</thead>
{% for vehicle in vehicles %}
<tr id="rows">
<th scope="row">{{ vehicle[0] }}</th>
<td>{{ vehicle[1] }}</td>
<td>{{ vehicle[2] }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</fieldset>
</form>
{% endblock %}
Python:
# configure application
app = Flask(__name__)
# ensure responses aren't cached
if app.config["DEBUG"]:
#app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
# configure session to use filesystem (instead of signed cookies)
app.config["SESSION_FILE_DIR"] = gettempdir()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
if not request.form.get("q"):
return print("Give me a search query")
else:
q = request.form.get("q") + "%"
print(q)
conn = sqlite3.connect('items.db')
db = conn.cursor()
db.execute("SELECT * FROM items WHERE item LIKE '{}'".format(q))
itemList = db.fetchall()
db.execute("SELECT * FROM vehicles")
vehicleList = db.fetchall()
db.close()
conn.close()
return render_template("index.html", items = itemList, vehicles = vehicleList)
# else if user reached route via GET (as by clicking a link or via redirect)
else:
# configure CS50 Library to use SQLite database
conn = sqlite3.connect('items.db')
db = conn.cursor()
db.execute("SELECT * FROM items")
itemList = db.fetchall()
db.execute("SELECT * FROM vehicles")
vehicleList = db.fetchall()
db.close()
conn.close()
return render_template("index.html", items = itemList, vehicles = vehicleList)
#app.route("/search")
def search():
"""Search for places that match query."""
print("here")
# TODO
q = request.args.get("q") + "%"
items = db.execute("SELECT * FROM items WHERE item LIKE '{}'".format(q))
return jsonify(items)
Javascript:
// execute when the DOM is fully loaded
$( document ).ready(function() {
configure();
});
/**
* Configures application.
*/
function configure()
{
// configure typeahead
$("#q").on('input propertychange paste', function() {
//$("#q").val("hi");
// get items matching query (asynchronously)
var table = "<thead><tr><th>ID</th><th>Item</th><th>Buy</th><th>Sell</th></tr></thead><tr>";
var parameters = {
q: $("q").val()
};
$.getJSON(Flask.url_for("search"), parameters)
.done(function(data) {
$('#q').val("hi");
for (var i=0; i<data.length; i++) {
obj = data[i];
for (var key in obj) {
table += "<td>" + key + "</td>";
}
}
table += "</tr>";
$("#itemTable").replaceWith(table);
//document.getElementById('').innerHTML = table;
})
.fail(function() {
// log error to browser's console
console.log(errorThrown.toString());
});
});
// give focus to text box
$("#q").focus();
}
Where I'm stuck is with the javascript when I check for the textbox input to be changed and thus try to update the table with data that matches the query from my database. I do actually make it into the "input propertychange paste" section, I know because I set it to change the textbox value to "hi" whenever anything is typed in the box, but I never make it through the next section where I try to getJson from my /search python function, then format it into a table, and then replace the old table with the one I just made.
I'm not really sure what more information I can give you guys, but I'm sure I'm missing some stuff you need. So let me know if you do. Thanks again in advance for helping me out if you can!
Edit:
I should add that my code never even reaches into that getJSON block. As you can see I try to change the value of the input textbox inside of the getJSON call, but the textbox never gets updated to say "hi". Seems like I'm possible calling getJSON wrong? Not sure.
Edit2:
Okay, so I did manage to get into the getJSON call now. Thank you for your help with that. But now I'm onto the real part of my question, how would I go about formatting the table after I get the JSON from the SQL database? From what I can tell, I have the formatting at least mostly correct, but I might be having an issue when it comes to removing the old table and inserting the new one.
The selector #itemTable is not matched in HTML. The <table> element name attribute value is "itemTable"; the element does not have an id set at HTML. Use $("table[name=itemTable]") or set the id to "itemTable" at HTML to be able to use #itemTable selector and match the existing <table> element.
I am having problems trying to update a block of my site (which includes another templates) by issuing an Ajax call. The that needs to be updated works just fine, but the JS script that is inside that template does not work (before, I was just adding the full request to my template, but that caused to have twice the content of the parsed template, but JS scripts were working).
PD: I am kind new to JS and have some experience with Django (still just digging in the world of Web Apps development).
My template:
{% load staticfiles %}
<script>
$(document).ready(function() {
var current_item_id = null;
var current_item_title = null;
var selected_items = null;
// Selección en las tablas
$('.table tr').on('click', function() {
$(this).toggleClass('selected');
});
// Comportamiento del toolbar flotante
$('.table tr').on('click', function() {
selected_items = $('.selected');
current_item_id = $(this).attr('id');
current_item_title = $(this).attr('name');
if (selected_items.length === 0) {
$('.modify-big').attr('disabled', true);
$('.delete-big').attr('disabled', true);
}
else if (selected_items.length > 1) {
$('.modify-big').attr('disabled', true);
}
else {
$('.modify-big').attr('disabled', false);
$('.delete-big').attr('disabled', false);
}
});
});
</script>
<div id='notifications'>
{% if notifications %}
<table class="table">
<thead>
<tr>
<!--<th class="text-left table-id">ID del Item</th>-->
<th class="text-left">Item</th>
<th class="text-left">Link de descarga</th>
<th class="text-left">Plantilla</th>
</tr>
</thead>
<tbody class="table-hover">
{% for notification in notifications %}
<tr id='{{ notification.item_id }}' name='{{ notification.item_title }}'>
<!--<td class='text-left'>{{ notification.item_id }}</td>-->
<td class='text-left'>
<a class='tooltip-right' href='#' tooltip='Ver item'>
<img src="{% static 'images/icon_notification_details.png' %}">
</a>
{{ notification.item_title }}
</td>
<td class='text-left'>
{% if notification.download_link %}
<a href='{{ notification.download_link }}' target='_blank'>{{ notification.download_link }}</a>
{% else %}
---
{% endif %}
</td>
<td class='text-left'>{{ notification.email_template.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if first_time %}
<p class='info-msg first-time'>Últimas notificaciones agregadas.</p>
{% else %}
<div class="pagination">
<span class='step-links'>
{% if notifications.has_previous %}
<button class='previous' onclick='search_notifications("", "{{ notifications.previous_page_number }}");'></button>
{% else %}
<button class='previous' disabled></button>
{% endif %}
<span class='current'>
Página {{ notifications.number }} de {{ notifications.paginator.num_pages }}
</span>
{% if notifications.has_next %}
<button class='next' onclick='search_notifications("", "{{ notifications.next_page_number }}");'></button>
{% else %}
<button class='next' disabled></button>
{% endif %}
</span>
</div>
{% endif %}
{% else %}
<p class="info-msg info">No se han encontrado notificaciones.</p>
{% endif %}
</div>
Ajax Call:
search_notifications = function(first_time=false, page=null) {
show_div_loader($('#notifications'));
$.ajax({
url: "{% url 'notifications_loader' %}",
data: {
'search_notifications_query': $('#search-notifications-query').val(),
'search_notifications_listing': $('#search-notifications-listing option:selected').val(),
'first_time': first_time,
'page': page,
},
success: function(data){
// Solo necesitamos actualizar la sección #notifications
data = $(data).filter('#notifications').html();
notifications.html(data);
hide_div_loader($('#notifications-container'));
}
});
};
My view:
def notifications_loader(request):
[...]
return render(request, 'notifications_search_result.html', {'notifications': notifications, 'first_time': first_time})
As you can see in the Ajax sucess function, I do:
data = $(data).filter('#notifications').html();
notifications.html(data);
Before, I was doing:
notifications.html(data);
This last one was adding twice the parsed template but JS script inside it were working.
What I am doing wrong?
Thanks in advance.
EDIT:
I don't know if is the best way, but I've added a 'container' to my code and just insert parsed themplate there:
In my main template:
<div id='notifications-container'>
<!--{% include 'notifications_search_result.html' %}-->
</div>
JS scripts are working again and I don't have twice the parsed template. Now, for I was reading I think this is not the best way to work with Django and Ajax or I am wrong? Maybe I just need to return the view as JSON and replace only the needed data?
Still I have doubs about Ajax+Django and best practices way.
Thanks.
When doing AJAX using Django I do it this way:
Define a route(view) that will serve your default template containing your ajax call script.
Add another route(view) for the ajax call:
def auto_complete(request):
# do some actions and put the results in var
return HttpResponse(simplejson.dumps(var),content_type='application/json')
And you will call the second route in your ajax call
Hope it helps you
I'm trying to develop a simple task list with PHP. I have a list of tasks using twig http://twig.sensiolabs.org/ for each loop. The checkbox is checked if the task is marked as done. My question is how can I attach to each checkbox a script to make ajax calls with different parameters so the server knows which task is being checked?
<table>
<thead>
<tr>
<th>Task</th>
<th>Valmis</th>
</tr>
</thead>
<tbody>
{% for task in tasks %}
<tr>
<td>{{task.description}}</td>
<td><input type="checkbox" {{ task.done ? "checked" : "" }} ></td>
</tr>
{% endfor %}
</tbody>
</table>
I add a class for enable ajax call to a form, and make it via a POST to the server and manage the response, like this example:
Javascript code:
$(document).ready(function() {
$('.lista-proroghe-item').each(function(){
$(this).find('.perform-ajax-request-button').click(function(){
var form = $(this).closest("form"), action = form.attr('action');
var currentButton = $(this);
var currentMessage = $(this).next('.proroga-messaggio');
$(this).attr('disabled', 'disabled');
$(this).html('Processing...');
$.ajax({
url: action,
type: "POST",
data: form.serialize(),
success: function(data) {
// console.log(data);
$(currentButton).hide();
$(currentMessage).html('<span>'+data.description+'<br>'+data.extendedMessage+'</span>');
}
});
e.preventDefault();
return false;
})
})
});
Twig code:
{% for extension in assignation.contract.contractModificationDurations %}
<li class="lista-proroghe-item">
<form action="{{ path('contractExtension', {'idContractModification' : extension.idContractModification, 'userid':user.id }) }}">
Element
<button class="button small perform-ajax-request-button right"><span class="refresh">to task</span></button>
<div class="proroga-messaggio"></div>
</form>
</li>
{% else %}
<li>
Nessuna proroga trovata
</li>
{% endfor %}
Hope this help.
I'm attempting to access the contents of a cell, within my django template using javascript.
The table can contain x rows. When any of the "click to show" links (in the TEST column) are clicked, I would like the row's {{ i.description }} to be passed through to my views (ajax_test function), the "click to show" link to disappear and be replaced with the returned html from the view. I've included my terrible javascript attempt, knowing full well that this doesn't do what I'm asking. If anyone could help, it would be very much welcomed.
Template:
{% extends "myapp/base.html" %}
{% block content %}
<p>You searched for: <strong>{{ query }}</strong></p>
{% if elements %}
<p>Found {{ elements|length }} Element{{ elements|pluralize }}.</p>
<div class="datagrid"><table>
<thead><tr>
<th>Name</th>
<th>Description</th>
<th>Type</th>
<th>Parameter Arguments</th>
<th>TEST</th>
<tbody>
{% for i in elements %}
<tr class="{% if forloop.counter|divisibleby:2 %}alt{% else %}{% endif %}">
<td>{{ i.name }}</td>
<td><div id="desc">{{ i.description }}</div></td>
<td>{{ i.type }}</td>
<td>{% for a in i.parameter.all %}{{ a.argument }}{% endfor %}</td>
<td><div class="clicks">click to show</div></td>
</tr>
{% endfor %}
</tbody>
</table></div>
{% endblock %}
Javascript (this is where I haven't got a clue really, the code below is a failed attempt). The code is included as a static file (ajax_test.js, which is loaded from base.html)
$(function(){
var myCell = document.getElementById("desc");
var cellContents = (myCell.innerHTML);
$('.clicks').click(function() {
$.ajax({
type: "POST",
url: "/myapp/test_ajax/",
data: {
'ajax_data' : cellContents,
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
success: ajaxSuccess,
dataType: 'html'
});
});
});
function ajaxSuccess(data, textStatus, jqXHR)
{
$('.here').html(data); //I would like to put this in the "click to show" cell. I was placing it in a random div for testing.
}
test_ajax view:
def ajax_test(request):
if request.POST:
ajax_data = request.POST['ajax_data']
ajax_query = #some generic lookup using ajax_data
return render_to_response('myapp/ajax_test.html', {'ajax_query': ajax_query})
ajax_test template (to be returned to the cell):
{{ ajax_query }}
Try this:
html
<td><div class="clicks"><a class="click-to-show" href="#" data-desc="{{ i.description }}">click to show</a></div></td>
javascript
$(function(){
$('.click-to-show').click(function(e) {
$.ajax({
type: "POST",
url: "/myapp/test_ajax/",
data: {
'ajax_data' : $(e.target).attr("data-desc"),
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
success: function ajaxSuccess(data, textStatus, jqXHR) {
$(e.target).parent().html(data);
},
dataType: 'html'
});
});
});
As you can see we keep the DOM relative to link element all the time, since we render rows multiple times, this will make sure we update the correct row.