a better way to do ajax in django - javascript

The other day I wrote some AJAX for a Django app that i have been working on.
I come from Ruby on Rails, so I haven't done much in the way of raw JS.
So based on Rails' partials, I something similar to the following in a sort of pseudocode, don't sweat the details:
1) JS function using prototype's Ajax.Updater ('tablediv' being the id of the table i wanted to update Ajaxily, and url pointing to the proper django view)
function updateTable(){
new Ajax.Updater('tablediv',url {params: params....etc
2) django view that got new data to populate the table with:
def ajaxTable
objects = Objects.object.all...
return render_to_response('ajaxtable.html',objects)
3) ajaxtable.html was just a sort of Rails "partial" so basically a table w/o <table> </table> ...:
<th>{{object.data}}</th>
<td>{{object.moredata}}</td>
so to my actual question:
This seemed hacky to me, I sort of threw it together after getting tired of searching online for what i wanted.
Is this the way it's done? It works fine, I just don't know enough to know, you know?

It kinda depends what you want to do I think. Ajax being quite a wide range of scenarios from Google Maps to a simple auto-complete varys greatly in complexity and the best approach.
However, there are some useful things you can do that help.
1) Template level
Make sure you have "django.core.context_processors.request" in your TEMPLATE_CONTEXT_PROCESSORS setting. Then you can do this;
{% if not request.is_ajax %}
<html>
<head>
...
</head>
<body>
...
{% endif %}
actual content
{% if not request.is_ajax %}
</body>
</html>
{% endif %}
Basically then say this page is /test/ you can do a browser request and get the full content or a request via JavaScript and just get the content. There is a blogpost somewhere that explains this in more detail but I can't find it at the moment.
2) In the view
In the template we are just accessing the request object in the template. In the view you can do very similar things.
def my_view(request):
if requst.is_ajax():
# handle for Ajax requests
# otherwise handle 'normal' requests
return HttpResponse('Hello world')
The above methods don't really do it differently than you do but allow you to re-use views and write it bit more concisely. I wouldn't really say what you are doing is wrong or hacky but you could write it to make it more concise and re-use the templates and views.
say for example you could have just one template and if its a Ajax request have it only return the section that will need to be updated. In your case it would be the tables views.

I am quite late, but I want to document how to combine and adapt the solutions presented by d0ugal
in a way, that it will resolve a much cleaner template-code.
I have a model representing contact persons.
The (generic) view to get one ContactPerson looks like this:
def contcactperson_detail_view(request, name):
try:
person = ContactPerson.objects.get(slug=name)
except:
raise Http404
if request.is_ajax():
return contcactperson_detail_view_ajax(request, person)
return list_detail.object_detail(
request,
queryset = ContactPerson.objects.all(),
object_id = person.id,
template_object_name = "contactperson",
)
#render_to('cms/contactperson_detail_ajax.html')
def contcactperson_detail_view_ajax(request, person):
return {'contactperson':person, 'is_ajax':True}
The template to render the view that handles one ContactPerson is called contcactperson_detail_view.html:
{% extends "index.html" %}
{% block textpane %}
<h1 id="mainheader">{{ contactperson.first_name }} {{ contactperson.family_name }} </h1>
<div class="indentation"> </div>
{% include 'cms/contactperson_detail_photo.html' %}
<div id="text_pane">
{% include 'cms/contactperson_detail_textpane.html' %}
</div>
{% endblock %}
It includes two sub-templates
contactperson_detail_textpane.html
<p>{{ contactperson.description }}</p>
<ul>
<li>
<dl>
<dt>Email</dt>
<dd>
{{ contactperson.mail }}
</dd>
</dl>
</li>
<li>
<dl>
<dt>Contact Person for</dt>
<dd>
<ul>
{% for c in contactperson.categories.all %}
<li>{{ c }}</li>
{% endfor %}
</ul>
</dd>
</dl>
</li>
</ul>
and contactperson_detail_photo.html
{% with contactperson.photo.detailphoto as pic %}
{% with pic.url as pic_url %}
<div {% if not is_ajax %}id='imageContainer'{% endif %} style="float: right;padding-right:0.5em;
padding-bottom: 1em; padding-left:0.5em;clear:both;
width:{{ pic.width }}px">
<div style="width:{{ pic.width}}px">
<img style="clear:both" src="{{ pic_url }}" alt="{{ i.name }}"/>
</div>
</div>
{% endwith %}
{% endwith %}
this 3 templates will be used, if the request isn't ajax.
But if the request is ajax, contcactperson_detail_view will return the view contcactperson_detail_view_ajax, that uses the template contactperson_detail_ajax.html for rendering. And this template looks like this:
<h1>{{ contactperson.first_name }} {{ contactperson.family_name }}</h1>
{% include 'cms/contactperson_detail_photo.html' %}
{% include 'cms/contactperson_detail_textpane.html' %}
So it uses the same sub-templates but isn't extending anything, therefore only the needed markup delivered. As the ajax view passes is_ajax = True to the template, it can be used to adjust minor things, like setting correct id-attributes.
No context-processor or additional url-conf needed.
Finally the Javascript code:
$("#contact_person_portlet a").click(function(event){
event.preventDefault();
$.ajax({
type: "GET",
url: event.target.getAttribute('href'),
success: function(msg){
overlay(msg);
}
});
});
Hope that it will be useful for some people. If so, please leave a comment!

