Forms dynamism problem with Identity.

7 views
Skip to first unread message

samuraisam

unread,
Jun 20, 2006, 2:57:54 AM6/20/06
to TurboGears
I have a form:

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:

def action(self, tg_errors=None, *args, **kw):
my_form.fields.append(my_dymanically_generated_SingleSelectField)

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.

Thanks,
-Sam

Kevin Dangoor

unread,
Jun 20, 2006, 6:41:30 AM6/20/06
to turbo...@googlegroups.com
On Jun 20, 2006, at 2:57 AM, samuraisam wrote:

>
> I have a form:
>
> 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.

http://trac.turbogears.org/

Thanks!

Kevin

samuraisam

unread,
Jun 20, 2006, 12:56:31 PM6/20/06
to TurboGears
Alright, here: http://trac.turbogears.org/turbogears/ticket/978 is the
ticket. If I did something wrong please let me know.

If you can think of a workaround, please let me know that, too. This
is, for this application, a "blocker."

Thanks for your attention to this problem,
-Sam

Kevin Dangoor

unread,
Jun 20, 2006, 1:04:12 PM6/20/06
to turbo...@googlegroups.com
On Jun 20, 2006, at 12:56 PM, samuraisam wrote:

>
> Alright, here: http://trac.turbogears.org/turbogears/ticket/978 is the
> ticket. If I did something wrong please let me know.
>
> 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).

Kevin

Ksenia Marasanova

unread,
Jun 20, 2006, 2:00:30 PM6/20/06
to turbo...@googlegroups.com
On 6/20/06, samuraisam <samur...@gmail.com> wrote:
>
> Alright, here: http://trac.turbogears.org/turbogears/ticket/978 is the
> ticket. If I did something wrong please let me know.
>
> 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...

Alberto Valverde

unread,
Jun 20, 2006, 2:46:42 PM6/20/06
to turbo...@googlegroups.com

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

samuraisam

unread,
Jun 20, 2006, 7:16:43 PM6/20/06
to TurboGears
> 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?

-Sam

Kevin Dangoor

unread,
Jun 20, 2006, 10:47:34 PM6/20/06
to turbo...@googlegroups.com
On Jun 20, 2006, at 7:16 PM, samuraisam wrote:

> 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.)

Kevin

samuraisam

unread,
Jun 21, 2006, 4:20:35 AM6/21/06
to TurboGears
I think this:

1 def _get_sample_option(self):
2 if self.options:
3 if isinstance(self.options[0][1], list):
4 sample = self.options[0][1][0]
5 else:
6 sample = self.options[0]
7 return sample[0]
8 else:
9 return None

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.

-Sam

Alberto Valverde

unread,
Jun 21, 2006, 5:38:20 AM6/21/06
to turbo...@googlegroups.com

On 21/06/2006, at 10:20, samuraisam wrote:

>
> I think this:
>
> 1 def _get_sample_option(self):
> 2 if self.options:
> 3 if isinstance(self.options[0][1], list):
> 4 sample = self.options[0][1][0]
> 5 else:
> 6 sample = self.options[0]
> 7 return sample[0]
> 8 else:
> 9 return None
>
> 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... :)

Alberto

samuraisam

unread,
Jun 22, 2006, 9:16:44 PM6/22/06
to TurboGears
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.

-Sam

Kevin Dangoor

unread,
Jun 23, 2006, 8:38:42 AM6/23/06
to turbo...@googlegroups.com

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:

tf = TextField(name="test", default="Hi!")
print tf.render("Bye!")

The second line will print the text field with a value of "Bye!". The
whole purpose of default is to handle the case where no value is
passed in.

In answer to your question, here's the 1.0 branch:

http://www.turbogears.org/svn/turbogears/branches/1.0/

You should be able to just to "python setup.py develop" in that
checkout to start working with the latest 1.0 code.

Kevin

Reply all
Reply to author
Forward
0 new messages