Equivalent of request.args(...) for request.vars(...)

463 views
Skip to first unread message

Brendan Barnwell

unread,
Nov 14, 2016, 1:53:33 AM11/14/16
to web2py-users
The request.args object has a handy callable interface that allows specification of default value, type to coerce to, etc.  As far as I can tell, the request.vars object has no such interface.  Is this right?  Why is this?  What can I do if I want to access various vars that may or may not be present, with the same sort of fallback options as are available with args?

Anthony

unread,
Nov 14, 2016, 12:50:19 PM11/14/16
to web2py-users
On Monday, November 14, 2016 at 1:53:33 AM UTC-5, Brendan Barnwell wrote:
The request.args object has a handy callable interface that allows specification of default value, type to coerce to, etc.  As far as I can tell, the request.vars object has no such interface.  Is this right?  Why is this?  What can I do if I want to access various vars that may or may not be present, with the same sort of fallback options as are available with args?

Keep in mind, your app is written in Python -- you are not limited to the web2py API -- just use Python to achieve what you want. request.vars is a dict-like object, so for example, you can do:

myvar = request.vars.get('myvar', 'my_default_value')

Anthony

Brendan Barnwell

unread,
Nov 14, 2016, 1:29:11 PM11/14/16
to web2py-users
Of course, but that's equally true of the "List" class that provides the request.args functionality.  That's just Python code.  I could write it myself.  But web2py provides it for convenience.  I'm just saying it would be nice to have similar convenience for request.vars.

You're right that dict.get handles the default value, but the functionality available for args includes substantially more than that.  It's only about 20 lines of code, but it does at least three different things:

1. use the default value if provided
2. convert the value using the "cast" parameter if provided
3. redirect to an error page if given in the "otherwise" parameter

Plain dicts don't do all those things, nor do Storage objects.

