Paweł Pogorzelski
Paweł Pogorzelski

Reputation: 651

Pyramid, sqlalchemy dbsession

When building a Pyramid app (SQLAlchemy as ORM) I'm hit with a problem I can't seem to figure out.
Project built with SQLAlchemy scaffold, so all the setup is proper.
When I use DBSession variable in views or like it works properly.
Example :

from pabsite.models import DBSession
....
@view_config(route_name='admin_class', renderer='admin_class.mako')
def admin_class(request):
    db_class = eval(request.matchdict['db_class'])
    class_objects = DBSession.query(db_class).all()
    return {'db_class':db_class, 'class_objects':class_objects, "db_class_name":request.matchdict['db_class']}

Now the problem arises when I try to create a new, non-SQLAlchemy class like :

from pabsite.models import DBSession
.....
class AdminPost(colander.MappingSchema):
    tester = DBSession.query(Post).all()
    title = colander.SchemaNode(colander.String(),
                                       validator=colander.Length(max=100),
                                       widget = get_inputbox(),
                                       description='Tytuł')

The moment I want to run the app I'm hit with :

  File "/media/stuff/Projects/current/web/PabsiteProject/pabsite/pabsite/__init__.py", line 1, in <module>
    from pabsite.admin.views import admin_routes
  File "/media/stuff/Projects/current/web/PabsiteProject/pabsite/pabsite/admin/views.py", line 9, in <module>
    from models import getAdminPost
  File "/media/stuff/Projects/current/web/PabsiteProject/pabsite/pabsite/admin/models.py", line 13, in <module>
    class AdminPost(colander.MappingSchema, Base):
  File "/media/stuff/Projects/current/web/PabsiteProject/pabsite/pabsite/admin/models.py", line 14, in AdminPost
    tester = DBSession.query(Post).all()
.....
sqlalchemy.exc.UnboundExecutionError: Could not locate a bind configured on mapper Mapper|Post|posts, SQL expression or this Session

It must be something trivial but I can't seem to make that class aware of the DBSession binding. Can somebody please point me to some solution / docs.

EDIT : Sorry, I've posted a stupid example which I've ended up when trying different things.
Proper example :

class AdminPost(colander.MappingSchema):
    title = colander.SchemaNode(colander.String(),
                                       validator=colander.Length(max=10),
                                       widget = get_inputbox(),
                                       description='Tytuł')
    category = colander.SchemaNode(colander.String(),
                                       widget = get_select_widget(),
                                       description='Kategoria')

    def getForm(self):
        schema = self
        admin_form = Form(schema, buttons=('submit',))
        return admin_form

Now I define the select widget (I want them to be reused in other classes) as :

def get_select_widget():
    get_categories = DBSession.query(Category).all()
    select_widget = deform.widget.SelectWidget(values=get_categories())

And here get_categories throws the error I mentioned earlier. It maybe my misconception of using Python, but I don't know how to get DBSession be aware of the app engine configuration.
EDIT 2 Category is a db model while AdminPost class server as a deform model. Here's the def

class Category(Base):
    __tablename__ = 'categories'
    id = Column(Integer, primary_key=True)
    name = Column(String(150))
    slug = Column(String(150))
    name_en = Column(String(150))
    slug_en = Column(String(150))

    def __init__(self,name=''):
        self.name = name

    def __repr__(self):
        return self.name

How can they share the same parent as they're meant for different things.

EDIT 4

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()

They're defined as the pyramid scaffold makes them - in models.py in app root. That's where I import them from, and they do work, but in views, not in a class which I prepared on deform.

Upvotes: 1

Views: 2064

Answers (1)

javex
javex

Reputation: 7564

Your problem lies with the way python works and what you are trying to achieve here. I assume you want AdminPost.tester to always contain an up-to-date list of all items in the Post table?

Let's address the problems one-by-one. First, the statement

class AdminPost(colander.MappingSchema):
    tester = DBSession.query(Post).all()

will be executed at import time. Check this out:

print "Before import"
import some_module
print "After import"
some_module.SomeClass()

And let some_module look like this:

class A(object):
    print "Inside A"
    def __init__(self):
        print "New instance of A"

This is the output you will get:

Before import
Inside A
After import
New instance of A

The reason for this is the way python parses the code: It executes each of the statements it finds, but on encountering a def it will not evaluate anything that is indented. (This is a rough description!)

So now that you have seen this, you know that your query is executed (most likely) when you start your program. At this point, sqlalchemy has not found all its tables and made the configuration for it. Furthermore, you most likely don't have an engine bound yet.

But your problem actually isn't really SQLAlchemy related. It is a common misconception of how to define values that every instance of a class should as a default. In Python you do this inside an __init__:

class AdminPost(colander.MappingSchema):
    def __init__(self, *args, **kwargs):
        tester = DBSession.query(Post).all()
        colander.MappingSchema.__init__(self, *args, **kwargs)

The last line makes sure the default constructor is called afterwards.

Upvotes: 1

Related Questions