No matter what, you're going to need at least two things:
Your javascript code to make the call (you have this)
Server side code to handle the request (this is your view and url-config)
There is absolutely nothing "hacky" about this.
The third thing, your template file, is optional - but is generally good practice. You want to separate your markup from the code, for many reasons.
So I think you've got the right idea. Carry on.

What exactly seems hacky about it? Seems like a perfectly valid way of doing something.
I guess an alternative would be serialising to json and sending it back to a javascript templating snippet.

Related

Django and HTMX I am looking for not "hacky" way to update class of link that calls HTMX request. Like "refresh-self" or something along those lines

I have a problem with HTMX in Django. I basically have two important components on page. List of categories and content that is being shown after you click on category.
I was working nicely with just standard htmx "out of the box". But I started having problems when I wanted to add active css class on category link after you click it (to show user where he is currently).
I did a lot of experiments with hx-swap-oob and hx-swap but the only thing that work was this:
(it is the most relevant part of the code)
<div class="col-sm-4">
<div class="card">
<div class="card-body" hx-boost="true" hx-target="#manual_results">
<div id="manual_categories">
{% include 'partials/manual_categories.html' %}
</div>
</div>
</div>
</div>
<div class="col-sm-8">
<div id="manual_results">
{% include 'partials/manual_entries_list.html' %}
</div>
</div>
and in manual_entries_list.html:
<some html results>
<div id="manual_categories" hx-swap-oob="true">
{% include 'partials/manual_categories.html' %}
</div>
Each category has simple if statement in django template code that is checking if it is selected (based on url path.)
And it is working, thing is, on the first request the categories are rendered twice (which is logical since I have 2 includes on the same HTML). After I select one category, everything goes back to normal because HTMX "starts to understand what is happening" and it switches the categories from manual_entries_list.html into our main page.
And like I said it works, I modified manual_entries_list.html to:
<some html results>
<div class="set_size_to_0px">
<div id="manual_categories" hx-swap-oob="true">
{% include 'partials/manual_categories.html' %}
</div>
</div>
So it is always invisible (so I will have only one set of categories visible).
The thing is, that it is an obvious hack. I am sure that there needs to be a better way of solving this problem but I cannot find it.
(I even tried to do it with plain javascript the thing is that categories are rendered in a for loop so it is pretty much impossible to get IDs correctly etc.)
Could someone please help me?
The easiest way to avoid this issue is to detect the HTMX request in the view function, pass this state to your templates and render HTMX content only if needed. HTMX will add a HX-Request: true header to each request.
For the detection you can use the Django-HTMX package that provides a request.htmx variable in your view functions that will be True if the request is coming from HTMX. Or if you want to check it manually:
def my_view(request):
is_htmx = request.headers.get('HX-Request') == 'true'
return render(request, 'my_template.html', {'is_htmx': is_htmx})
After that in manual_entries_list.html template include HTMX related stuff only in the HTMX requests:
<some html results>
{% if is_htmx %}
<div id="manual_categories" hx-swap-oob="true">
{% include 'partials/manual_categories.html' %}
</div>
{% endif %}