For now, what I did is just write a BetterStorage class that inherits from Storage and includes the __call__ method from List (with a couple modifications at the beginning because it's delegating to dict keys rather than list indices).  Would there be interest in making this change to web2py?

Anthony

unread,
Nov 14, 2016, 4:01:26 PM11/14/16
to web2py-users
On Monday, November 14, 2016 at 1:29:11 PM UTC-5, Brendan Barnwell wrote:
Of course, but that's equally true of the "List" class that provides the request.args functionality.  That's just Python code.  I could write it myself.  But web2py provides it for convenience.  I'm just saying it would be nice to have similar convenience for request.vars.

Got it. I was just reacting to your "What can I do..." question, which made it seem as if you were at a loss for alternatives.
 
You're right that dict.get handles the default value, but the functionality available for args includes substantially more than that.  It's only about 20 lines of code, but it does at least three different things:

1. use the default value if provided
2. convert the value using the "cast" parameter if provided
3. redirect to an error page if given in the "otherwise" parameter

Plain dicts don't do all those things, nor do Storage objects.

There's also a fourth feature of the __call__ method of the List class (of which request.args is an instance), which is that an out of bound index returns None rather than generating an error. Now, let's consider the Storage class (and request.vars) with regard to these four features:
  • Like the out-of-bound index behavior of the List class, if you attempt to access a non-existent key/attribute, the Storage class returns None rather than generating an error.
  • As already noted, with request.vars, you can specify a default via the inherited .get method.
  • The "otherwise" parameter would be less relevant for request.vars. Unlike request.vars, request.args captures part of the URL path, so it makes sense to validate that the entire expected path exists and possibly redirect otherwise (not saying this would never be the case with request.vars, just likely more common when dealing with the URL path).

That leaves the "cast" functionality, and I'm not sure it would be worth implementing a special __call__ method just for casting.


For now, what I did is just write a BetterStorage class that inherits from Storage and includes the __call__ method from List (with a couple modifications at the beginning because it's delegating to dict keys rather than list indices).  Would there be interest in making this change to web2py?

I don't have a strong opinion but would lean towards no, as I think it is best to avoid adding code to the framework, unless we expect this to be a very common use case. But others may have a different opinion, so feel free to bring it up on the developers list.

Anyway, how are you using BetterStorage? Your initial post referred specifically to request.vars -- are you converting request.vars to BetterStorage? In that case, it might be simpler to write a function rather than a whole new class just for this purpose.

Anthony

Brendan Barnwell

unread,
Nov 15, 2016, 3:08:14 PM11/15/16
to web2py-users
On Monday, November 14, 2016 at 1:01:26 PM UTC-8, Anthony wrote:
On Monday, November 14, 2016 at 1:29:11 PM UTC-5, Brendan Barnwell wrote:
You're right that dict.get handles the default value, but the functionality available for args includes substantially more than that.  It's only about 20 lines of code, but it does at least three different things:

1. use the default value if provided
2. convert the value using the "cast" parameter if provided
3. redirect to an error page if given in the "otherwise" parameter

Plain dicts don't do all those things, nor do Storage objects.

There's also a fourth feature of the __call__ method of the List class (of which request.args is an instance), which is that an out of bound index returns None rather than generating an error. Now, let's consider the Storage class (and request.vars) with regard to these four features:
  • Like the out-of-bound index behavior of the List class, if you attempt to access a non-existent key/attribute, the Storage class returns None rather than generating an error.
  • As already noted, with request.vars, you can specify a default via the inherited .get method.
  • The "otherwise" parameter would be less relevant for request.vars. Unlike request.vars, request.args captures part of the URL path, so it makes sense to validate that the entire expected path exists and possibly redirect otherwise (not saying this would never be the case with request.vars, just likely more common when dealing with the URL path).

That leaves the "cast" functionality, and I'm not sure it would be worth implementing a special __call__ method just for casting.


For my usage, anyway, the "otherwise" parameter is just as relevant in either case.  I see args and vars as parallel to passing arguments to Python functions by position and by keyword.  In either case, I might want to raise an error if a given argument is not present.  But unlike when calling a plain Python function, web2py doesn't provide a builtin way to validate the arguments of a request (i.e., as far as I can tell, there's no way to stipulate that a given controller function requires particular arguments, in the URL path or query string).  So "otherwise" can provide a way to do what would normally be done by Python's enforcement of a function's call signature, but with the extra flexibility of potentially raising different errors for different kinds of mismatch.
 
For now, what I did is just write a BetterStorage class that inherits from Storage and includes the __call__ method from List (with a couple modifications at the beginning because it's delegating to dict keys rather than list indices).  Would there be interest in making this change to web2py?

I don't have a strong opinion but would lean towards no, as I think it is best to avoid adding code to the framework, unless we expect this to be a very common use case. But others may have a different opinion, so feel free to bring it up on the developers list.


Perhaps I will do that.
 
Anyway, how are you using BetterStorage? Your initial post referred specifically to request.vars -- are you converting request.vars to BetterStorage? In that case, it might be simpler to write a function rather than a whole new class just for this purpose.

Not sure what you mean by "a whole new class".  My BetterStorage class inherits from Storage, and adds nothing except the __call__ method.  So I essentially did just write a function, but putting that function as a method on a class lets me inherit all the existing functionality of Storage.
 

Anthony

unread,
Nov 15, 2016, 4:54:51 PM11/15/16
to web2py-users

For my usage, anyway, the "otherwise" parameter is just as relevant in either case.  I see args and vars as parallel to passing arguments to Python functions by position and by keyword.

Understood, but because request.args are part of the URL path, it is more likely that they will be required for a valid resource to be returned, whereas URLs that include query strings typically return some resource even without the query string (i.e., at the base URL).
 
  In either case, I might want to raise an error if a given argument is not present.  But unlike when calling a plain Python function, web2py doesn't provide a builtin way to validate the arguments of a request (i.e., as far as I can tell, there's no way to stipulate that a given controller function requires particular arguments, in the URL path or query string).  So "otherwise" can provide a way to do what would normally be done by Python's enforcement of a function's call signature, but with the extra flexibility of potentially raising different errors for different kinds of mismatch.

If you want to validate a set of required arguments, there are probably better ways than making separate calls to a method for each argument. I would write a generalized function or a decorator to take some specification and confirm that request.args and request.vars meet the specification. Anyway, right now, instead of:

    myvar = request.vars('myvar', otherwise=URL('default', 'other'))

you can simply do:

    myvar = request.vars.myvar or redirect(URL('default', 'other'))

Anyway, how are you using BetterStorage? Your initial post referred specifically to request.vars -- are you converting request.vars to BetterStorage? In that case, it might be simpler to write a function rather than a whole new class just for this purpose.

Not sure what you mean by "a whole new class".  My BetterStorage class inherits from Storage, and adds nothing except the __call__ method.  So I essentially did just write a function, but putting that function as a method on a class lets me inherit all the existing functionality of Storage.

Sure, just a matter of preference -- I tend to go for a function if a class doesn't add any further benefit.

Anthony
Reply all
Reply to author
Forward
0 new messages