Version 10 (modified by 17 years ago) ( diff ) | ,
---|
KSS in Django with kss.django application
What is KSS?
KSS means Kinetic Style Sheets. A beautiful AJAX framework.
A KSS definition taked from KSS project site:
KSS is a javascript framework that aims to allow Ajax development without javascript. It uses stylesheets with CSS-compliant syntax to setup behaviours in the client and a set of well-defined commands that are marshalled back from the server to manipulate the DOM.
KSS was designed by the developers for making an AJAX UI's. The main concern was to get AJAX features but without losing accesibility.
The goals of KSS are:
- To have a framework for javascript in a declarative way but without using helpers that been attached with an specific technology.
- Don't lose accesibility.
- To do things in a non intrusive way. You don't change HTML for getting an accesibility version. It's the same version for all.
A brief introduction to KSS. How KSS works
In server side, KSS load all the plugins your application has configured (i.e. Scriptaculous). In Python code, you load plugins like this:
from kss.base import load_plugins load_plugins(['kss-core', 'django', 'scriptaculous-effects'])
This activates the selected plugins. Activation does the following:
- Load all command sets (later explain what
command set
means for KSS).
- Makes the javascripts files needed for the plugin in HTML page available to the system.
In client side, there is a kss.js
Javascript file that is a concatination of the available plugins. This Javascript loads a KSS file, put in a line on HTML source like:
<link rel="kinetic-stylesheet" href="/site_media/kss/wiki.kss" type="text/kss" />
After this it binds all the events to the matching nodes. It also sets up the server actions in a declarative way, see the example below:
.page-link a:click{ evt-click-preventdefault: True; action-server: ajax/view; ajax/view-title: nodeContent(); }
The lines above mean that if the user clicks on a link element like <span class="page-link"><a href="wiki/view?title=WikiWord">WikiWord</a></span>
, KSS will execute the previous fragment (like CSS selector does) and will:
- Override
href
argument, and send an AJAX request to URL defined in
action-server
KSS attribute. In this case the link goes to
ajax/view
, but without refreshing the page.
- Pass a HTTP parameter (
POST
by default) named
title
(as defined in
ajax/view-title
) with the value taken from the content of the node (
WikiWord
in this case). In this case it is like
ajax/view?title=WikiWord
request, but in aPOST
method.
- The server action takes the AJAX request and returns KSS commands to the browser. Commands are XML fragments that do things like (I explain better later):
- replace breadcrumbs with
Home / News / Foo News item
- do a scriptaculous effect in top of screen
- replace main content with the new content i am seeing
- replace breadcrumbs with
The first step is very important for accesibility. If you have javascript disabled and the users clicks he would go to wiki/view?title=WikiWord
, and it works perfectly both with or without javascript.
The last thing to explain is how to send KSS commands from server side. Normal code at server side (i.e. a django view) could be something like this:
from kss.base import KSSCommands from kss.base.selectors import css def a_django_view(request): commands = KSSCommands() commands.core.replaceInnerHTML(css('div.content'), '<h1>Hello world</h1>') commands.scriptaculous.effect(css('div.message'), 'blinddown') commands.scriptaculous.effect(css('div.message'), 'blindup', delay=2) return HttpResponse(commands.render(), mimetype='text/xml')
This returns a XML code that is caught in client side by KSS javascript, and then he execute this commands in the browser.
More on KSS website.
KSS in django
At Plone Conference 2007 I developed kss.django, a django application for accelerating KSS development with django.
Django is a perfect mix with KSS due to features like templatetags
,
url mappings
,
settings
and so on.
Demo site
I created this demo site for show KSS features and a real site that uses kss.django
. Try enter first with javascrip enabled and later disabled.
You also can take a look to the source code
Installing kss.django
$ bzr checkout http://bazaar.launchpad.net/~kissbooth/kss.django/trunk/ kss.django $ cd kss.django $ python setup.py install ---> [or python setup.py develop ]
KSSing your website with kss.django application
I will use the coolwiki project code as an example of KSSing a web site.
Register kss.django application in your project
Register kss.django
application in your
settings.py
:
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'coolwiki.wiki', 'kss.django', # KSS django application )
Setup your KSS plugins
If you want to use scriptaculous effects, or other plugins, you can put on KSS_EXTRA_PLUGINS
settings parameter, like that:
KSS_EXTRA_PLUGINS = ['scriptaculous-effects']
URL configuration
One of the main goals for kss.django
application was the advocacy of using exactly the same django views for Javascript version and classic version of web sites.
First of all, in your root
urls.py
, you have to put this:
urlpatterns = patterns('', (r'^kss/', include('kss.django.urls')), ... )
This line is needed for automatically loading of all the KSS Javascripts.
Later, in your application, you must change your URLs. For example I put the URLs like below for my coolwiki
demosite:
from wiki import views urlpatterns = patterns('', (r'^$', views.index), (r'^view$', views.view), (r'^edit$', views.edit), (r'^save$', views.save), (r'^history$', views.history), (r'^ajax/view$', views.view, {'is_kss': True}), (r'^ajax/edit$', views.edit, {'is_kss': True}), (r'^ajax/save$', views.save, {'is_kss': True}), (r'^ajax/history$', views.history, {'is_kss': True}), )
As you can see, It is using the same view for the AJAX version and the normal version in every URL. All the URLs begin with wiki/...
are standard, but the
ajax/...
the URLs go to the same view in KSS version (with
is_kss
parameter enabled).
What decides the URL is that the user goes to when he clicks in a wiki word? The answer is: KSS. In your template, you put some HTML code like this:
This is a <span class="page-link"><a href="/wiki/view?title=WikiPage">WikiPage</a></span>.
And you put this in your KSS file:
.page-link a:click{ evt-click-preventdefault: True; action-server: ajax/view; ajax/view-title: nodeContent(); }
If you have Javascript disabled, the link you click goes to /wiki/view?title=WikiPage
, that pass to wiki.views.view
with
is_kss=False
. But if you have enabled Javascript, KSS will do an Ajax request to
/ajax/view
with a
title
HTTP parameter with
WikiPage
value. The views will change the wiki content but without returning a normal
HttpResponse
.
Warning: KSS does not yet support for dynamic URL actions, that is needed for to get RESTful URLs in your application. For example, in the wiki demo site, I have to change the /wiki/WikiWord/view
URL format to a /wiki/view?title=WikiWord
format, to get working both Javascript and non Javascript version with exactly the same code in the django views.
KSSing django views The low level way to use a django view with KSS is this:
def view(request, is_kss=False): page_name = request.REQUEST['title'] # it take the wiki word page = Page.objects.get(name=page_name) if is_kss: commands = KSSCommands() commands.core.replaceInnerHTML(css('div.content'), page.cooked_content) return HttpResponse(commands.render(), mimetype='text/xml') else: return render_to_response('wiki/view.html', {'page': page})
It is valid and clean way to get AJAX rendering and also works with Javascript disabled.
For the django templates, kss.django
has several templatetags that helps your development. One of those is
include_kssjs
. It looks for the plugins you have installed and puts all of your
<script type="text/javascript" src="..." />
tags in automatically.
The wiki/view.html
may look like this:
<html> <head> {% load ksslib %} {% include_kssjs %} </head> <div class="content"> </div> </html>
But often you have a more complex template with a lot of rendering thing and you don't want to do the rendering by hand. For example, if you have this template:
<html> <head> {% load ksslib %} {% include_kssjs %} </head> <div class="content"> <h1>{{ page.name }}</h1> <div> {{ page.cooked_content }} <table> <tr><td>Version:</td><td>{{ page.version }}</td></tr> </table> </div> </div> </html>
If you want to change all div.content
fragment, you must to do ugly things like this:
def view(request, is_kss=False): ... tpl = Template('''<h1>{{ page.name }}</h1> <div> {{ page.cooked_content }} <table> <tr><td>Version:</td><td>{{ page.version }}</td></tr> </table> </div>''') ctx = Context({'page': page}) if is_kss: commands.core.replaceInnerHTML(css('div.content'), tpl.render(ctx)) ...
To avoid this ugly example and to keep with the DRY principle, kss.django has a ksswidget
templatetag, and several generic views like
render_widget
. The template becomes:
<html> <head> {% load ksslib %} {% include_kssjs %} </head> <div class="content"> {% ksswidget main %} <h1>{{ page.name }}</h1> <div> {{ page.cooked_content }} <table> <tr><td>Version:</td><td>{{ page.version }}</td></tr> </table> </div> {% endksswidget %} </div> {% ksswidget footer %} You are on {{ page.name }} {% endksswidget %} </html>
And the views becomes:
def view(request, is_kss=False): page_name = request.REQUEST['title'] # it take the wiki word page = Page.objects.get(name=page_name) if is_kss: commands = KSSCommands() commands.django.replace_widgets('wiki/view.html', ['main', 'footer'], {'page': page}) return HttpResponse(commands.render(), mimetype='text/xml') else: return render_to_response('wiki/view.html', {'page': page})
commands.django
is a command set that is registered in KSS when
django.kss
initializes.
replace_widgets
is a KSS command that renders the ksswidget nodes and returns the HTML rendered as a KSS action. The effect is that both the main content and footer are updated in the page, but without refreshing the page.
There is a shortcut for avoid the return return HttpResponse(commands.render(), mimetype='text/xml')
. Is the
kss_response
method. The code becomes:
from kss.django.render import kss_response def view(request, is_kss=False): page_name = request.REQUEST['title'] # it take the wiki word page = Page.objects.get(name=page_name) if is_kss: commands = KSSCommands() commands.django.replace_widgets('wiki/view.html', ['main', 'footer'], {'page': page}) return kss_response(commands) else: return render_to_response('wiki/view.html', {'page': page})
There is also a shortcut for this, the render_kss_response
generic view. By using this the view code can be reduced to:
from kss.django.render import render_kss_response def view(request, is_kss=False): page_name = request.REQUEST['title'] # it take the wiki word page = Page.objects.get(name=page_name) commands = KSSCommands() return render_kss_response('wiki/view.html', {'page': page}, is_kss, ksswidgets=['main', 'footer'], commands=commands)
The render_kss_response
will do the
if is_kss: ... else: ...
stuff for us. Remember that this small amount of code works with Javascript and without.
If you want to add some extra effects to the ksswidget rendering, you can also create KSSCommands
and pass it to the
render_kss_response
, like this:
from kss.django.render import render_kss_response def view(request, is_kss=False): page_name = request.REQUEST['title'] # it take the wiki word page = Page.objects.get(name=page_name) commands = KSSCommands() if is_kss: commands.scriptaculous.effect(css('div.message'), 'blinddown') commands.scriptaculous.effect(css('div.message'), 'blindup', delay=2) return render_kss_response('wiki/view.html', {'page': page}, is_kss, ksswidgets=['main', 'footer'], commands=commands))