21
votes

I'm learning Django and I found class-based views and I wonder how to implement Ajax on those views.

I searched github for a django project and I found some using class-based views but not ajax.

So... Anybody knows an open source project that use both things? It easier to learn that way.

Thank you :)

3
I would recommend that you deal with them separately. It's much eaiser to work with AJAX if you make the site work without it, then define some JSON interfaces, and then add some AJAX to make it nicer/faster.SystemParadox
Well, that's the idea in every framework, make it work without ajax and then, ajax it. Is that what you mean? :)Jesus Rodriguez
I think he means to separate your Ajax views altogether i.e. have a set of views to receive normal requests and a separate set of views to deal with ajax requests (maybe in the form of an api)Timmy O'Mahony
Well, as an ex asp.net mvc developer, I had actions for normal views and actions just for ajax (that returns json stuff).Jesus Rodriguez

3 Answers

11
votes

An ajax view isn't much different to a normal view except that you usually want to return a different format then when processing a normal request. This format is usually JSON.

The documentation has an example of a mixin that can be used to return JSON, so this is a good starting point:

https://docs.djangoproject.com/en/dev/topics/class-based-views/mixins/#more-than-just-html

Do you want your view to reply to normal requests or only deal with AJAX requests? If the former, the only trick would be to write in a small check in the render_to_response method to reject any normal GET requests. If the latter, the above link goes on to discuss a situation where you can create a view that will deal with ajax requests and with normal requests.

13
votes

without using the popular dajaxic and dajax packages, its still a straightforward affair.

It would help to write a decorator that just wraps around django's is_ajax() function for request objects like so:

def ajax_request(function):
    def wrapper(request, *args, **kwargs):
        if not request.is_ajax():
            return render_to_response('error/ajax_required.html', {},
                context_instance=RequestContext(request))
        else:
            return function(request, *args, **kwargs)
    return wrapper

assuming there is a template called ajax_required to handle this particular failure. Something like this prevents a user from entering your ajax specific url in the browser if thats what you don't want.

Because it makes for a shorter example, the following is a class based ajax view that renders a template.

from django.views.generic.base import TemplateView

class AjaxGeneral(TemplateView):
    template_name= None
    def get(self, request):
        data={}
        return render_to_response(self.template_name, data,
            context_instance=RequestContext(request))

    @method_decorator(ajax_request)
    def dispatch(self, *args, **kwargs):
        return super(AjaxGeneral, self).dispatch(*args, **kwargs)

now for everything ajax that just needs to render an html snippet you can define short class based views like:

class ShowSomeTable(AjaxGeneral):
    template_name="some_table.html"

Assuming some_table.html has some html snippet in it.

Now your urls.py entry for this view will look like:

url(r'showtable/$', ShowSomeTable.as_view()),

and you can call it in the js as normal like:

$(#dynamic-content).load('/showtable');
5
votes

If you want to support both AJAX and traditional views, you can add something like this to your CreateView:

# at top of file
from django.http import JSONResponse  

# inside CreateView class
def render_to_response(self, context, **response_kwargs):
    """ Allow AJAX requests to be handled more gracefully """
    if self.request.is_ajax():
        return JSONResponse('Success',safe=False, **response_kwargs)
    else:
        return super(CreateView,self).render_to_response(context, **response_kwargs)

This handles regular views normally (with redirect etc.) but for AJAX, it return a JSONResponse instead. Where it returns 'Success', you may want to add more sophisticated logic but this is the basic idea.