-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Krishnakant,
On 19 Dec 2009, at 09:35, Krishnakant wrote: > I thought I would put this question in a different way, not just to > keep > it on-topic for pylons list and make my mis-understanding clear about > the issue at hand. Understood - and answered in a similar spirit ... > My ownly question is that when we do @jsonify, do we need to say which > action we want to use for returning a json response or just putting > the > @jsonify asumes that the action following that line is the one to be > used for responding to json requests? Following up on Mario's post ... "@jsonify" is an example of a Python decorator. Unfortunately, the concept of a "decorator" in Python is not terribly well documented as demonstrated by a couple of probes of the official Python documentation (don't bother following these two links, KK, they'd just be a waste of your time): http://docs.python.org/search.html?q=decorators http://docs.python.org/glossary.html#term-decorators Probably the best overview description of decorators appears in Python's wikipedia entry (keep it for later, KK): http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Decorators Let's see if we can produce a succinct distillation from the wikipedia entry: "A decorator is a Python object that can be called with a single argument and which modifies a function or a method. Python decorators were inspired in part by Java annotations, and have a similar syntax; the decorator syntax is pure syntactic sugar and uses "@" as the keyword, e.g. "@jsonify". Decorators are a form of metaprogramming; they enhance the action of the function or method they decorate." To decorate a function or a method, simply reference the decorator in the line immediately prior to the function/method definition, as shown in this pseudo-code: > @jsonify > def entityname(self, id): > dbentity = model.Session.query(model.MyEntity).get(id) > return {"name": dbentity.name} Decorators are often used as "convenience functions" (to use a broad term, broadly), here's the (slightly edited) Pylons code for @jsonify: > import simplejson > from decorator import decorator > from pylons.decorators.util import get_pylons > > def jsonify(func, *args, **kwargs): > """Action decorator that formats output for JSON > > Given a function that will return content, this decorator will > turn > the result into JSON, with a content-type of 'application/json' > and > output it. > > """ > pylons = get_pylons(args) > pylons.response.headers['Content-Type'] = 'application/json' > data = func(*args, **kwargs) > if isinstance(data, (list, tuple)): > msg = "JSON responses with Array envelopes are susceptible > to " \ > "cross-site data leak attacks, see " \ > "http://pylonshq.com/warnings/JSONArray" > warnings.warn(msg, Warning, 2) > log.warning(msg) > log.debug("Returning JSON wrapped action output") > return simplejson.dumps(data) > > jsonify = decorator(jsonify) [ BTW, the URL referenced in the warning doesn't actually resolve, so you'll just have to use your imagination as to exactly how dire is the detail of the warning about JSONArrays :-) ] As you can see, it's a convenience. One could almost as easily set the response header oneself and similarly return the result of simplejson.dumps(data). Note: as Mario observed, when using Pylons, you don't need to return a rendered template. If you wanted, your action could be as simple as: > def test_action(self): > return "Hello world" or > def test_action(self): > return ["Hello world"] Basically, you can return anything you like, just so long as it can be iterated. It's a WSGI thing rather than anything Pylons-specific but it explains why you can return just simplejson.dumps(data) without having to use a template as a vehicle for carrying the result. Sometimes the convenience offered by decorators is significant. Here is the code for an "@authorize" decorator (used in the Shabti auth'n'auth augmented Pylons project template) which can be used to guard a controller action by imposing access-control permissions: > def authorize(permission): > > """Decorator for authenticating individual actions. Takes a > permission > instance as argument(see lib/permissions.py for examples)""" > def wrapper(func, self, *args, **kw): > if permission.check(): > return func(self, *args, **kw) > pylons.session['redirect'] = \ > pylons.request.environ['pylons.routes_dict'] > pylons.session.save() > redirect_to_login() > return decorator(wrapper) Here's an example of a permission instance: > class SignedIn(object): > def check(self): > return (get_user() is not None) and the decorator is used typically thus (minimalist code, purely as an example): > @authorize(SignedIn()) > def delete(self, id): > dbentity = get_object_or_404(model.MyEntity, id=id) > model.Session.delete(dbentity) > model.Session.commit() The @authorize decorator causes the permissions code to execute before the method/action is called. If the user is not authenticated, they are redirected to a login page. If the user /is/ authenticated, the decorator code simply calls the controller action. Judicious use of decorators can keep your controller code quite tightly focused on the data marshalling task that it's directly aimed at, leaving the decorators to handle ancillary-yet-necessary functionality such as protecting from XSS and other infections. This is in addition to potentially handling other useful ancillary functionality that also results in controller actions being neatly focused on the task in hand. I endorse Mario's reference of http://pylonsbook.com/en/1.1/css-javascript-and-ajax.html#json - that URL will position you accurately in that chapter at James' useful explanation of using JSON with Pylons. It is also very likely that you will find the prior sections of that chapter significantly useful as they deal with Ajax and debugging Ajax requests as well presenting an extensive description of using YUI, which IIRC you specifically enquired about. If you come across anything in there that you need explaining differently or you need to have a missing "longdesc" for an img provided, feel free to ask. (You have my email address and I will be happy to help). You may not have picked up on this yet but ... the PylonsHQ web site is a Pylons instance, written by Ben Bangert and the source code is available on bitbucket [1]. I have found it an invaluable guide to the practice of using Pylons code and concepts. For example, here is a chunk from the account registration controller showing how Ben uses decorators to excellent effect: > @rest.dispatch_on(POST='_process_login') > def login(self): > redir = request.GET.get('redir') > if redir and redir.startswith('/'): > session['redirect'] = str(redir) > session.save() > return render('/accounts/login.mako') > > @validate(form=forms.login_form, error_handler='login') > @secure.authenticate_form > def _process_login(self): > user = self.form_result['user'] > user.process_login() > success_flash('You have logged into PylonsHQ') > if session.get('redirect'): > redir_url = session.pop('redirect') > session.save() > redirect_to(redir_url) > redirect_to('home') Taking a quick look at the use of the "@rest" decorator (without going into tedious detail), a GET request on "/accounts/login" causes the method to execute, returning the rendered template "login.mako". However a POST request to "/accounts/login" causes a dispatch to the _process_login method which is then called (in turn) by the attendant decorator chain. Ben's use of "@validate" and "@secure" decorators for _process_login shows how decorators can easily be "chained" - they are simply executed in order of appearance, each one "guarding" the next. I guess there's only one thing left to cover which is: how to get a controller action to produce json on request. You have a couple of choices: 1. Create a dedicated action, e.g. "fetch_model_as_json" and (as shown above) either decorate it with @jsonify or handle the simplejson.dumps of the data yourself, then call the action explicitly via Ajax or whatever: /mycontroller/fetch_model_as_json" 2. Use Routes' "RESTful service" [2] to pass through a "format" specifier. As the Routes manual describes: "Several routes are paired with an identical route containing the format variable. The intention is to allow users to obtain different formats by means of filename suffixes; e.g., “/messages/1.xml”. This produces a routing variable “xml”, which in Pylons will be passed to the controller action if it defines a formal argument for it." For example, you can generate a format-specific request like this: > url("message", id=1, format="json") => "/messages/1.json" the "format" keyword arg and its bound value will be available when the action is called: > def view(self, id, format='html'): > if format == 'json': > # return some json > else: > # return some HTML, possibly via a rendered mako template Then you can call "messages/1" and get HTML returned or call "/message/ 1.json" and get JSON returned (or, if you've coded for it) call "/ message/1.xml" and get XML returned. HTH. References: [1] http://bitbucket.org/bbangert/kai/ [2] http://routes.groovie.org/manual.html#restful-services (KK, I tried to distinguish code from narrative by quoting it. Is that an effective approach to take for use with a screen reader? If there's a specific style that suits a screen reader better, then I'd be happy to learn of it.) Cheers, Graham http://www.linkedin.com/in/ghiggins -----BEGIN PGP SIGNATURE----- iEYEARECAAYFAkss934ACgkQOsmLt1NhivziDQCgiL77dqtYGxe4wletiWOd8nzj Tg4An1MesrWn9rWZXReUcfXk/ttVY3SNiQCVAgUBSyz3flnrWVZ7aXD1AQKexwP/ Yaw7/jqnqc7IvX0zgWLa8pi0CxMoPto7SE62VWUQ3hBOerXGt6KO8nsgZgPNlL6g v+dVnftQJm0Lt/2L6krXQolwBmjx4otcA26RRryfVfKPyFAIqL8PHEYzXnumNCmS TSwt0UH5qGPmLTb2IOUAe1WsWnMMEMyc/WZ2WqfBkV0= =MqSG -----END PGP SIGNATURE----- -- You received this message because you are subscribed to the Google Groups "pylons-discuss" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/pylons-discuss?hl=en.
