django and joomla with jsonrpc
well *phew* thank goodness for django, is all i can say. i
spent today updating a joomla site into an experimental
joomla-django-pyjamas hybrid.
it's not pretty :)
step 1 - the django model
legacy
databases says that your key is "python manage.py
inspectdb". that gives you a basic start and, depending on
the quality of the design of the database, you get a
reasonable approximation.
i found three errors in the joomla database i'm working
with: three tables didn't have primary keys, but one _was_
identified as "unique", and they all did have "id" as the
first field (being django).
ok - i say three errors - but i would consider not having
any foreign keys - at all - to be an absolutely massive
error - and serious problem.
this is mysql - there's not a lot that can be done about it...
step 0
whoops - first: install django (from svn), django-evolution
(absolutely essential) and libcommonDjango.
libcommonDjango contains the essential JSONrpc service.
step 2 - jsonrpc django view
create a django fooapp/events/view.py:
from django.pimentech.network import *
from testapp.models import Events
eventservice = JSONRPCService()
@jsonremote(eventservice)
def echo(request, event_id, test_txt):
try:
e = Events.objects.get(id=event_id)
event_desc = e.description
except Events.DoesNotExist:
event_desc = ''
return "hello %s %s" % (test_txt, event_desc)
urls.py gets this:
(r'^event-service/$', 'fooapp.events.views.eventservice'),
step 3 - pyjamas integration in django
this is where it gets tricky. first thing: i'm doing a
hybrid, so the pyjamas code will only be loaded on a
per-component basis. i.e. on a _specific_ joomla page,
which throws up a _specific_ joomla component (in this case,
com_event), _only_ then do we want a specific (matching)
pyjamas module to be activated.
pyjamas starts off by overriding the javascript body
"onload" function, and setting up "modules" which the code
looks for in the "meta" tags. so, the only way i could
think of getting the modules activated at the right time was
to parse the url arguments, looking for the query string
"?option=com_event":
/*
***** Pyjamas Integration ******
*
integrate with pyjamas modules by adding the appropriate
meta tags, here. inclusion of the auto-generated pygwt.js
will go hunting through the meta tags looking for names
"pygwt:module" and will call the appropriate onModuleLoad
depending on the content="moduleName".
*/
$args = array();
$args['option'] = '';
$args['view'] = '';
parse_str($_SERVER['QUERY_STRING'], $args);
/* this is gonna be fun. detect component, also must detect
view */
if ($args['option'] == 'com_events')
{
echo '<meta name="pygwt:module" content="Test">';
}
as you can see, i'm preparing to load up _different_ pyjamas
modules, depending on whether users select different views -
so that'll be if ($args['option'] == 'com_events' &&
args['view'] == 'all') load a different pygwt module.
this bit of awfulness needs to go into your django
template's index.php file - right at the top, so that it
gets outputted in the <head> section.
then, you're ready to add, to your
components/com_events/views/NAMEOFVIEW/tmpl/default.php file
the pygwt.js application (which will do the module loading):
<script language="javascript"
src="/js/Events/test/pygwt.js"></script>
<h2>Testing</h2>
<div id="event"> </div>
<h2>End Testing</h2>
you _could_ add this in along with that meta tag thing...
but... i decided against it. the div tag will be explained
at the end of the next step
step 4 - building the pyjamas app
i've modified build.py
accordingly so that the name of the app is included in the
per-platform javascript files. see lines 150 to 154 - this
results in Test.IE6.cache.html, Test.Mozilla.cache.html and
you can thus copy those files into templates/yourapp/ with
impunity. i may at some point make it possible to install
them in a subdirectory, but if you want to do that yourself
manually you'll need to edit pygwt.js.
the test example i chose was the JSONRPC
example, renaming it to... Test.py. just a couple of tweaks
needed:
class EchoServicePython(JSONProxy):
def __init__(self):
JSONProxy.__init__(self, "/event-service/", ["echo",
"reverse", "uppercase", "lowercase"])
/event-service/ matches our django urls.py, above.
import Window
...
...
else:
if method == self.METHOD_ECHO:
loc = Window.getLocation()
event_id = loc.getSearchVar('id')
id = self.remote_py.echo(event_id, text, self)
alert readers will have noticed that fooapp/events/view.py's
"echo" function (step 2, above) had an extra argument - an
event_id. here what we're doing is pulling the
django-driven "&id=NNN" from the query string, using
Window.getLocation() to parse the query string.
last modification:
RootPanel("event").add(panel)
this is veery special - it results in the AJAX code looking
in the HTML DOM model for a tag with an id of "event". this
is incredibly, incredibly useful and powerful stuff: it
means that you can write HTML and then, when standard HTML
is just... too simple, go adding fully-blown AJAX
applications into the basic HTML, at the right place.
write your stub template in HTML, get your designer to do
it; write your application in python, get your programmer
to do _that_, hit "make" and the app gets turned into
javascript....
step 5 - installing the pyjamas app
this required that bit of trickery - modifying build.py - so
that when it comes to adding more than one pyjamas app to be
loaded at different points, the code can be loaded without
name clashes (Safari.cache.html for Test.py,
Safari.cache.html for Test2.py):
all: build install
build:
python ~/pyjamas/src/builder/build.py Test.py
install:
mkdir -p ../../../www/js/Events/test
cp output/pygwt.js ../../../www/js/Events/test
cp output/*.html ../../../www/
so - the modified build.py generates Test.Mozilla.cache.html
and Test.Safari.cache.html not Safari.cache.html and thus
output/Test.*.html can all be copied into the main www
directory. pygwt.js goes into the js/Events/test
subdirectory, matching where it was specified to be in the
last part of (step 3).
watch it all fall over
well... surprisingly... it didn't fall over. the
sticky-tape technique worked. slight down-side: loading the
php page takes the usual amount of time... and then ....
mmmmmm BLAM, up comes the javascript app bit, after another
second or so.
not a lot that can be done about that - might put
"Loading..." into that div or something.
overall, i'm just really relieved. the hybrid between
django and joomla turned out to be easier than i thought it
might be - thanks to django's "inspectdb" functionality.
i know for a fact that i'm going to have to watch the way
that django-evolution is used like a hawk - i've already
changed the name of one of the model classes and, perfectly
legitimately, django-evolution wiped that table off the
mysql map and recreated it (blank) although it had the same
"Meta" mysql table name. a backup of the database fixed
that one by restoring the table.
this... this should be good.