Changes between Initial Version and Version 1 of KSSInDjango


Ignore:
Timestamp:
Oct 15, 2007, 8:49:31 AM (17 years ago)
Author:
Manuel Saelices
Comment:

A first version of KSS in django tutorial

Legend:

Unmodified
Added
Removed
Modified
  • KSSInDjango

    v1 v1  
     1[[PageOutline]]
     2
     3= KSS in Django with kss.django application =
     4
     5== What is KSS? ==
     6
     7Taked from [http://kssproject.org/ KSS project site]:
     8
     9''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.''
     10
     11KSS was designed by the developers for making an AJAX UI's. The main concern was to get AJAX features but '''without losing accesibility'''.
     12
     13The goals of KSS are:
     14 * To have a framework for javascript in a declarative way but without using helpers that been attached with an specific technology.
     15 * Don't lose accesibility.
     16 * 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.
     17
     18
     19== A brief introduction to KSS. How KSS works ==
     20
     21In '''server side''', KSS load all the plugins your application has configured (i.e. Scriptaculous). In Python code, you load plugins like this:
     22{{{
     23#!python
     24from kss.base import load_plugins
     25
     26load_plugins(['kss-core', 'django', 'scriptaculous-effects'])
     27}}}
     28
     29This activates the selected plugins. Activation does the following:
     30   1. Load all command sets (later explain what ``command set`` means for KSS).
     31   1. Makes the javascripts files needed for the plugin in HTML page available to the system.
     32
     33In '''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:
     34
     35{{{
     36<link rel="kinetic-stylesheet" href="/site_media/kss/wiki.kss" type="text/kss" />
     37}}}
     38
     39After 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:
     40
     41{{{
     42#!css
     43.page-link a:click{
     44  evt-click-preventdefault: True;
     45  action-server: ajax/view;
     46  ajax/view-title: nodeContent();
     47}
     48}}}
     49
     50The 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:
     51 1. 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.
     52 1. 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 a ``POST`` method.
     53 1. 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):
     54   * ''replace breadcrumbs with ``Home / News / Foo News item``''
     55   * ''do a scriptaculous effect in top of screen''
     56   * ''replace main content with the new content i am seeing''
     57
     58The 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.
     59
     60The 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:
     61{{{
     62#!python
     63from kss.base import KSSCommands
     64from kss.base.selectors import css
     65
     66def a_django_view(request):
     67    commands = KSSCommands()
     68    commands.core.replaceInnerHTML(css('div.content'), '<h1>Hello world</h1>')
     69    commands.scriptaculous.effect(css('div.message'), 'blinddown')
     70    commands.scriptaculous.effect(css('div.message'), 'blindup', delay=2)
     71    return HttpResponse(commands.render(), mimetype='text/xml')
     72}}}
     73
     74This returns a XML code that is caught in client side by KSS javascript, and then he execute this commands in the browser.
     75
     76More on [http://kssproject.org KSS website].
     77
     78== KSS in django ==
     79
     80At Plone Conference 2007 I developed [https://code.launchpad.net/kss.django kss.django], a django application for accelerating KSS development with django.
     81
     82Django is a perfect mix with KSS due to features like ``templatetags``, ``url mappings``, ``settings`` and so on.
     83
     84== Demo site ==
     85
     86I created this [http://coolwiki.yaco.es/wiki/ demo site] for show KSS features and a real site that uses ``kss.django``.
     87
     88== Installing kss.django ==
     89
     90{{{
     91 $ bzr checkout http://bazaar.launchpad.net/~kissbooth/kss.django/trunk/ kss.django
     92 $ cd kss.django
     93 $ python setup.py install   ---> [or python setup.py develop ]
     94}}}
     95
     96== KSSing your website with kss.django application ==
     97
     98I will use the [http://codebrowse.launchpad.net/~kissbooth/coolwiki/trunk/files coolwiki project code] as an example of KSSing a web site.
     99
     100'''Register kss.django application in your project'''
     101Register ``kss.django`` application in your ``settings.py``:
     102
     103{{{
     104INSTALLED_APPS = (
     105    'django.contrib.auth',
     106    'django.contrib.contenttypes',
     107    'django.contrib.sessions',
     108    'django.contrib.sites',
     109    'coolwiki.wiki',
     110    'kss.django', # KSS django application
     111)
     112}}}
     113
     114'''Setup your KSS plugins'''
     115If you want to use scriptaculous effects, or other plugins, you can put on ``KSS_EXTRA_PLUGINS`` settings parameter, like that:
     116{{{
     117KSS_EXTRA_PLUGINS = ['scriptaculous-effects']
     118}}}
     119
     120'''URL configuration'''
     121One 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.
     122First of all, in your root ``urls.py``, you have to put this:
     123{{{
     124urlpatterns = patterns('',
     125    (r'^kss/', include('kss.django.urls')),
     126    ...
     127)
     128}}}
     129This line is needed for automatically loading of all the KSS Javascripts.
     130
     131Later, in your application, you must change your URLs. For example I put the URLs like below for my ``coolwiki`` demosite:
     132{{{
     133#!python
     134from wiki import views
     135
     136urlpatterns = patterns('',
     137    (r'^$', views.index),
     138    (r'^view$', views.view),
     139    (r'^edit$', views.edit),
     140    (r'^save$', views.save),
     141    (r'^history$', views.history),
     142    (r'^ajax/view$', views.view, {'is_kss': True}),
     143    (r'^ajax/edit$', views.edit, {'is_kss': True}),
     144    (r'^ajax/save$', views.save, {'is_kss': True}),
     145    (r'^ajax/history$', views.history, {'is_kss': True}),
     146)
     147}}}
     148As 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).
     149
     150What 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:
     151{{{
     152This is a <span class="page-link"><a href="/wiki/view?title=WikiPage">WikiPage</a></span>.
     153}}}
     154And you put this in your KSS file:
     155{{{
     156#!css
     157.page-link a:click{
     158  evt-click-preventdefault: True;
     159  action-server: ajax/view;
     160  ajax/view-title: nodeContent();
     161}
     162}}}
     163If 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``.
     164
     165'''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:KSSInDjango#Demosite 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.
     166
     167'''KSSing django views'''
     168The low level way to use a django view with KSS is this:
     169{{{
     170#!python
     171def view(request, is_kss=False):
     172    page_name = request.REQUEST['title'] # it take the wiki word
     173    page = Page.objects.get(name=page_name)
     174    if is_kss:
     175        commands = KSSCommands()
     176        commands.core.replaceInnerHTML(css('div.content'), page.cooked_content)
     177        return HttpResponse(commands.render(), mimetype='text/xml')
     178    else:
     179        return render_to_response('wiki/view.html', {'page': page})
     180}}}
     181
     182It is valid and clean way to get AJAX rendering and also works with Javascript disabled.
     183
     184For 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.
     185
     186The ``wiki/view.html`` may look like this:
     187{{{
     188<html>
     189<head>
     190  {% load ksslib %}
     191  {% include_kssjs %}
     192</head>
     193<div class="content">
     194</div>
     195</html>
     196}}}
     197
     198But 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:
     199{{{
     200<html>
     201<head>
     202  {% load ksslib %}
     203  {% include_kssjs %}
     204</head>
     205<div class="content">
     206  <h1>{{ page.name }}</h1>
     207  <div>
     208    {{ page.cooked_content }}
     209    <table>
     210      <tr><td>Version:</td><td>{{ page.version }}</td></tr>
     211    </table>
     212  </div>
     213</div>
     214</html>
     215}}}
     216If you want to change all ``div.content`` fragment, you must to do ugly things like this:
     217{{{
     218#!python
     219def view(request, is_kss=False):
     220    ...
     221    tpl = Template('''<h1>{{ page.name }}</h1>
     222    <div>
     223      {{ page.cooked_content }}
     224      <table>
     225        <tr><td>Version:</td><td>{{ page.version }}</td></tr>
     226      </table>
     227    </div>''')
     228    ctx = Context({'page': page})
     229    if is_kss:
     230       commands.core.replaceInnerHTML(css('div.content'), tpl.render(ctx))
     231    ...
     232}}}
     233To 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:
     234{{{
     235<html>
     236<head>
     237  {% load ksslib %}
     238  {% include_kssjs %}
     239</head>
     240<div class="content">
     241 {% ksswidget main %}
     242  <h1>{{ page.name }}</h1>
     243  <div>
     244    {{ page.cooked_content }}
     245    <table>
     246      <tr><td>Version:</td><td>{{ page.version }}</td></tr>
     247    </table>
     248  </div>
     249 {% endksswidget %}
     250</div>
     251{% ksswidget footer %}
     252 You are on {{ page.name }}
     253{% endksswidget %}
     254</html>
     255}}}
     256
     257And the views becomes:
     258{{{
     259#!python
     260def view(request, is_kss=False):
     261    page_name = request.REQUEST['title'] # it take the wiki word
     262    page = Page.objects.get(name=page_name)
     263    if is_kss:
     264        commands = KSSCommands()
     265        commands.django.replace_widgets('wiki/view.html',
     266                                        ['main', 'footer'], {'page': page})
     267        return HttpResponse(commands.render(), mimetype='text/xml')
     268    else:
     269        return render_to_response('wiki/view.html', {'page': page})
     270}}}
     271
     272``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.
     273
     274There is a shortcut for avoid the return ``return HttpResponse(commands.render(), mimetype='text/xml')``. Is the ``kss_response`` method. The code becomes:
     275{{{
     276#!python
     277from kss.django.render import kss_response
     278
     279def view(request, is_kss=False):
     280    page_name = request.REQUEST['title'] # it take the wiki word
     281    page = Page.objects.get(name=page_name)
     282    if is_kss:
     283        commands = KSSCommands()
     284        commands.django.replace_widgets('wiki/view.html',
     285                                        ['main', 'footer'], {'page': page})
     286        return kss_response(commands)
     287    else:
     288        return render_to_response('wiki/view.html', {'page': page})
     289}}}
     290
     291There is also a shortcut for this, the ``render_kss_response`` generic view. By using this the view code can be reduced to:
     292{{{
     293#!python
     294from kss.django.render import render_kss_response
     295
     296def view(request, is_kss=False):
     297    page_name = request.REQUEST['title'] # it take the wiki word
     298    page = Page.objects.get(name=page_name)
     299    commands = KSSCommands()
     300    return render_kss_response('wiki/view.html', {'page': page},
     301                               is_kss, ksswidgets=['main', 'footer'],
     302                               commands=commands)
     303}}}
     304The ``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.
     305
     306If 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:
     307{{{
     308#!python
     309from kss.django.render import render_kss_response
     310
     311def view(request, is_kss=False):
     312    page_name = request.REQUEST['title'] # it take the wiki word
     313    page = Page.objects.get(name=page_name)
     314    commands = KSSCommands()
     315    if is_kss:
     316        commands.scriptaculous.effect(css('div.message'), 'blinddown')
     317        commands.scriptaculous.effect(css('div.message'), 'blindup', delay=2)
     318    return render_kss_response('wiki/view.html', {'page': page},
     319                               is_kss, ksswidgets=['main', 'footer'], commands=commands))
     320}}}
     321
Back to Top