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.
Related
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 %}
I'm very new to python and django framework. I'm facing a minor issue of not being able to assign a variable to my for loop.
In my html file there are buttons and a list (rendered using a loop).
for ex:
Buttons
<li><a class="layer-item" href="#" onclick="selectLayer('base')" title="Base layer">Base layer</a></li>
<li><a class="layer-item" href="#" onclick="selectLayer('soil')" title="Soil layers">Soil layers</a></li>
Inside script tags i'm updating the variable value as
<script>
var layerType = "";
function selectLayer(layer){
layerType = layer;
}
</script>
Also i have a loop like following in the same html file
{% for layer in base %}
<div class="col-4">
<span class="base-layer-title d-block">{{layer.title}}</span>
</div>
{% endfor %}
Here i want to replace base according the button clicked.
For ex:
<a class="layer-item" href="#" onclick="selectLayer('soil')" title="Soil layers">Soil layers</a>
Clicking on the above button should make the for loop as
{% for layer in soil %}
<div class="col-4">
<span class="base-layer-title d-block">{{layer.title}}</span>
</div>
{% endfor %}
From i read you can do something like this to assign a value to a variable.
{% with name="World" %} But not sure how to implement it in my issue.
Any help is appreciated
Also i have a loop like following in the same html file
{% for layer in base %}
{{layer.title}} {% endfor %}
Here i want to replace base according the button clicked.
In short, Django is a server-side framework and it cannot directly respond to any client-side activity. You cannot do it effectively without client-side libraries like React, JQuery, etc.
Workaround 1
You can make a new request to the server on click and re-render the entire page with new parameters.
urls.py
path('my_pattern/<slug:my_layer>', myview)
views.py
def myView(request, my_layer):
# your logics
base = get_layer_data(my_layer) # <-- logic to get list dynamically
return render(request, 'my_template.html', context={'base':base})
my_template.html
{% for layer in base %}
<div class="col-4">
<span class="base-layer-title d-block">{{layer.title}}</span>
</div>
{% endfor %}
...
<a class="layer-item" href="/my_pattern/soil" title="Soil layers">Soil layers</a>
<a class="layer-item" href="/my_pattern/some_other_layer" title="Some other layers">Soil layers</a>
...
The base will have dynamically generated data on every request.
Workaround 2
You can, however, render all the data on the page and control it using collapse in bootstrap https://getbootstrap.com/docs/4.5/components/collapse/
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/
On my django app I display a list of the user's facebook friend, using django-facebook.
The friends are sorted depending on there "activity" (if they are active or not).
I would like to refresh this list on my page without refreshing the whole page.
Here is my view:
class fb_friend(request)
if request.user.userprofile.facebook_id:
# Get Friends
profile = request.user.get_profile()
idle_friends = profile.get_idle_facebook_friends()
active_friends = profile.get_active_facebook_friends()
offline_friends = profile.get_inactive_facebook_friends()
friend_groups = (active_friends, idle_friends, offline_friends)
In my template:
<div>
{% for group in friend_groups %}
{% for friend in group %}
<p>{{ friend.name }} ({{ friend.online_presence }})</p>
{% endfor %}
{% endfor %}
</div>
My idea would be to put the content of my view in an other view, and then use a JQuery request with a setInterval.
But the problem is that I only know how to get one element of a page with a JQuery request.
So my question is how can I get the whole div with a JQuery get request and display it in my template?
Thank you for your help!
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.