Flask - Component templates structure?

I've been experimenting with a Flask template structure to resemble a component workflow. This is inspired by Vue.
Some key ideas :
Each page is treated as a component
A component is just a folder containing all necessary files inside it (except ones that are common)
Each component is encourageg to have several HTML files to avoid large files, we will be using Jinja's include to "bundle" them
togheter
Avoid javascript, or at least, loading unnecessary javascript, each page will include only what's necessary
Here's an example :
The route for PageExample would be the following :
app.route('/example'):
return render_template('PageExample/html.html')
And PageExample/html.html :
{% extends 'base.html' %}
{% block content %}
<div class="tab" id="tab1">
{% include 'PageExample/tab1.html' %}
</div>
<div class="tab" id="tab2">
{% include 'PageExample/tab2.html' %}
</div>
{% endblock %}
PageExample/tab1.html (tab2 follows the same logic)
<!-- block header is in base.html -->
{% block header %}
{{ super() }}
<script src="{{url_for('components', 'PageExample/tab1.js')}}">
{% endblock %}
<div class="tab-content">
Some content here that uses the tab1.js javascript
</div>
So, what's the question ?
I have limited experience with Flask, so I'm not sure if this idea is sound, it seems hacky but it makes developing pages easier, especially with multiple people (the little javascript a page might need can be done in any way). Also increased loading speed by a ton.
Dos this work? Is this structure reccomended? Are there any flaws to it? What sort of problems could I run into? Any recommendation to do it better? Is this already done nowadays in Flask? I couldn't find any examples of a Flask app done this way (specially the Jinja stuff using lots of includes)
I'm currently not at my main computer, so I'll update this later if need be, but if your included html files referenced any data that is stored in the backend then you'd also need to make sure you reference the exact location there as well.
For example:
If you need the first name property from a User class it would be
{{ User.firstName }}
Not only that, but the tab template will also need to be supplied this User.firstName variable, which isn't done implicitly.
You will need to create what is called a Blueprint then a View and tell the view to make the User.firstName variable available to PageExample/tab1.html
tabs.py
import User
from flask import Blueprint, render_template
import """other flask dependencies needed"""
bp = Blueprint('index', __name__, url_prefix='/')
def tabData():
"""data to process"""
render_template("PageExample/tab1.html", User=User)
In that same tabs.py file, you can also write a view for tab2.html as well as html.html. Should be noted that I'm assuming that the contents of PageExample are for the index of the website and that they will all be rendered on that same page.
Also, It seems like your tabs will be in the content/body of the html but it will also render the blocks of base.html and add the defined scripts inline with the body of html.html. Instead, it should be referenced in the html.html file itself
html.html
{% extends 'base.html' %}
{% block header %}
{{ super() }}
<script src="{{url_for('components', 'PageExample/tab1.js')}}">
{% endblock %}
{% block content %}
<div class="tab" id="tab1">
{% include 'PageExample/tab1.html' %}
</div>
<div class="tab" id="tab2">
{% include 'PageExample/tab2.html' %}
</div>
{% endblock %}
with tab1.html being
<div class="tab-content">
Some content here that uses the tab1.js javascript
</div>
Obviously, this is just the surface of what you will need to know about flask. Think the bottom line is that it can work. Although I would suggest that your put all resources such as JS and CSS in a separate folder and reference those resources from those locations.
For more information on Flask and how you can utilise it, check out the Flask Tutorial here:
http://flask.pocoo.org/docs/1.0/tutorial/

Catch a string and transmit it to a Javascript function on button click

