CrudRestController and @before_validate

59 views
Skip to first unread message

Moritz Schlarb

unread,
Apr 15, 2012, 5:54:58 PM4/15/12
to turbo...@googlegroups.com
Hi there!

I'm subclassing EasyCrudRestController and now I have an attribute self.event, that I don't want to be changed in the add and edit forms, but that needs to be present when writing the form to the database.
I thought about using a @before_validate hook to simply insert it into kwargs, but that doesn't work since @before_validate doesn't seem to get called (no logging).
Another alternative would be using a HiddenField, but then I would have to insert the value into the AddForm, which I don't know how to do.

Any help would be nice! ;)

Thanks and good night,
Moritz

Alessandro Molina

unread,
Apr 16, 2012, 4:36:07 AM4/16/12
to turbo...@googlegroups.com
How are you registering the decorator? It should work correctly.
I tested it on the fly and it seems to correctly work as expected.

class MyGroupRestController(EasyCrudRestController):
model = model.Group

def before_put(self, *args, **kw):
print args, kw

def __init__(self, *args, **kw):
super(MyGroupRestController, self).__init__(*args, **kw)
before_validate(self.before_put)(self.put)

There are other ways to register the hook, but they should all work correctly.

> --
> You received this message because you are subscribed to the Google Groups
> "TurboGears" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/turbogears/-/qTqkd_d9t0gJ.
> To post to this group, send email to turbo...@googlegroups.com.
> To unsubscribe from this group, send email to
> turbogears+...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/turbogears?hl=en.

Moritz Schlarb

unread,
Apr 16, 2012, 7:28:36 PM4/16/12
to turbo...@googlegroups.com
Ooooh noo....
I think I was doing something incredibily stupid...

I'm not sure what I coded yesterday, I don't seem to have saved it, but I think I was using the decorator wrongly.

I think it was something like:

@before_validate()
def before_put(...):

Thanks a lot!

Moritz Schlarb

unread,
Apr 17, 2012, 4:07:15 AM4/17/12
to turbo...@googlegroups.com
Alessandro, a few additional questions came up ;):

I got:

class LessonsCrudController(FilteredCrudRestController):
    model = Lesson
    __table_options__ = {...}
    __form_options__ = {...}
    
    def inject_event(self, *args, **kw):
        log.debug('inject_event')
        for a in args:
            log.debug(a)
        for k in kw:
            log.debug('%s: %s' % (k,kw[k]))
    
    def __init__(self, *args, **kw):
        super(LessonsCrudController, self).__init__(*args, **kw)
        before_validate(self.inject_event)(self.post)

And that is what I got on the logs:

