class X(widgets.WidgetsList): blog = widgets.SingleSelectField(label="Blog", options=get_blogs)
with a function:
def get_blogs(): return [(blog.id, blog.title) for blog in identity.current.identity().user.blogs]
and as seen in this (unresolved) thread: http://groups.google.com/group/turbogears/browse_thread/thread/b1c764... it is impossible (or at least to the best of my knowledge) to access identity when a form is created because, again, to the best of my knowledge and from the errors TurboGears is giving me, that happens outside of a request. Specifically:
turbogears.identity.exceptions.RequestRequiredException: An attempt was made to use a facility of the TurboGears Identity Management framework that relies on an HTTP request outside of a request.
What I need is a way to make it so I can dynamically add options to X.blog from /within/ a request. I imagine I can do this by manipulationg the TableForm object it's placed into, but even that is created /outside/ of a request. So, still, I need to create options my SingleSelectField on the fly from wthin the action. How, though? I have gotten this far:
But then, why even use the forms api? I have to roll my own validation for that and other fields that are generated depending on the user accessing the action. If someone could please look at my problem, I'd be very greatful.
> class X(widgets.WidgetsList): > blog = widgets.SingleSelectField(label="Blog", options=get_blogs)
> with a function:
> def get_blogs(): > return [(blog.id, blog.title) > for blog in identity.current.identity().user.blogs]
> turbogears.identity.exceptions.RequestRequiredException: An attempt > was > made to use a facility of the TurboGears Identity Management framework > that relies on an HTTP request outside of a request.
> What I need is a way to make it so I can dynamically add options to > X.blog from /within/ a request. I imagine I can do this by > manipulationg the TableForm object it's placed into, but even that is > created /outside/ of a request. So, still, I need to create options my > SingleSelectField on the fly from wthin the action. How, though? I > have > gotten this far:
That sounds like a bug. That's exactly the point of passing in a callable: it should be resolved at request time.
Please file a bug report, if there isn't one already. Give it a 1.0 milestone.
> If you can think of a workaround, please let me know that, too. This > is, for this application, a "blocker."
Unfortunately, I don't have time to investigate this now. There is one suggestion I can make. In turbogears.util, there's a function called request_available that you can call to check to see if there is a request available... maybe return a dummy list of options if the request is not available. I'm wondering if the problem is that the widget is calling that function multiple times (when the request is not available, and then again later when the request is available).
> If you can think of a workaround, please let me know that, too. This > is, for this application, a "blocker."
If Kevin is right, and the widget is calling the function multiple times, you probably can use an error handler that will ignore the first call outsite of request...
> class X(widgets.WidgetsList): > blog = widgets.SingleSelectField(label="Blog", options=get_blogs)
> with a function:
> def get_blogs(): > return [(blog.id, blog.title) > for blog in identity.current.identity().user.blogs]
> and as seen in this (unresolved) thread: > http://groups.google.com/group/turbogears/browse_thread/thread/ > b1c764b56c1153eb/477c254accec0402? > it is impossible (or at least to the best of my knowledge) to access > identity when a form is created because, again, to the best of my > knowledge and from the errors TurboGears is giving me, that happens > outside of a request. Specifically:
> turbogears.identity.exceptions.RequestRequiredException: An attempt > was > made to use a facility of the TurboGears Identity Management framework > that relies on an HTTP request outside of a request.
> What I need is a way to make it so I can dynamically add options to > X.blog from /within/ a request. I imagine I can do this by > manipulationg the TableForm object it's placed into, but even that is > created /outside/ of a request. So, still, I need to create options my > SingleSelectField on the fly from wthin the action. How, though? I > have > gotten this far:
> But then, why even use the forms api? I have to roll my own validation > for that and other fields that are generated depending on the user > accessing the action. If someone could please look at my problem, I'd > be very greatful.
Hmmm, my guess is that "get_blogs" is being called outside the request when the SingleSelectField is trying to guess a validator. Can you please confirm this guess by providing a validator to the widget?
from turbogears.validators import Int class X(widgets.WidgetsList): blog = widgets.SingleSelectField(label="Blog", options=get_blogs, validator=Int())
Should be enough.
If this works I'm not sure if it's a widgets' bug but a corner case involving callables which need a request availble *for* the SingleSelectField (which is the only one which tries to guess a validator).
Maybe remove this "guessing" behaviour? It clearly contradict the "in the case of ambiguity avoid the temptation to guess" principle IMO.
> Hmmm, my guess is that "get_blogs" is being called outside the > request when the SingleSelectField is trying to guess a validator. > Can you please confirm this guess by providing a validator to the > widget?
> from turbogears.validators import Int > class X(widgets.WidgetsList): > blog = widgets.SingleSelectField(label="Blog", > options=get_blogs, validator=Int())
> Should be enough.
> If this works I'm not sure if it's a widgets' bug but a corner case > involving callables which need a request availble *for* the > SingleSelectField (which is the only one which tries to guess a > validator).
> Maybe remove this "guessing" behaviour? It clearly contradict the "in > the case of ambiguity avoid the temptation to guess" principle IMO.
> Alberto
I've tried it with and without a validator. The validators that I've tried were Int() and NotEmpty, both of them with the same result. That's not to say it still isn't calling it outside of the request to guess the validator, that's entirley possible. I'll more closely examine the stack trace to see where exactly I'm getting the error (that's assuming the stack trace offers anything of value). If I track anything down and fix it, does turbogears have tests in place for this that I can run?
> I've tried it with and without a validator. The validators that I've > tried were Int() and NotEmpty, both of them with the same result. > That's not to say it still isn't calling it outside of the request to > guess the validator, that's entirley possible. I'll more closely > examine the stack trace to see where exactly I'm getting the error > (that's assuming the stack trace offers anything of value). If I track > anything down and fix it, does turbogears have tests in place for this > that I can run?
Just run "nosetests" from the top level directory of a checkout. These days, we're making most changes against the 1.0 branch and merging to the trunk from there. (In other words, make a patch against the 1.0 branch, rather than the trunk.)
is the culpirt. I'm not super duper knowledgible about TurboGears yet, but it looks like it digs into options without checking if it's callable first. So, I'm guessing Alberto Valverde was right in pointing out the validation guessing. But still, I got the same error as posted above with or without specifiying validation. Perhaps it does not check elsewhere, too.
Using turbogears.utils.request_available worked good, though:
def get_blogs(): if request_available(): return [(blog.id, blog.title) for blog in identity.current.identity().user.authorships]
Despite not being how it *should* work, it's fine for now and this problem, thankfully, no longer has me stopped in my tracks.
> is the culpirt. I'm not super duper knowledgible about TurboGears yet, > but it looks like it digs into options without checking if it's > callable first. So, I'm guessing Alberto Valverde was right in > pointing > out the validation guessing. But still, I got the same error as posted > above with or without specifiying validation. Perhaps it does not > check > elsewhere, too.
This is weird.... A little (very little) investigation shows that _get_sample_options is only called from _guess_validator (http:// trac.turbogears.org/turbogears/browser/branches/1.0/turbogears/ widgets/forms.py#L727) and _guess_validator is *only* called if bool(widget's validator) evaluates to False (http://trac.turbogears.org/turbogears/browser/ branches/1.0/turbogears/widgets/forms.py#L710)
Maybe the check "not self.validator" is not enough... can you try to provide a validator and change that check to "if self.validator is None" see what happens?
I'm sorry but I'm very busy to investigate this myself ATM :( so any help would be greatly appreciated :) (maybe send me a the smallest chunk of your app that can reproduce the problem?)
> Using turbogears.utils.request_available worked good, though:
> def get_blogs(): > if request_available(): > return [(blog.id, blog.title) > for blog in > identity.current.identity().user.authorships]
This is a rather hackish solution IMO, callables listed at "params" (SingleSelectField's "options", for example) should only be called when rendering/displaying a widget. I think it's better we solve the root of the problem sooner or later, though I understand the fix is good enough to keep you running... :)
Alright, if this hasn't been fixed I'm going to start fixing it. How do I update the turbogears egg from the latest snapshot? Also, what's the SVN address for the 1.0 branch? I would like to make default (when creating an instance of a widget) to be callable at render/display time. For instance, from what I can tell I have to create an entirley new widget and override update_params() _just_ to set defaults at runtime, and it's a *major* pain.
> Alright, if this hasn't been fixed I'm going to start fixing it. > How do > I update the turbogears egg from the latest snapshot? Also, what's the > SVN address for the 1.0 branch? I would like to make default (when > creating an instance of a widget) to be callable at render/display > time. For instance, from what I can tell I have to create an entirley > new widget and override update_params() _just_ to set defaults at > runtime, and it's a *major* pain.
default is just a fallback value if no value is presented. The equivalent at render time is just the value you pass in. In other words: