I like the idea but disagree with adding these functions to the global
namespace.  What about adding them to the Storage class? Pseudocode:
>>> request.vars.query = ['one','two']
>>> q = request.vars.getfirst('query')
>>> 'one'

Same for getlast and getall. Thoughts?

On Aug 21, 11:26 am, Kevin <extemporalgen...@gmail.com> wrote:
> Yeah, use case might be as follows (let me know if there's already a
> web2py pattern for this -- I just downloaded the framework a couple
> days ago):
>
> Perhaps a legacy RESTful API that is being porting to web2py -- it
> uses HTTP conventions to query the server, and returns type-
> appropriate content based on the request URL's file extension.
>
> Thus the following would both work as expected when passed to the ,
>
> /search?term=sir+robin&limit=3
> /search.json?term=sir+robin&limit=3
>
> def search():
>   # We're assuming it's a string -- but it could be a list
>   # point of failure.
>   if not re.match('\d+$', request.vars.limit):
>     raise HTTP(400, "limit must be an integer")
>   term = request.vars.term
>   for row in db(db.topics.term.contains(term)).select(db.topics.ALL,
> limitby=(0, int(limit))):
>     pass
>
> However, the following URL would likely raise a TypeError (if not in
> web2py code, at least in commonly-seen naive code):
>
> /search.json?term=sir+robin&term=brave&limit=3
>
> The boilerplate way of fixing the function would be as follows:
>
> def search():
>   limit = request.vars.limit
>   if not isinstance(limit, basestring):
>     limit = limit[0]
>   if not re.match('\d+$', limit):
>     raise HTTP(400, "limit must be an integer")
>   term = request.vars.term
>   if not isinstance(term, basestring):
>     term = term[0]
>   for row in db(db.topics.term.contains(term)).select(db.topics.ALL,
> limitby=(0, int(limit))):
>     pass
>
> The shorthand way of fixing it would be as follows -- note, it also
> trivially extends it to allow multiple terms which are AND'd together
> (in fewer lines than the boiler-plate example):
>
> def search():
>   limit = getfirst(request.vars.limit)
>   if not re.match('\d+$', limit):
>     raise HTTP(400, "limit must be an integer")
>   query = db
>   for term in getall(request.vars.term):
>     query = query(db.topics.term.contains(term))
>   for row in query.select(db.topics.ALL, limitby=(0, int(limit))):
>     pass
>
> Note that in these examples, I've modified the getfirst, getlast, and
> getallfunctionsso that they're specifically designed to work with
> object attributes instead of dictionary keys, which allows for the
> shorter syntax you see here (which is much more useful for working
> with Storage objects, which as of 1.83.2 return None for non-existant
> attributes).  Although it's less flexible, the abbreviated syntax
> makes them much more handy for working with request.vars,
> request.get_vars, and request.post_vars (from which you only
> reasonably expect either lists, or non-lists). So in essence, these
> act as 'scalarify' and 'listify'functions.
>
> Note that "getall(request.vars.non_existant)" will return [] instead
> of [None] (so it's not a 'strict' listify function).
>
> The revised (stillproposedpatch) is athttp://pastebin.com/8f9z4k6J.
> Further examples of API use (in doctest format) are in thepatch.
> Also, I discovered that the *old*patchwould have broken on strings
> (so don't use thatpatch), which is what the code would've been
> dealing about 100% of the time in real-world cases.
>
> On Aug 21, 6:22 am, mdipierro <mdipie...@cs.depaul.edu> wrote:
>
> > I never needed this but I have no opposition to include them.
> > Could you provide a use case?
> >  What do other people think?
>
> > On Aug 21, 12:27 am, Kevin <extemporalgen...@gmail.com> wrote:
>
> > > Hi,
>
> > > this is aproposedpatchtoaddglobalfunctionsfor accessing values
> > > from (in particular) request.vars and friends (any dictionary-like
> > > object will work) in a way that (safely) satisfies the assumption that
> > > the input vars for a given key are either singletons or lists.
>
> > > Thefunctionsare rather simple:
>
> > > Given the request: /a/b/c?x=abc
> > > getfirst(request.vars, 'x') -> 'abc'
> > > getlast(request.vars, 'x') -> 'abc'
> > > getall(request.vars, 'x') -> ['abc']
>
> > > Given the request: /a/b/c?x=abc&x=def
> > > getfirst(request.vars, 'x') -> 'abc'
> > > getlast(request.vars, 'x') -> 'def'
> > > getall(request.vars, 'x') -> ['abc', 'def']
>
> > > getall(request.vars, 'y') -> None
> > > getall(request.vars, 'y') or [] -> []
>
> > > If there is anything like this already, I certainly will retract my
> > > suggestion.  The potentially controversial parts are that the
> > >functionsare defined in gluon.utils (I couldn't find a more logical
> > > place to put them, and it makes no difference to me where they end
> > > up), and they're loaded into the request environment, just like the
> > > html helpers.
>
> > >Patchcan be found at:http://pastebin.com/g6Vs9PrU
>
> > > Background/motivation:
>
> > > This function group is inspired by the behavior of <http://
> > > pythonpaste.org/webob/reference.html#query-post-variables> and similar
> > > functionality that other frameworks provide, and would be particularly
> > > useful in cases where the client-side code is not managed by something
> > > like web2py's FORM interface -- as of version 1.83.2, web2py prepares
> > > a Storage instance such that:
>
> > > /a/b/c?x=5 -> request.vars.x == '5'
> > > /a/b/c?x=5&x=abc -> request.vars.x == ['5', 'abc']
>
> > > This could lead to naive code like the following to fail with some
> > > simple request fakery:
>
> > > if request.vars.search.upper().startswith('FUZZY'): pass # some real
> > > code here
>
> > > It's possible that this kind of fakery could also lead to many of the
> > > web2py validators failing in common cases (though I haven't looked
> > > into that much).
>
> > > However, it is often allowable that the first (or last) value passed
> > > is authoritative, leading to a more robust system.
>
>

Reply via email to