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?
myvar = request.vars.get('myvar', 'my_default_value')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.
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?
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 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.
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.
myvar = request.vars('myvar', otherwise=URL('default', 'other')) 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.