Settling onto decent python web technologies

Posted 20 Apr 2006 at 22:55 UTC by lkcl Share This

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.

    • don't use php.

    • don't use psp (part of mod_python).

    • only use dtml to do true/false tests (dtml-if variable), substitute pre-computed variables (not the results of functions being calculated) and "dtml-in list-of-dicts mapping" (to do tables etc.)

      avoid dtml-call and dtml-let like the friggin plague.

    • in fact, don't use zope at all (for custom web development and programming) - all the features you're likely to need can be found in any python-standard-WSGI-compliant python web server framework along with some standard libraries that you, a debian-lover, can apt-get install within minutes.

    • do programming in a programming language and leave the html bits to web designers: never the twain shall meet.

    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?

javascript turned off, posted 21 Apr 2006 at 13:07 UTC by lkcl » (Master)

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>

http://developer.apple.com/internet/webcontent/iframe.html, posted 22 Apr 2006 at 02:14 UTC by lkcl » (Master)

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!

What's Your Take On C# ASP.Net?, posted 23 Apr 2006 at 11:27 UTC by MichaelCrawford » (Master)

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.

kid - more templates!, posted 24 Apr 2006 at 09:28 UTC by lkcl » (Master)

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.

HTMLdict - for HTMLTemplate, posted 28 May 2006 at 14:19 UTC by lkcl » (Master)

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)

gather here - keep going and going and going :) cross borders, posted 29 May 2006 at 16:06 UTC by badvogato » (Master)

test embedded greeting card (1600x1200pix) from Jones Farm, West Trenton NJ.

my other half is terrific, posted 30 May 2006 at 14:41 UTC by badvogato » (Master)

look at her through this lens. what you see ain't what you get.

http://advajax.anakin.us/index-en.htm, posted 1 Jun 2006 at 13:43 UTC by lkcl » (Master)

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.

LAMP bitchin article, posted 6 Jun 2006 at 14:41 UTC by lkcl » (Master)

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 :)

support for different browser functionality levels, posted 7 Jun 2006 at 11:09 UTC by lkcl » (Master)

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 :)

article on mod_python, posted 14 Jun 2006 at 19:52 UTC by lkcl » (Master)

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)

AAAAAGH! NOOOOOOOOO! :)

ajax mistakes, posted 14 Jun 2006 at 21:51 UTC by lkcl » (Master)

google search for "ajax mistakes" shows up a whole stack of useful articles.

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.

'gather here' is in the flooding zone of West Trenton NJ, posted 29 Jun 2006 at 15:43 UTC by badvogato » (Master)

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)

more ajax stuff - very good.

dear badvogato, posted 4 Jul 2006 at 11:57 UTC by lkcl » (Master)

what on _earth_ are you wittering about? :)

man. i was homeless and now jobless, posted 5 Jul 2006 at 10:26 UTC by badvogato » (Master)

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)

http://www.ajaxscaffold.com/

hmm, there appears to be a lot of ruby-ajax-integration - haven't found _anything_ like it for python.

http://www.djangoproject.com/documentation/templates/, posted 7 Oct 2006 at 20:29 UTC by lkcl » (Master)

django looks brilliant

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!

X
Share this page