Settling onto decent python web technologies
Posted 20 Apr 2006 at 22:55 UTC by lkcl
I've been doing web development in python, now, for five years, since I wrote my first python cgi script running out of CGIHTTPServer.py, and
have loved it ever since. This article describes what technology I am using today, and wish I had found, or that it had been available, then: mod_python, sqlobject, formencode, htmltmpl and PIL.
Python has come on drastically in five years: in 2001 i met someone who was excited to hear that i had been working for six months to produce a 25,000 line project: at the time, aside from Zope, that was the biggest they'd ever heard of. By contrast: some time last year, I heard of a company doing an ERP project with a MILLION lines of python under their belt.
I started out with the usual: cgi-script programming. After two months, i had already got fed up with cut/paste of the hand-coded python/SQL programming and written a template-generator (pysqlgen) and some helper-functions to construct SQL (pysqldb); had written pyxsqmsll2000 (MS-SQL 2000 accesser via the XML interface); helped do a couple of tiny patches to python 2.0, and had done a very basic table/form generator to help me do horizontal and vertical tables, and auto-generate < select > lists.
it was all thoroughly archaic, and it clunked along nicely.
this technology i used successfully over the next four years, to write all sorts of little projects for myself, some of which i even got paid for. in the back of my mind, however, was the nagging feeling that there had to be something better out there.
to zope or not to zope
well, I got an opportunity to focus my tiny brain onto Zope, this january, and i have to say that i am genuinely disappointed by it. i had been led to believe that zope is fantastic as a web development tool.
what Zope _actually_ is is a web development tool _kit_, and it has taken me about two months to work this out.
Zope allows non-programmers to plug things together: you can "plug in" a SQL statement in the zope administrative front-end, and then you can use that SQL statement to generate as many tables as you want - again, with the zope administrative front-end.
i don't know about you, but if you want to go beyond that - into actual web development (as we, as programmers, would expect), then it all starts to get a bit... well... hairy. the entry-level requirements are quite high: plus, you have to "worm" your way through the google searches, eliminating all of the "not-quite-useful-to-programmers" stuff that makes Zope so successful to non-programmer-types.
let me put it this way: when you feel the need to start a web site "zope for real programmers" then you know that something's not quite right.
the final straw for me was when i started writing my OWN project
in zope, hoping to use and then learn more about SQLObject
(very good article about sqlobject on
ibm developerworks network
by the way) only to find that it conflicts badly with zope 2.6
to 2.8 due to "refresh cacheing" of modules. i took one look
at zope 3.0's xml-based config files and went "nnnnnn....naah".
at that point, i had about four days worth of programming already done, but my experience with zope (from my day job that i started two months ago) had ALREADY led me to write-off dtml-call, dtml-let etc. leaving me with true/false-based dtml-if statements, dtml-var "somevariable" and dtml-in (if you've ever used htmltmpl, you know what's coming next...)
mod_python and htmltmpl
converting the code to use mod_python took, if i am to be honest, about a day - including looking up the documentation on how to use Cookies, how to use requests, doing the apache2 configuration, converting the use of http get/post variables (which didn't take a lot because it's just a dictionary of values or lists)...
... but what _should_ have been the trickiest bit (converting from dtml to htmltmpl) was actually the simplest: global/search/replace "mapping" with ""; global/search/replace "dtml-if" with TMPL_IF; ... etc.
one gotcha: htmltmpl has "cgi escape" on as the default (for safety reasons) - so you can either make sure that any template-variables that are hyperlinks you use < TMPL_VAR theweblink ESCAPE="NONE" >, or you can set the default of the TemplateManager to not do cgi-escaping of any template-variables.
here's where i cheated a bit, because i've been using htmltmpl since i discovered it, two years ago, and i created a REALLY useful class which wraps TemplateProcessor as a dictionary, and overloaded the __str__ method: you then declare an TMPLdict object with the filename as the argument, set all of the template variables you need to be substituted, and then when you're ready, do return str(tmpl_object) and voila you get your rendered HTML page.
so, that class was _pretty_ much identical to zope's HTMLfile class - close enough for me to write some vi macros which warped the code, carefully, so i was done converting within a couple of hours.
there are two absolutely _vital_ bits of technology - both by ian bicking - which make web development an art to enjoy rather than a "job to be done", and they are SQLobject and FormEncode. SQLobject is incredibly powerful, but i found that (back in 2002/3) its requirement (since lifted) that all tables must have an auto-numbered "id" field, very restrictive except for new projects. this being a new project, i could use it.
sqlobject
wow, wow, i love this stuff. it's very powerful and it supports six
well-known database back-ends - all seamlessly. the only thing is
that, as someone who has been doing relational database programming
"the hard way", keeping a list in my head of the top most complex
database queries i've had to design (eight weeks design and two weeks
implementing is the record, so far), i find sqlobject a little
difficult.
my associate, however, who has had _zero_ relational database experience,
finds it stunningly simple to use :) unfortunately, i taught him
about 4th normalised database forms and we only have... *frantic*
one table! yes, one table! we're about to make it a few more
(one for each type of object - float, blob, string) but it's even
stretching what sqlobject is capable of.
... fortunately, between the two of us... :)
at some point, we'll make a decision as to whether to make the code
free software or not: certainly the object-orientated database access
module is something worth open sourcing, but it's also worthy of
an article on its own, it's that horrendous, and we're just investigating
python meta-classes as part of making the api very easy to use.
formencode
FormEncode. wow, how i wish i'd found formencode before now. it's incredible. you pass it a straight, vanilla, plain, £6/hr-web-designer-written form, a cgi-based dictionary of web-form-values and a validator class, and it gives you a dictionary of results, plus some errors back.
you can then go "oh look, errors", and hand the results and the errors BACK to FormEncode, and it will generate the HTML page containing those errors (substituted into the right places!) plus the results substituted into the page AS WELL, so that the user can re-submit the form without having to go "back".
it's just... incredible!
unfortunately, ian believes that zope (ptoooey) has superseded formencode (with things like Formulator and ZPT) and ... how wrong, wrong wrong could he be (fortunately this assessment ability doesn't affect his programming abilities - hi ian, love your code! :)
ZPT is a macro-based programming language embedded in HTML pages with an XML syntax (more on this later) - formulator is a python-based library that understands HTML (input, textarea, select - the works) as it uses the python "SGML / SAX" library to walk the html nodes, looking for the right places.
bottom line: gimme formulator any day.
AJAX
the other technology that i've discovered (through my day job) is AJAX. now, given that i'm a javascript-hater, for me to be advocating AJAX there'd better be a damn good reason for it - and it's this: insanely, insanely fast web sites.
what is the _one_ thing that you find most annoying about web pages?
REFRESH!!! _dang_ - hitting a button and the ENTIRE page has to be
regenerated at the server-end, and then fed over the internet connection,
AND THEN it gets redrawn (from scratch, with a flicker), including all
those REALLY annoying flashing images, and because it's badly-written
html, the tables go "splurge" sideways until all the content is finally
downloaded...
have you ever _looked_ at myspace.com?
... don't!
the alternative is AJAX, and the site i'm writing now uses it to the
extreme: as an alternative to frames and iframes. in combination with
carefully-crafted CSS stylesheets, it's FAST.
whenever a user clicks on a link, the only html that is loaded from
the server, which is done via an XmlHTTPRequest object on the browser,
is that tiny fragment which is then substituted into a < div > tag.
the only down-side of AJAX is that if you use it, you must USE it,
including for forms, because the user's browser is storing all the state
information about the current page - your server certainly isn't!
so when you click that POST form, you lose everything to the "action"
destination page.
before i started using AJAX "post" on forms, i did come up with a
"solution": i made the "main index page"
the form "action" for EVVEERRYYTHING - and redirected all of the
POST arguments dynamically through to the (ajax-based) frame targets,
which themselves had to be dynamic.
this quickly got out-of-hand beyond the fourth form that i added,
and at that point i began to investigate the POST method of
XmlHTTPRequest.
the tricky bit of using AJAX on forms is that you must parse the
HTML form yourself, looking for all the form "elements" in, yep,
you got it: javascript. fortunately i found a very good half-working
example.
another alternative is to put the form into an iframe.
and for "file upload", well... you _have_ to use a standard form,
because javascript isn't allowed access to the local filesystem
as it's a security risk, so you can't get at the file in order to
read it in order to POST it via an XmlHTTPRequest javascript object.
... i have to say, it's quite complex, but well worth the effort.
... if you can tolerate javascript.
CSS styles
i now _love_ css. the site i'm doing would make a lynx user proud.
at my associate's recommendation, there are ZERO colour codings,
ZERO font tags, and judicious use of "em" instead of "px".
the reason is this: if people _want_ to make their fonts smaller or
larger, they should be able to. if people _want_ to select a
different background/foreground, they should be able to.
without fear of retribution from their own computer.
want to lose customers and clients? write crap or overblown
html. you remember that ugly web site article on slashdot?
(this one) - well worth reading.
what have you _done_, dude????
i'm writing a social networking site (yes, another one). because i
can. because i don't like the existing ones. because i want to do
cool stuff that i can be proud of achieving. because i want to learn
about elegant and useful stuff that will help me do my job better and
make stacks of wonga.
in two weeks, using these simple python technologies, i and a very
technically competent associate of mine have an alpha version
of our site that already rivals the worst of the worst dating sites
you've probably never dared to go near - and it's FAST, simple and...
well, it's early days yet :)
links
here is a quick list of the components and some links where relevant:
- mod_python
- htmltmpl
- sqlobject + sqlbuilder (using postgresql)
- formencode
- PIL (python imaging library) - converting to PNG and doing thumbnails.
- mxDateTime (duh)
- AJAX get - in japanese. persevere with it...
- AJAX post article - very useful. needs work.
- DHTML stuff more useful stuff.
- CSS tables
- ibm developerworks sqlobject article
</ul>
web-programming in unprogrammable languages
one final and very important thing - my recommendations on what to use
for web development.
why am i making these recommendations?
... well... it goes like this. i was asked to take over a project
that was written in php, and the guy had been working for a year
on it - cutting and pasting some MASSIVE forms, several thousand
lines long - to produce near-identical add, edit and error pages.
absolute insanity.
the reason?
it had never crossed his mind to use templating, because when you
are presented with a page full of php, it's simpler to cut/paste
than it is to think.
consequently, the site, which is very useful and runs a 100,000-strong
subscription list with over two million database records, now has an absolute nightmare maintenance headache on its hands.
formencode and htmltmpl are the FIRST tools you should look to
(or their perl or *shudder* php variants)
dtml is, for similar reasons, a pile of pants.
... but, worse, it's got an XML syntax! and the poor people who
know and love it are beginning to realise that something is wrong,
so they're recommending ZPT (zope page templates) instead, which is
not quite as bad. that's a bit like saying "oooooh, isn't the
air fresh today?" as you walk out of a decaying fish-market
and slip nose-first into dogshit. but seriously: the "macro"
system of ZPT is a really good idea, but you _can_ avoid it
altogether by doing your web programming in an actual programming
language (like python, for example).
dtml is fine - _if_ you stick to dtml-if, dtml-in and dtml-var!
but htmltmpl is better, because it does cacheing and pre-parses/compiles
the templates (like php does, i assume).
stay away from psp, for exactly the same reasons: it will only
encourage your programmers to embed python code into an html page,
where it will get cut-and-paste beyond reasonable and sane limits.
if you _have_ to use PHP, write every page as two separate pages -
one which contains the programming, and the other which contains
the HTML - even write your own templating system if you can't
be bothered to use the php-version of htmltmpl. just... *slap*
don't do it!
maybe i'm preaching to the converted, here, i dunno. i just can't
get over how little code is actually needed to do so much, with
the above list of technologies. i've been fishing and
floundering around for several years, and finally found something reasonable.
just had to tell you about it.
HTMLdict, posted 21 Apr 2006 at 08:53 UTC by lkcl »
(Master)
here's my "htmldict" class, which wraps htmltmpl. the __access__ bit is so that mod_python can't get at any functions/classes in it.
you use this as:
content = HTMLdict('www/maincontent.tmpl')
content['name'] = 'fred'
page = HTMLdict("www/index.tmpl")
page['title'] = 'hello'
page['content'] = contentpage
return str(page)
on e.g. www/index.tmpl:
< html >
< title > < TMPL_VAR title > < /title >
< body > < TMPL_VAR content > < /body >
< /html >
and e.g. www/content.tmpl:
< p >
hurrah, welcome, < TMPL_VAR name >
< /p >
< /pre >
__access__ = 0
from htmltmpl import TemplateProcessor, TemplateManager
class HTMLdict(dict, TemplateProcessor):
def __init__(self, fname=None, htmlescape=0):
self.tmpl_fname = fname
dict.__init__(self)
TemplateProcessor.__init__(self, html_escape=htmlescape)
def __str__(self):
if self.tmpl_fname is None:
return ''
for (k, val) in self.items():
if type(val) != type([]) and type(val) != type('') and \
val is not None:
self.set(k, str(val))
elif val is not None:
self.set(k, val)
template = TemplateManager().prepare(self.tmpl_fname)
return self.process(template)
ZPT, posted 21 Apr 2006 at 10:44 UTC by lkcl »
(Master)
i just received word from someone who read this article, and they kindly pointed out a couple of things. the first one is that ZPT makes it incredibly easy to hand HTML to web designers: the syntax is < tal:table > and so the template macro stuff doesn't interfere, in any way, with the web designer's job - unlike dtml and htmltmpl.
i pointed out to him that if ZPT only supported if-true-false, variable substitution and loop-on-list-of-dictionaries, i would consider it to be ideal.
in situations where you put a "cool toy" in front of people whose job it is to "do", they will "do", and they will use every "cool toy" possible.
regardless of the consequences, because it's not their job to think of that.
hence, why i have such a problem with zope: so much _can_ be done with it that people _will_ do it - and get themselves into a mess.
Javascript, posted 21 Apr 2006 at 11:45 UTC by cdfrey »
(Journeyer)
Thanks for the article. Always interesting to read your output.
You're an interesting person to ask this: since you hate javascript, what do you do for those who turn it off? Do you write two versions of your site?
Personally, since I usually have javascript turned off in my main browser configuration, I feel morally obligated to write sites that are still usable that way. It wouldn't be right to force others to use javascript if I don't want to use it myself.
Not to mention the security side of things. It wouldn't be right to force my users to make their computers less secure just to use my site either.
Thoughts?
hiya cdfrey,
well, what i've done is written a simple javascript function which walks the DOM model, looking for "onclick". i then "overwrite" the href with a javascript "void" function. this stops double-linking, but also allows people to click on the href if they have javascript disabled.
then, i make sure that all hrefs are like this:
< a href="< TMPL_VAR thelink > onclick="ajax_dlink('thedivtagid', '< TMPL_VAR thelink >'"> click here < /a >
function set_hrefs()
{
//var oAs = document.getElementsByTagName("a");
var oAs = document.links;
var LC;
for (LC = 0; LC < oAs.length; LC++)
{
if (oAs[LC].onclick)
{
oAs[LC].href = "javascript:void(0)";
//oAs[LC].href="javascript:"+oAs[LC].onclick+";";
//oAs[LC].onclick='';
}
}
}
the maintenance effort of having two sites really would be a nightmare. so i hope to come up with some tricks where, with ease, i can switch between frames, non-javascript, iframes, ajax... we'll see what happens :)
correction, posted 21 Apr 2006 at 13:11 UTC by lkcl »
(Master)
the syntax of ZPT (tal) is like this:
< td tal:content="here/now" > < /td >
which means of course that the "unrecognised" attributes simply get ignored by "industry-standard" web design tools.
and i was very interested to hear (from matthew hamilton, of netsight) that a lot of people who are using ZPT are advocating the same things that i am - namely that ZPT should be restricted to if-true-false, loop-on-list-of-dictionaries, and text-substitution.
btw, also, it's worth pointing out: ZPT doesn't have to be used exclusively in zope.
noscript, posted 22 Apr 2006 at 00:23 UTC by lkcl »
(Master)
< noscript > do something here like put in an href to a different style menu
< /noscript>
according to the above article, what i am apparently _supposed_ to do is return "true" from the "onclick" function and then the href won't be actioned. hm. oh well!
Just now I posted a diary about how I'm learning C# ASP.Net so I can put those two all-important buzzwords in my resume. It seems the the once proud and independent nation of Canada is now completely within the grip of the Evil Empire.
To prove I can do it, and hopefully make a little coin on my own, I'm using them to write a web application that will run on Mono, Apache and Linux rather than Windoze and IIS.
From what I've seen of C# so far, it looks like a well-designed language, and I hear good things about it all the time.
I personally am a big fan of Python, and would prefer to code my new web app in it, if it weren't for the fact that there seem to be no Python jobs to be found anywhere in Canada. I need the website to give me a foot in the door for C# contracts, as I don't have any paid experience using it.
i really like mono, posted 23 Apr 2006 at 13:26 UTC by lkcl »
(Master)
this may come as a surprise, but i actually really like mono and .net and what it promises - but then, i was privileged to hear what the designers had in mind for it - namely that it should be a common binding for _all_ the possible languages that ms could get their hands on.
to that end, they hired the guy who wrote ironpython, which if you have the time and love python, i do recommend that you investigate it. it's basically .net (mono) with a python syntax. you CANNOT get access to the standard python librariey: ironpython is actually a compiler that happens to have a python syntax, it really DOES have absolutely nothing to do with python2.X, so you have all of the .net and mono libraries available to you instead.
okay.
here's what i would like to see happen, wrt mono.
namely, that they go get libwine, and then add a COMPLETE set of bindings into mono, for Win32.
this would truly make it possible for .net applications to run cross-platform.
already, from what i can gather, it's possible to run applications written for ASP/.NET under apache2 - it would be absolutely perfect if you could run win32 graphical applications, too.
UGH!
lovely syntax (right up to the ${fruit} bit).
love the < ul py:xxx > bit: like tal that makes it possible to hand the html to a specialist html designer without them needing to know about "code".
... but evaluation of functions? NNOOOOO! template-based functions/macros? NOOOO!
... y'know... i really should set up an example site of what i've been using... i'd love to see how it compares against e.g. cherrypy.
ajax.js, posted 7 May 2006 at 23:16 UTC by lkcl »
(Master)
might as well dump the javascript i use at you - it's a mish-mash of various examples i've found online, some of which needed work.
one little "trick" that i do is, if you download an HTTP fragment which contains < script >, then i execute its content!
this is an unbelievably incredibly useful trick, because what you can use it for is to put in further ajax calls in to update other < div > tags.
for example, my "congratulations you logged in" welcome message appears in my "centrepage" div, and that div tag is the target of the AJAX POST of the login form. being greeted by "congratulations you logged in" is friggin boring, so i added < script > ajax_dlink('leftcontext, './usercontext.html'); ajax_dlink('banner', 'usermenu.html'); < /script > and that of course gets executed by the "activate_javascript()" function...
neat, huh? :)
btw - watch out for using the timeout-based ajax_dlink_refresh()
function: it's a bit weird, it's not perfect, and that's probably because i need to call clearTimeout() from the AJAX POSTing function as well and then restart the timer... oh well :)
function createHttpRequest(){
if(window.ActiveXObject){
try {
return new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
return new ActiveXObject("Microsoft.XMLHTTP");
} catch (e2) {
return null;
}
}
} else if(window.XMLHttpRequest){
return new XMLHttpRequest();
} else {
return null;
}
}
function set_hrefs()
{
//var oAs = document.getElementsByTagName("a");
var oAs = document.links;
var LC;
for (LC = 0; LC < oAs.length; LC++)
{
if (oAs[LC].onclick)
{
oAs[LC].href = "javascript:void(0)";
//oAs[LC].href="javascript:"+oAs[LC].onclick+";";
//oAs[LC].onclick='';
}
}
}
/**
* activate_javascript(str)
*
* looks for any script and runs it (using eval)
*
*/
function activate_javascript(targetnode)
{
var scriptnodes = targetnode.getElementsByTagName('script')
var LC;
for (LC = 0; LC < scriptnodes.length; LC++)
{
var sn = scriptnodes[LC];
var nd = document.getElementById('centercontent');
if (sn.src)
{
ajax_eval(sn.src);
}
else
{
eval(sn.innerHTML);
}
}
}
/**
* ajax_eval(url)
*
* @param url load and activate url
* @returns readyState
*/
function ajax_eval(url)
{
var xhtoj = createHttpRequest()
xhtoj.open("GET", url , true );
xhtoj.onreadystatechange = function()
{
if ((xhtoj.readyState==4) && (xhtoj.status == 200) )
{
str = xhtoj.responseText;
eval(str);
}
}
xhtoj.send("")
}
/**
* ajax_dlink_refresh(oj,url)
*
* @param id id of element for insert
* @param url load url
* @param timeout refresh timeout period, ms
* @returns readyState
*/
/* use these to overrun an existing timeout, so that
we don't end up with several of them!
*/
var running_timeout = 0;
var timeout_idname;
var timeout_url;
function ajax_dlink_refresh(idname, url, timeout)
{
timeout_idname = idname;
timeout_url = url;
redo_timeout = timeout;
if (running_timeout)
return;
setTimeout("do_ajax_dlink_refresh()", timeout);
running_timeout = 1;
}
function do_ajax_dlink_refresh()
{
if (ajax_dlink(timeout_idname, timeout_url) == 0)
{
running_timeout = 0;
return;
}
running_timeout = 0;
ajax_dlink_refresh(timeout_idname, timeout_url, redo_timeout);
}
/**
* ajax_dlink(oj,url)
*
* @param id id of element for insert
* @param url load url
* @returns readyState
*/
function ajax_dlink(idname,url)
{
clearTimeout(); /* really important - get into a mess otherwise */
var xhtoj = createHttpRequest()
xhtoj.open("GET", url , true );
xhtoj.send("");
var jsnode = 0;
xhtoj.onreadystatechange = function()
{
if ((xhtoj.readyState==4) && (xhtoj.status == 200) )
{
if (chk_browser('konqueror'))
str = utf8to16(xhtoj.responseText);
else if(chk_browser('safari'))
str = utf8to16(xhtoj.responseText);//dame
else str = xhtoj.responseText;
jsnode = document.getElementById(idname);
if (jsnode)
{
jsnode.innerHTML=str;
activate_javascript(jsnode);
/* remove all hrefs from onlick links */
set_hrefs();
return 1;
}
}
}
return 0;
}
/**
* chk_browser()
*
* @param browserName browser name
*/
function chk_browser(browserName)
{
var ua = navigator.userAgent
switch (browserName)
{
case 'konqueror' :
return ua.indexOf("Konqueror") != -1 ; break ;
case 'safari' :
return ua.indexOf("Safari") != -1 ; break ;
dafault :
return null ; break ;
}
}
/* utf.js - UTF-8 <=> UTF-16 convertion
*
* Copyright (C) 1999 Masanao Izumo <mo@goice.co.jp>
* Version: 1.0
* LastModified: Dec 25 1999
* This library is free. You can redistribute it and/or modify it.
*/
/*
* Interfaces:
* utf8 = utf16to8(utf16);
* utf16 = utf16to8(utf8);
*/
function utf16to8(str) {
var out, i, len, c;
out = "";
len = str.length;
for(i = 0; i < len; i++) {
c = str.charCodeAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
out += str.charAt(i);
} else if (c > 0x07FF) {
out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
} else {
out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
}
}
return out;
}
function utf8to16(str) {
var out, i, len, c;
var char2, char3;
out = "";
len = str.length;
i = 0;
while(i < len) {
c = str.charCodeAt(i++);
switch(c >> 4)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
out += str.charAt(i-1);
break;
case 12: case 13:
// 110x xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
char3 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
}
function ajax_post(idname, file, fobj)
{
var str = getFormValues(fobj, null);
var xhtoj = createHttpRequest();
var jsnode = document.getElementById(idname);
xhtoj.open( "POST", file, true );
xhtoj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhtoj.setRequestHeader("Content-length", str.length);
xhtoj.setRequestHeader("Connection", "close");
xhtoj.send(str);
xhtoj.onreadystatechange = function()
{
if ((xhtoj.readyState==4) && (xhtoj.status == 200) )
{
if (chk_browser('konqueror'))
str = utf8to16(xhtoj.responseText);
else if(chk_browser('safari'))
str = utf8to16(xhtoj.responseText);//dame
else str = xhtoj.responseText;
jsnode = document.getElementById(idname);
jsnode.innerHTML = str;
activate_javascript(jsnode);
/* remove all hrefs from onlick links */
set_hrefs();
}
}
}
function getFormValues(fobj)
{
var str = "";
var val = "";
var cmd = "";
var valFunc = null;
for (var i = 0;i < fobj.elements.length; i++)
{
switch (fobj.elements[i].type)
{
case "checkbox":
case "radio":
{
if (fobj.elements[i].checked)
{
str += fobj.elements[i].name + "=" + fobj.elements[i].value + "&";
} else {
str += fobj.elements[i].name + "=&";
}
break;
}
case "text":
case "hiddentext":
case "hidden":
case "password":
case "textarea":
case "button":
if(valFunc)
{
//use single quotes for argument so that the value of
//fobj.elements[i].value is treated as a string not a literal
cmd = valFunc + "(" + 'fobj.elements[i].value' + ")";
val = eval(cmd)
}
str += fobj.elements[i].name + "=" + escape(fobj.elements[i].value) + "&";
break;
case "select-one":
str += fobj.elements[i].name + "=" + fobj.elements[i].options[fobj.elements[i].selectedIndex].value + "&";
break;
}
}
str = str.substr(0,(str.length - 1));
return str;
}
more cool stuff!, posted 23 May 2006 at 22:36 UTC by lkcl »
(Master)
http://freespace.virgin.net/hamish.sanderson/htmltemplate.html
and
http://www.dscpl.com.au/projects/vampire/
htmltemplate looks a _hell_ of a lot better than anything i've previously encountered. you indicate what you want to "tag", by using the syntax "node=somename" and then you can reference anything with somename as a python variable.
the syntax could use a little work. e.g. to access (and replace) the inner-html, you do template.somename.content = "the new content", and to add (or delete?) an attribute, you do template.somename.atts['href'] = "http://fred.com"
with proper overloading, this could be template.somename = "the new content" and template.somename['href'] = "http://fred.com".
and yes you can do template.somename = "the new content" - in the template class you overload __setattr__ i believe.
the way that HTMLTemplate works is _very_ obscure. i love the convenience behind the HTMLdict concept, so here's a simple wrapper which provides the same functionality - for HTMLTemplate:
from HTMLTemplate import Template
class HTMLdict2(dict):
def __init__(self, fname=None, content=None):
dict.__init__(self)
if fname:
f = open(fname, "r")
content = f.read()
f.close()
self.t = Template(self.render, content)
def render(self, node, kvs):
for (k, val) in kvs.items():
a = getattr(node, k)
if isinstance(val, type([])):
a.repeat(self.render, val)
else:
a.content = val
def __str__(self):
return self.t.render(self)
def test():
html = """
<div node="rep:section">
<h2 node="con:title">section title</h2>
<p node="con:desc">section description</p>
</div>
"""
x = HTMLdict2(content=html)
d = [{'title': 'title1', 'desc': 'desc1'}]
x['section'] = d
print str(x)
test embedded greeting card (1600x1200pix) from Jones Farm, West Trenton NJ.
look at her through this lens.
what you see ain't what you get.
the author calls this "advanced ajax".
basically it's very similar to the ajax.js i posted, above.
the key thing that i can notice (and understand, with 1mins' scanning)
is that it copes with select-multiple on HTTP POST.
also it looks like it has some auto-refresh stuff, whoopeee.
http://developers.slashdot.org/developers/06/06/06/0451239.shtml
a slashdot article which highlights why PHP and MySQL are so horrendous: maintenance. although the author on his blog mentions that PHP is "bad" because it encourages people to mix-code-with-markup, he then goes on to recommend Zope - without then advocating that people avoid ZPT/TAL's mix-code-with-markup features, or dtml's mix-code-and-namespaces-with-markup.
lucky he doesn't know about mod_python's "PSP" in order to recommend that, then :)
i've just started adding in support for no javascript, with a view to "if things go wrong with ajax..." :)
the principle is quite straightforward, and has two aspects. the first is to "construct" the web page, from multiple functions, or to set up ajax calls to "populate" the web page; the second is to "remember" previous AJAX calls and to add them to the "populating" bit; or to reconstruct the page again using the new
and the key to doing this is to subdivide the web site into sections using < div id="centercontent" /> < div id="banner /> etc.
"remembering" the history basically revolves around "recording", on a per-div-id-basis, the last location where an AJAX call overwrote the DOM model. to that end, it is necessary to record the href which was clicked on and associate it with the div "id" - using the div "id" as a key into a dictionary and recording the entire href clicked on as the key.
the trick is to distinguish between those AJAX calls which are to be "remembered" and those which are not - at the server-end. i do this by adding in an extra parameter onto the href - "&source=centercontent". BEFORE the python function which constructs the response page is returned, this argument is REMOVED, then the rest of the URL is recorded under the divid (in this example, centercontent) in a dictionary.
also, i don't set up links to web pages by shoving in an < a href="..." /> into the HTML template - that's boring :) what i do is pass a list of dictionaries containing the link information to a function, which creates the actual a href - _including_ automatically adding "&source=xxxx" and _including_ setting it up to do the javascript "onclick=ajax_dlink('./thewebpage?source=xxxx','xxxx')" stuff.
i haven't quite got round to it yet but in "non-ajax" mode, there will be only one target page - the index page. and the link-targets will get "munged" so that what _was_ the target page (e.g. "./profile?user=fred" in the menu) will become an argument passed to the index page (e.g. "./index?source=xxxx&actualtarget=profile%2Euser%3Cfred" - i don't really know yet.
ultimately, i should also be able to add in a "frames" mode, where i construct framesets, too.
at that point, when i have it all working, i'll try to get it all documented and package it up because it's pretty cool :)
mod_python introduction article
very good article explaining how to set up apache, mod_python, mysql - and recommending the use of sqlobject and explaining it. code fragment examples, config file examples - all in one. hurrah.
python web objects, posted 14 Jun 2006 at 20:00 UTC by lkcl »
(Master)
ajax mistakes, posted 14 Jun 2006 at 21:51 UTC by lkcl »
(Master)
ar$e, posted 14 Jun 2006 at 21:52 UTC by lkcl »
(Master)
correct link
damn xxxxing advogato seriously needs some arse-kicking. oh. and an edit mode.
and it may not be a bad thing. Natural simplying process. Mandatory evaculation means ' if you walk freely in that zone, authority may put you behind the bar.'
Here's latest snap shot. Water under the Washington Crossing bridge. Last time when i walked on the bridge sidewalk, water might be more than 20 feet below...
scriptaculous, posted 4 Jul 2006 at 11:46 UTC by lkcl »
(Master)
dear badvogato, posted 4 Jul 2006 at 11:57 UTC by lkcl »
(Master)
what on _earth_ are you wittering about? :)
hardly can gather any strength for anything. heartless to link my name with 'wittering'.
NJ Politics is the funniest and cruelest. 'Everybody wants to be gay. Everybody wants to be happy." Former governor James McGreavy has a book out. He lives in my old neighborhood, hillside historical Queen City now.
AJAX "scaffold", posted 20 Jul 2006 at 15:34 UTC by lkcl »
(Master)