09:54:46,751 DEBUG [sauce.controllers.crc] inject_event
09:54:46,751 DEBUG [sauce.controllers.crc] []
09:54:46,751 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,751 DEBUG [sauce.controllers.crc] inject_event
09:54:46,751 DEBUG [sauce.controllers.crc] []
09:54:46,751 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,751 DEBUG [sauce.controllers.crc] inject_event
09:54:46,752 DEBUG [sauce.controllers.crc] []
09:54:46,752 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,752 DEBUG [sauce.controllers.crc] inject_event
09:54:46,752 DEBUG [sauce.controllers.crc] []
09:54:46,752 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,752 DEBUG [sauce.controllers.crc] inject_event
09:54:46,752 DEBUG [sauce.controllers.crc] []
09:54:46,752 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,752 DEBUG [sauce.controllers.crc] inject_event
09:54:46,752 DEBUG [sauce.controllers.crc] []
09:54:46,752 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,753 DEBUG [sauce.controllers.crc] inject_event
09:54:46,753 DEBUG [sauce.controllers.crc] []
09:54:46,753 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,753 DEBUG [sauce.controllers.crc] inject_event
09:54:46,753 DEBUG [sauce.controllers.crc] []
09:54:46,753 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,753 DEBUG [sauce.controllers.crc] inject_event
09:54:46,753 DEBUG [sauce.controllers.crc] []
09:54:46,753 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,753 DEBUG [sauce.controllers.crc] inject_event
09:54:46,753 DEBUG [sauce.controllers.crc] []
09:54:46,754 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,754 DEBUG [sauce.controllers.crc] inject_event
09:54:46,754 DEBUG [sauce.controllers.crc] []
09:54:46,754 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}
09:54:46,754 DEBUG [sauce.controllers.crc] inject_event
09:54:46,754 DEBUG [sauce.controllers.crc] []
09:54:46,754 DEBUG [sauce.controllers.crc] {'event': u'', 'name': u'text', 'teacher': u'3', 'lesson_id': u'text', 'sprox
_id': u'', 'id': u''}

So you see, the hook gets called very often! It's not event a constant number, at first I counted 6 calls, now its more than 10.
And the arguments are (not merely wrong but) unexected both packed in args like args = [[], {}].

What am I doing wrong NOW? :D

Thanks a lot,
best wishes,
Moritz

Alessandro Molina

unread,
Apr 17, 2012, 4:35:42 AM4/17/12
to turbo...@googlegroups.com
Sorry, my fault.
I forgot that Pylons based versions of TG reallocate the controller on
each request, as you are hooking the before_validate inside the
__init__ this has the side effect of registering the validate hook
each time you get a request.

Change it to something like:

class MyGroupRestController(EasyCrudRestController):
model = model.Group

def before_put(self, *args, **kw):
print args, kw

before_validate(MyGroupRestController.before_put)(MyGroupRestController.put)

This is one of the things that are going to change on in 2.3+ which
allocates the controllers only on application startup, in the mean
time you must avoid doing anything that changes the controller class
inside the init itself.

>>> > turbogears+...@googlegroups.com.


>>> > For more options, visit this group at
>>> > http://groups.google.com/group/turbogears?hl=en.
>

> --
> You received this message because you are subscribed to the Google Groups
> "TurboGears" group.
> To view this discussion on the web visit

> https://groups.google.com/d/msg/turbogears/-/LUazTYzarrcJ.


>
> To post to this group, send email to turbo...@googlegroups.com.
> To unsubscribe from this group, send email to

> turbogears+...@googlegroups.com.

Alessandro Molina

unread,
Apr 17, 2012, 4:39:00 AM4/17/12
to turbo...@googlegroups.com
Also, forgot to say that in this case the before_put method has to be
a static/class method as you are registering it without self.
Message has been deleted

Moritz Schlarb

unread,
Apr 18, 2012, 4:02:57 AM4/18/12
to turbo...@googlegroups.com
I wanted to do it that way, but since I need a property of the class that I want to "inject" into kw, I can't do it as a classmethod....
But it doesn't really matter if it gets injected twenty times...

Thanks anyway!


>>>> > For more options, visit this group at
>>>> > http://groups.google.com/group/turbogears?hl=en.
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "TurboGears" group.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msg/turbogears/-/LUazTYzarrcJ.
>>
>> To post to this group, send email to turbo...@googlegroups.com.
>> To unsubscribe from this group, send email to

Alessandro Molina

unread,
Apr 18, 2012, 5:18:40 AM4/18/12
to turbo...@googlegroups.com
That shouldn't be an issue, you can access the currently dispatched
controller using tg.dispatched_controller()
This way you will be able to access the controller instance and
retrieve the property you need even from the class method.

About the behavior of the before_validate parameters, that is actually
the right one.
I used *args and **kw just because I'm lazy and didn't want to specify
the right parameters.

The expected before_validate hook signature is:

def before_validate_hook(remainder, params):

if you use *args and **kw you simply get the remainder and the params
inside the args argument :D

>> >>>> > turbogears+...@googlegroups.com.


>> >>>> > For more options, visit this group at
>> >>>> > http://groups.google.com/group/turbogears?hl=en.
>> >>
>> >> --
>> >> You received this message because you are subscribed to the Google
>> >> Groups
>> >> "TurboGears" group.
>> >> To view this discussion on the web visit
>> >> https://groups.google.com/d/msg/turbogears/-/LUazTYzarrcJ.
>> >>
>> >> To post to this group, send email to turbo...@googlegroups.com.
>> >> To unsubscribe from this group, send email to

>> >> turbogears+...@googlegroups.com.


>> >> For more options, visit this group at
>> >> http://groups.google.com/group/turbogears?hl=en.
>
> --
> You received this message because you are subscribed to the Google Groups
> "TurboGears" group.
> To view this discussion on the web visit

> https://groups.google.com/d/msg/turbogears/-/Dgo6nOyTDfQJ.


>
> To post to this group, send email to turbo...@googlegroups.com.
> To unsubscribe from this group, send email to

> turbogears+...@googlegroups.com.

Moritz Schlarb

unread,
Apr 26, 2012, 3:12:30 PM4/26/12
to turbo...@googlegroups.com
Alessandro,

I just ran into an issue with tgext.crud, since it generates pager links based on self._mount_point().
They look like they would have been generated from the last statically dispatched controller, so there are some parts missing.

In tgext/crud/controller.py, line 144, you could probably replace 
tg.dispatched_controller().mount_point
with
request.controller_state.controller.mount_point

Cheers,
Moritz


>> >>>> > For more options, visit this group at
>> >>>> > http://groups.google.com/group/turbogears?hl=en.
>> >>
>> >> --
>> >> You received this message because you are subscribed to the Google
>> >> Groups
>> >> "TurboGears" group.
>> >> To view this discussion on the web visit
>> >> https://groups.google.com/d/msg/turbogears/-/LUazTYzarrcJ.
>> >>
>> >> To post to this group, send email to turbo...@googlegroups.com.
>> >> To unsubscribe from this group, send email to


>> >> For more options, visit this group at
>> >> http://groups.google.com/group/turbogears?hl=en.
>
> --
> You received this message because you are subscribed to the Google Groups
> "TurboGears" group.
> To view this discussion on the web visit
> https://groups.google.com/d/msg/turbogears/-/Dgo6nOyTDfQJ.
>
> To post to this group, send email to turbo...@googlegroups.com.
> To unsubscribe from this group, send email to

Alessandro Molina

unread,
Apr 26, 2012, 4:14:23 PM4/26/12
to turbo...@googlegroups.com
On Thu, Apr 26, 2012 at 9:12 PM, Moritz Schlarb <ma...@moritz-schlarb.de> wrote:
> Alessandro,
>
> I just ran into an issue with tgext.crud, since it generates pager links
> based on self._mount_point().
> They look like they would have been generated from the last statically
> dispatched controller, so there are some parts missing.
>
> In tgext/crud/controller.py, line 144, you could probably replace
> tg.dispatched_controller().mount_point
> with
> request.controller_state.controller.mount_point
>

mount_point would return None for a non statically mounted controller,
so it won't probably solve the issue.

If the issue is caused by the fact that you need to allocate the
controllers with some options you can use cached_property to make it
behave as it was statically mounted. Take a look at
https://bitbucket.org/_amol_/tgapp-photos/src/81288e064f75/photos/controllers/root.py
to see what I mean.

If you really need to serve the controllers from _lookup you must
override _mount_point to use request.path_info
Something like request.path_info[len(tg.dispatched_controller().mount_point):].split('/')[0]
might be the way to go, but I don't have any ready made snippet for
something like that as I always served crud rest controllers from
statically mounted places or cached properties.

Moritz Schlarb

unread,
Apr 27, 2012, 5:59:27 PM4/27/12
to turbo...@googlegroups.com
I have now just set the mount_point to . and it seems to work, the pager links are correct now and I haven't noticed any other breakage...

    @cached_property
    def mount_point(self):
        return '.'

And in response to your question from irc (sorry, wasn't at home yesterday), yes, I need to give the sub-controllers the id of the parent object.

So that I get urls like

/events/eip12/lessons/1/teams/
And the lesson_id=1 is specific to the event "eip12". 
Probably like Github urls where the issue ids are unique just for the repo they belong to.

I thought that would have to be done using _lookup for the nested controllers, or is there another way that I have overseen?

Thanks,
Moritz

Alessandro Molina

unread,
Apr 27, 2012, 8:29:49 PM4/27/12
to turbo...@googlegroups.com
On Fri, Apr 27, 2012 at 11:59 PM, Moritz Schlarb <ma...@moritz-schlarb.de> wrote:
> I have now just set the mount_point to . and it seems to work, the pager
> links are correct now and I haven't noticed any other breakage...
>
>     @cached_property
>     def mount_point(self):
>         return '.'
>

Using a relative mount point is a good solution.

Switching crud itself to use a relative mount point when not
statically mounted would probably make sense.
I'll just have to check why it has been switched to full path since it
was already served using relative urls and I remember that relative
paths were the source of some issues.
Reply all
Reply to author
Forward
0 new messages