problems with custom pyramid_formalchemy forms

85 views
Skip to first unread message

lostdorje

unread,
Oct 29, 2011, 5:39:37 AM10/29/11
to FormAlchemy

I posted this to stackoverflow, but wanted to post here too...I'm
trying to customize some forms...in particular I need to add csrf
tokens to the forms. Nothing out-of-the ordinary at, but I'm seeing
strange problems...here is the gist of my setup:

I'm having problems creating custom forms in pyramid_formalchemy. I
suspect there is a bug in the package and wanted to confirm I'm not
missing anything. Note that I'm using fa.jquery as well. My setup
looks like this:

def includeme(config):
config.include('pyramid_formalchemy')
# Adding the jquery libraries
config.include('fa.jquery')
# Adding the package specific routes
config.include('myapp.web.formalchemy.faroutes')

config.formalchemy_admin('admin',
models=[User],
forms=faforms,
session_factory=session,
view='fa.jquery.pyramid.ModelView',
factory='myapp.model.RootFactory')

config.formalchemy_model('user',
model='myapp.model.user.User',
session_factory=session,
view='fa.jquery.pyramid.ModelView',
factory='myapp.model.RootFactory')
faforms is a module containing my custom forms:

from myapp.model.user import User

from formalchemy import FieldSet
from formalchemy import Grid

class UserFieldSet(FieldSet):
def __init__(self):
FieldSet.__init__(self, User)
self.configure()

class UserGrid(Grid):
def __init__(self):
Grid.__init__(self, User)
self.configure()

If I comment out the two classes above, formalchemy works fine. I can
view Users and I can edit them (minus the csrf problem).

When I put the two classes in I run into problems. The problem is
pyramid_formalchemy grabs UserGrid and UserFieldSet from the module's
namespace and then tries to use them as if they were instantiated
classes. This breaks things. On the other hand if pyramid_formalchemy
doesn't find the classes in will dynamically create the classes AND
instantiate them. I believe the offending code is in
pyramid_formalchemy/views.py, line 236 starting at the get_grid()
function:

def get_grid(self):
"""return a Grid object"""
request = self.request
model_name = request.model_name
form_name = '%sGrid' % model_name
if hasattr(request.forms, form_name):
g = getattr(request.forms, form_name) <-- during the first
call g is a class type not an
g.engine = g.engine or self.engine <-- instance!
g.readonly = True <-- why is it
not instantiated?
g._request = self.request
self.update_grid(g)
return g
model = self.context.get_model() <-- UserGrid not found
in faforms
grid = self.grid_class(model) <-- module.
grid.engine = self.engine <-- so a Grid is
instantiated
if not isinstance(request.forms, list):
# add default grid to form module eg: caching
setattr(request.forms, form_name, grid)
grid = grid.copy()
grid._request = self.request
self.update_grid(grid)
return grid

Here you can see if the matching grid (or fieldset) is not found it
will be instantiated, but if it is found the class type will be used
directly, but not actually instantiated.

Any thoughts here? Am I setting something up wrong?

Gaël Pasgrimaud

unread,
Oct 30, 2011, 7:26:23 AM10/30/11
to forma...@googlegroups.com

You must have instances here:

User = FieldSet(User)
User.configure()
UserGrid = Grid(User)
UserGrid.configure()

> --
> You received this message because you are subscribed to the Google Groups "FormAlchemy" group.
> To post to this group, send email to forma...@googlegroups.com.
> To unsubscribe from this group, send email to formalchemy...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/formalchemy?hl=en.
>
>

lostdorje

unread,
Oct 30, 2011, 7:39:01 AM10/30/11
to FormAlchemy
Thanks for the response. I think I'm missing something here...

One of the main reasons I want to customize my form is so I can add
hidden a csrf token to my form. I was thinking that
pyramid_formalchemy would create an instance of the form for me when a
request came in. Then the request could be bound the the form. From
the request I could get the session and then then csrf token...

Something like this:

class _UserFieldSet(FieldSet):
def __init__(self, request):
FieldSet.__init__(self, User, request=request)

self.append(Field(name='_csrf',
value=request.session.get_csrf_token()))

...

If I create the instances myself as you suggest I want have a request
context yet. Is it possible for me to accomplish what I'm trying to
do in a straightforward way?

I was thinking by using only config.formalchemy_admin() and
config.formalchemy_model() and then writing custom form classes I
could accomplish this.

Gaël Pasgrimaud

unread,
Oct 30, 2011, 12:27:54 PM10/30/11
to forma...@googlegroups.com
On Sun, Oct 30, 2011 at 12:39 PM, lostdorje <jrh...@gmail.com> wrote:
> Thanks for the response.  I think I'm missing something here...
>
> One of the main reasons I want to customize my form is so I can add
> hidden a csrf token to my form.  I was thinking that
> pyramid_formalchemy would create an instance of the form for me when a
> request came in.  Then the request could be bound the the form.  From
> the request I could get the session and then then csrf token...
>
> Something like this:
>
> class _UserFieldSet(FieldSet):
>    def __init__(self, request):
>        FieldSet.__init__(self, User, request=request)
>
>        self.append(Field(name='_csrf',
> value=request.session.get_csrf_token()))
>
>        ...
>
> If I create the instances myself as you suggest I want have a request
> context yet.  Is it possible for me to accomplish what I'm trying to
> do in a straightforward way?

