In this post I’ll describe how to create an ajax search page using Django
I’ve really liked using SGJR – “server generated javascript responses” in rails and wanted something as easy in Django.
Here’s the pattern we’re developing
- CLIENT: load html page with minimal content
- CLIENT: once page is loaded: make and ajax request, showing spinner
- SERVER: perform query from search parameters
- SERVER: return a html table from the data
- CLIENT: replace the html in a div with that returned
- CLIENT: stop spinner
model
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class System(models.Model): | |
system_id = models.IntegerField(primary_key=True) | |
name = models.CharField(max_length=200) | |
comments = models.TextField() | |
# ASSOCIATIONS | |
site = models.ForeignKey(Site, | |
on_delete=models.SET_NULL, | |
null=True) |
search form
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<form id='system_search_form' action="{% url 'metadata:_systems_search' %}" method="post" accept-charset="utf-8"> | |
{% csrf_token %} | |
<div class="row"> | |
<div class="col-12"> | |
<h3>SYSTEMS SEARCH</h3> | |
</div> | |
<div class="col"> | |
<label>Name</label><br /> | |
<input name="name" type="input" class="form-control" /> | |
</div> | |
<div class="col"> | |
<label>Comments</label><br /> | |
<input name="comments" type="input" class="form-control" /> | |
</div> | |
<div class="col-2 push-right"> | |
<label class="">Search</label><br /> | |
<input id='system_search_form_btn' class='btn btn-primary' type="submit" /> | |
</div> | |
</div> | |
</form> |
initial table div
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="systems_search_table"> | |
<div class="alert alert-info text-center" role="alert"> | |
Click search to find Systems. | |
</div> | |
</div> |
the form to submit search criteria
A few things to note:
- beforeSend() – perform any pre-request items., i’m starting a full-page css only spinner.
- always() – stopping that spinner regardless of request result
- done() – this is where the magic happens by replacing the html contents of the div with response, also html.
- $(‘input#system_search_form_btn‘).click(); – this is to submit the form after the page loads. This results is a fast initial page load since your only loading a static page.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script> | |
$(document).ready(function() { | |
$('input#system_search_form_btn').on('click', function(e) { | |
// submits related form | |
}); | |
$('form#system_search_form').on('submit', function(e) { | |
e.preventDefault(); | |
$.ajax({ | |
type: $(this).attr('method'), | |
url: $(this).attr('action'), | |
data: $(this).serialize(), | |
beforeSend: function( xhr ) { | |
$('#loading').addClass('loading'); | |
} | |
}).done(function(response) { | |
$( '#systems_search_table' ).html( response ); | |
}).fail(function(response) { | |
}).always(function() { | |
$('#loading').removeClass('loading'); | |
}); | |
}); | |
$('input#system_search_form_btn').click(); | |
}); | |
</script> |
view that receives ajax request
I’ve left out pagination, it’s coming next. Basically, this uses the inputs from the form and if there are no results uses a generic template saying something to the affect of “there are no search results, try different search criteria”
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@csrf_protect | |
def systems_search(request): | |
name = request.POST['name'] | |
comments = request.POST['comments'] | |
systems = System.objects.filter(name__icontains=name, comments__icontains=comments)[:10] | |
# systems = System.objects.all()[:5] | |
template = 'no_search_results.html' | |
context = None | |
if systems.count() > 0: | |
template = 'systems/_search.html' | |
context = { | |
"systems": systems | |
} | |
# https://docs.djangoproject.com/en/2.1/ref/template-response/ | |
return render(request, template, context) |
template rendered in ajax response
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% block content %} | |
<table class="table table-striped table-bordered table-condensed table-hover"> | |
<thead> | |
<tr> | |
<th class="">Id</th> | |
<th class="">Name</th> | |
<th class="">Site</th> | |
</thead> | |
<tbody> | |
{% for system in systems %} | |
<tr> | |
<td>{{ system.system_id }}</td> | |
<td>{{ system.name }}</td> | |
<td>{{ system.site.site_name }}</td> | |
</tr> | |
{% endfor %} | |
</tbody> | |
</table> | |
{% endblock content %} |