4

I am trying to refresh a certain part of my page with AJAX and JQuery (in Django). How can I get it to redisplay only the div, rather than the whole page.

    // In my template
    var tag_cloud_floor = function(floor) {
    $.ajax({ url: "/{{ user }}/{{ tag }}/",
                     data: {tag_cloud_floor: floor},
                     type: 'POST',
                     success: function(data) {
                         $('#tag_cloud).html(data);
                     },
    });

};

Here is my view.

@login_required
def tag_page(request, username, tag):
  if username == request.user.username:
    tags = request.user.userprofile.tag_set.all()

    if request.is_ajax() and request.POST:
      floored_tags = []
      for t in tags:
        if t.item_set.all().count() >= int(request.POST['tag_cloud_floor']):
          floored_tags.append(t)
      tags = floored_tags

    tag = Tag.objects.get(title=tag)
    items = tag.item_set.all()
    return render_to_response("tag_page.html", { 'user': request.user , 
                                              'tag': tag,
                                             'tags': tags,
                                            'items': items })
  else:
  return HttpResponseRedirect('/' + request.user.username + '/')

Currently, it places the entire html page into the #tag_page div. I want it to replace the old #tag_page div with the new #tag_page div. If I replace $('#tag_cloud').html(data); with $('body').html(data); it refreshes the whole page to how it should be, but I figure refreshing the whole page is a waste.

If there is a better way to do this, let me know.

Josh
  • 4,427
  • 5
  • 28
  • 27
  • also, is it possible to separate the ajax work to a different view than the view that does the initial page load? – Josh Nov 18 '10 at 18:09
  • 1
    Of course you can separate the functionality out; create a separate view with a separate URL, and have that view return just the tags data. Now, whether you return it in JSON and reconstruct the tags on the page, or return it as an HTML fragment and simply insert that into the page, is up to you. If you're planning on a lot of AJAX on your site, check out [django-piston](http://bitbucket.org/jespern/django-piston/wiki/Home) – eternicode Nov 19 '10 at 20:06

3 Answers3

12

Use load:

$('#tag_cloud').load(' #tag_cloud')

(Note the leading space in the load parameter; this specifies an empty string [which evaluates to the current page] as the source, and "#tag_cloud" as the fragment to load)

This will actually load #tag_cloud (including its outer html) into #tag_cloud, so you'll essentially get the following structure in your DOM:

<div id="tag_cloud">
    <div id="tag_cloud">
        <span>tag</span> ...

To fix this, just unwrap the children:

$('#tag_cloud').load(' #tag_cloud', function(){$(this).children().unwrap()})

Oh, by the way... you can try this here on SO! Just paste the following into your Firebug console, and watch the sidebar auto-reload. You'll notice some scripting code poking through; that's jQ's HTML-safety filters disabling the <script> elements for you (which can be annoying at times).

$('#sidebar').load(' #sidebar', function(){$(this).children().unwrap()})
eternicode
  • 6,805
  • 4
  • 33
  • 39
  • 1
    First I downvoted this answer, then I checked [load() docs](http://api.jquery.com/load/), noticed the "Loading Page Fragments" section and reversed the vote :-) Nice stuff, although it unnecesserily regenerates the whole page. – Tomasz Zieliński Nov 19 '10 at 19:52
  • @Tomasz well, thank you for reading the docs, then! And yeah, I would normally use piston (or even just a simple view that returns the data in json) for interactions like this, but OP seemed to be trying to do what `load` was meant for. – eternicode Nov 19 '10 at 20:05
9

First, it looks like your code is broken - this is not a valid Javascript/jQuery instruction:

$('#tag_cloud).html(data);

You need to add the missing quote:

$('#tag_cloud').html(data);

As for refreshing only a single div, I would extract contents of that div to a separate template my_div.html, include it in the main page template using {% include "my_div.html" %}. Then, in my AJAX view, I would render and return only that rendered my_div.html.

Flimm
  • 136,138
  • 45
  • 251
  • 267
Tomasz Zieliński
  • 16,136
  • 7
  • 59
  • 83
  • $('#tag_cloud').html(data); is in fact a valid function (i just forgot the second quote). i ended up keeping that, including a second file, and passing data that file. – Josh Nov 19 '10 at 19:18
  • @Josh: Yes, and I meant the missing quote. – Tomasz Zieliński Nov 19 '10 at 19:53
  • How do we do this could you point me to some link where I could learn this I have a table in a seperate file I want to update that based on user button click from the database. – Sohaib Jul 01 '13 at 16:04
  • @TomaszZieliński your answer is great but can you more explain it I'm new in Django. – Maninder Singh Apr 26 '20 at 17:30
3

In our project, we had too many separate Ajax views, that it made sense to write a middleware and reuse the same views. I recently created an application that lets the client request parts of any page.

Let's say, a page /usual_page/ has this template:

{% extends "another_template.html" %}
{% block title %}Usual page{% endblock %}
{% block content %}...{% endblock %}
{% block my_widget %}widget code{% endblock %}

If you install the middleware and make a GET request like /usual_page/?partial=my_widget, it will send the client this data:

{"blocks": {"my_widget": "widget code"}}

Or, if you do /usual_page/?partial=title,content, it will send the following:

{"blocks": {"title": "Usual page", "content": "..."}}

Other blocks, that are in the parent template, can also be requested. It doesn't matter where the block is in the hierarchy, it only matters that it is present in the normal page (the one without ?partial parameter).

In the BitBucket repo, I have a demo project that has a module that automates the work. It lets you use History.pushState trick and load parts of pages and insert them into the document.

culebrón
  • 34,265
  • 20
  • 72
  • 110
  • This is very smart. I was going to write something similar myself. But I see this answer is from 2011, does Django currently have anything similar built in or do we still need this solution? – Tiago Espinha Aug 27 '15 at 18:32