Below is Django template language syntax. But that doesn't matter.
{{ }} means something transmitted from the framework.
In the backend I have two related models. That may be person and their addresses. One-to-many relationship. So, one person has many addresses.
Well, in the page with detail information about the person I render a list of their addresses. And for each address on the right are two buttons: edit and delete. Update via AJAX of course.
How to edit and delete is a mystery to me. Let's focus on edit as an example.
This is part of detail information about the model.
<ul id="{{ related_model_name }}_ul" class="update">
{% for object in object_list %}
<li>{{ object }}
Edit
</li>
{% endfor %}
</ul>
What do we have here:
1. {{ object }} will render just a string. Like "Buckingham Palace
London SW1A 1AA". So, it is not clickable, but "Edit" is clickable.
The framework can generate update url for this object like this {{ object.get_update_url }}, which will result in something like 'person/12/update_address/'. I would like to catch this update url via Javascript on click event. And then I will be able to transmit it via AJAX to the server.
Then I am going to act like this:
function update_address(url){
$.ajax({
method: 'get',
url: url,
success: show_address_form_get,
error: fail
});
}
function handle_update_buttons(){
var update_button_list = $(".update");
$.each(update_button_list, function assign_click(){
this.click(update_date);
})
}
Well, this is my rough idea. A newbie in web development. Maybe there is a simpler way.
Could you give me a hint how to transmit for each address its update url to the update_date function on clicking the corresponding Edit button.
You might want to assign a data-attribute, like data-updateUrl to the link:
<ul id="{{ related_model_name }}_ul" class="update">
{% for object in object_list %}
<li>{{ object }}
Edit
</li>
{% endfor %}
</ul>
You can then take that attribute to your function (in your click handler)
function clicked(el) {
var updateUrl = el.target.getAttribute('data-updateUrl');
}

Dynamic content instead of constant reloads

On our online fashion store http://www.showstyle.lu I am the webmaster trying to improve the user experience perspective. I finally managed to use a toggle tool for our filters (click on any category: e.g. Homme) as you can see if you click on Couleurs/Tailles etc all the filters appear (they used to be displayed permanently which didn't look aesthethic).
Now, the issue is upon selection of filters the page is then reloaded with a new url to filter the products. Each time the filters then toggle back up of course and it's redundant to have to reclick each one and see what filter's one chose.
Is there a way I can either make the center content container refresh like php where the rest of the site remains static? Or otherwise can I at least force the toggle to remain open if the url has "%filter%*****" in it ?
We are using a ecommerce platform called SEOshop so I don't have access to every script/index page on the website but I have access to a large part.
The filters work on generation from backend so in the html code the placeholders are simply:
{% for filter in collection.filters.custom %} <a class="filtertitle" href="javascript:;">
<p>{{ filter.title }}</p>
</a>
{% for value in filter.values %}
<div class="filterbox2">
<div class="sidebar-filter-item clearfix">
<input id="filter_{{ value.id }}" type="checkbox" name="filter[]" value="{{ value.id }}" {% if value.active %} checked="checked"{% endif %} />
<label for="filter_{{ value.id }}">{{ value.title }}{% if not value.active and value.has_count %} <span>({{ value.count }})</span>{% endif %}</label>
</div>
</div>
{% endfor %}
{% endfor %}
</form>
</div>
Another topic, is it possible to have the toggle open a popup instead? Something to look like this: https://dl.dropboxusercontent.com/u/5636466/showstyle/mockup_filters.png
Thanks for any help, I will continue my research and post my attempts/anything I find :)
If you want to reload only a specific part of the website then there is no way to do it only with php. What you are trying to do will require ajax requests to load the data without reloading the page and javascript to determine where it should be displayed (I would recommend looking into a framework like angular for that). I don't know anything about your site backend but I'm guessing this is not a trivial change and will require you to learn about such things.

Passing RSS feeds to django view using AJAX