You can try something like this:

class FieldSet(Base):

def bind(self, model, request=None, **kw):
fs = super(FieldSet self).bind(model, request=request, **kw)
# add field here
return fs

User = FieldSet(User)


A better solution is to create a crsf renderer and override .render()
to get the value from the request.

class CRSF(HiddenFieldRenderer):

def render(self):
return '<input value="%" />" % self.request.session['crsf']

Then something like:

User = FieldSet(User)
User.configure()
User.append(Field('crsf').set(renderer=CRSF))

lostdorje

unread,
Oct 30, 2011, 11:53:42 PM10/30/11
to FormAlchemy
Thanks, I'll give it a shot.

Just a thought though. This all feels very strange.

1. I have to define instances of the actual FieldSet and Grid classes
and I can't use inheritance which would be a very natural way to
customize a form.

In fact, in pyramid_formalchemy, forms.py the def of get_grid() almost
allows us to accomplish inheritance. If it doesn't find UserFieldSet
in the request it will create a new object with grid =
self.grid_class(model).

def get_grid(self):
"""return a Grid object"""
request = self.request
model_name = request.model_name
form_name = '%sGrid' % model_name
if hasattr(request.forms, form_name):
g = getattr(request.forms, form_name)
g.engine = g.engine or self.engine
g.readonly = True
g._request = self.request
self.update_grid(g)
return g
model = self.context.get_model()
grid = self.grid_class(model)
grid.engine = self.engine
if not isinstance(request.forms, list):
# add default grid to form module eg: caching
setattr(request.forms, form_name, grid)
grid = grid.copy()
grid._request = self.request
self.update_grid(grid)
return grid


If the if statement is true, the getattr would return a type, not an
instance. Then we could just do:

g_type = getattr(request.forms, form_name)
g = g_type(self.request)
...

2. I just want to add a hidden field to my form, why should I need to
define a renderer for this?

One last question...When I try the code you suggest:

User = FieldSet(User)
User.configure()
User.append(Field('crsf').set(renderer=CRSF))

SQLAlchemy gets really unhappy about what I've done with the 'User'
name. If I change the instance name to UserFieldSet then
pyramid_formalchemy gets really unhappy. What are the naming
conventions I should be using? Is this documented somewhere?

Thanks,
Jacob

Gaël Pasgrimaud

unread,
Oct 31, 2011, 7:16:16 AM10/31/11
to forma...@googlegroups.com

Why not. But formalchemy use instances for years. That's how it work.

You can also subclass the fieldset to add a csrf field and instanciate it.

>
> 2. I just want to add a hidden field to my form, why should I need to
> define a renderer for this?
>

Because you want to get the value from a session. There is probably
others way to do that but it's the easy one.

> One last question...When I try the code you suggest:
>
> User = FieldSet(User)
> User.configure()
> User.append(Field('crsf').set(renderer=CRSF))
>
> SQLAlchemy gets really unhappy about what I've done with the 'User'
> name.  If I change the instance name to UserFieldSet then
> pyramid_formalchemy gets really unhappy.  What are the naming
> conventions I should be using?  Is this documented somewhere?
>

I guess you need this:

User = FieldSet(models.User)

That's not documented as I can see but you can have a look at the demo:
https://github.com/FormAlchemy/formalchemy_project/blob/master/formalchemy_project/forms.py

lostdorje

unread,
Nov 2, 2011, 1:17:40 AM11/2/11
to FormAlchemy
Thanks for the help. Here are my results...

Doing something like:

from myapp.model.user import User

UserFieldSet = FieldSet(User)

doesn't work. I see the error:

AttributeError: type object 'User' has no attribute 'engine'

File "/myapp/env/lib/python2.7/site-packages/pyramid_formalchemy/
views.py", line 231, in get_fieldset
fs.engine = fs.engine or self.engine


However if I subtlely change the code (as you suggest) to:

from myapp.model import user

UserFieldSet = FieldSet(user.User)

it all just works. This doesn't make a lot of sense because from
python's perspective User and user.User are the same thing, the same
class...Why doesn't pyramid_formalchemy agree with python?

There are a lot of undocumented assumptions here (eg: forms.py needs
to have instances not classes and the way package namespaces are used
as the above example shows). I really appreciate what
pyramid_formalchemy and formalchemy are doing. Using them provides a
lot. I just wished it were better documented and the user.User and
User is messy.
> That's not documented as I can see but you can have a look at the demo:https://github.com/FormAlchemy/formalchemy_project/blob/master/formal...
> >> > For more options, visit this group...
>
> read more »
Reply all
Reply to author
Forward
0 new messages