I'm pretty new to ajax.Following is my use case :
I am developing a side project that will show news and scores of different sports. For performing same, I am using rss feeds from different sources. I am able to fetch and parse the rss feeds in django views using 'feedparser' plugin, and passing the same as context to the corresponding template. Following is my view function and template(for cricket).
def cricket(request):
feeds_cric = feedparser.parse('http://www.espncricinfo.com/rss/content/story/feeds/0.xml') #espncricinfo feed.
feeds_cric_scores = feedparser.parse('http://static.cricinfo.com/rss/livescores.xml') #espncricinfo feed.
context = {'feeds_cric': feeds_cric,'feeds_cric_scores' : feeds_cric_scores}
return render(request,'scorecenter/cricket.html', context)
Following is the corresponding template.
{% extends "scorecenter/index.html" %}
{% block content %}
<ul>
{% for entry in feeds_cric.entries %}
<li>{{ entry.title }}</li>
<p>{{ entry.description }}</p>
{% endfor %}
</ul>
{% endblock content %}
{% block score %}
<ul>
{% for entry in feeds_cric_scores.entries %}
<li>{{ entry.title }}</li>
<!-- <p>{{ entry.description }}</p> -->
{% endfor %}
</ul>
{% endblock score %}
Following is index.html file.
Now, I want to use AJAX to reload only certain blocks of my page, rather than reloading and redirecting the complete page. I have written a script to check the
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#">Allscores</a>
</div>
<div>
<ul class="nav navbar-nav navbar-right">
<li>Cricket</li>
<li>Football</li>
<li>Basketball</li>
<li>Tennis</li>
</ul>
</div>
</div>
</nav>
<div class="navbar navbar-default" id="empty_nav"></div>
<div class="testingData">This should be updated</div>
<div class="row">
<div class="col-sm-8" id="news">
<h3>Latest News</h3>
{% block content %}
{% endblock content %}
</div>
<div class="col-sm-4" id="scores">
<h3>Latest scores</h3>
{% block score %}
{% endblock score %}
</div>
</div>
<div id="footer">
Copyright © acllscores.com
</div>
</body>
In my urlChecker() script, I check which anchor tag is clicked and assign the corresponding url. Following is the script :
function urlChecker (route) {
switch(route) {
case 'cricket':
$.post( "/scorecenter/cricket/", function( data ) {
$( ".result" ).html( data );
});
break;
case 'football':
$.post( "/scorecenter/football/", function( data ) {
$( ".testingData" ).html( data );
});
break;
case 'tennis':
$.post( "/scorecenter/tennis/", function( data ) {
$( ".testingData" ).html( data );
});
break;
case 'basketball':
$.post( "/scorecenter/basketball/", function( data ) {
$( ".testingData" ).html( data );
});
break;
}
}
Update :
Adding urls file :
urlpatterns = patterns('',
url(r'^$', views.index,name='index'),
url(r'^test/$', views.test,name='test'),
url(r'^cricket/$', views.cricket,name='cricket'),
url(r'^basketball/$', views.basketball,name='basketball'),
url(r'^football/$', views.football,name='football'),
url(r'^tennis/$', views.tennis,name='tennis'),
)
I am not fathom how to post the feed data to my templates using ajax.
Please help
From the Ajax point of view
From what you write in the question it would be more appropriate to send a GET request rather than a POST request. You are not sending any information to these endpoints that they need to store, but only want to retrieve updates from them. This doesn't change much of your code, you can do that with ☞ $.get() instead of $.post().
From the Django point of view
Sending a GET request is also easier, since you don't need to account for ☞ Django's CSRF protection. Issuing other ajax requests (like POST, DELETE, etc.) isn't all that hard either, but it requires an extra step that involves sending the CSRF token with every request (well documented in the linked docs).
Rendering the result from the Ajax request
Currently there might be a conceptual problem with the template that you use to render the Ajax results, e.g. scorecenter/cricket.html. On the very top you are extending from index.html, which means that each request will not only return the block of html that you want to insert but also anything that's part of index.html.
Also, since you will insert the results by Javascript into the already loaded website you don't need to define template blocks. Let these templates only render exactly the html that you want to insert into the DOM with $('.testingData').html(data).
Debugging problems with Ajax requests
While developing your site, open the developer tools in your browser and activate the console there (either Firefox and Chrome are fine). Whenever an Ajax-request fails, it will display an error there, which you can then introspect.
Common errors are 404 (if the endpoint couldn't be found), 400 or 403 if the request was not authorized or 500 if the server has an error processing your request. ☞ More complete list of status codes
Feel free to update your question with more details. Since this is a very wide topic be prepared that it might be closed by the community. In that case you can always search for more specific questions or if you don't find answers, put them in a new question.

Categories

Resources