On Mon, Mar 3, 2014 at 10:38 AM, AM <
ams...@gmail.com> wrote:
> On 02/28/2014 11:58 PM, Chung WONG wrote:
>>
>> I have a *models.py* that contains 10 classes. And I am trying to move all
>> classes out of that file and put each class into its own file under a models
>> package.
>>
>> I am using the alternative(at bottom of page) method mentioned here
>> <
https://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/database/sqlalchemy.html#importing-all-sqlalchemy-models>,
>> but I don't know how it works for initialise db script as it is using
>> *config.scan()*
>>
>> However, when i ran *bin/initialize_bug_db
development.in *to initialise
>> the database, it threw such as :
>>
>> sqlalchemy.exc.InvalidRequestError: When initializing mapper
>> Mapper|User|users, expression 'TopicUser' failed to locate a name ("name
>> 'TopicUser' is not defined"). If this is a class name, consider adding this
>> relationship() to the <class 'bug.models.user.User'> class after both
>> dependent classes have been defined.
>>
>> which was caused by something like *topics = relationship('TopicUser',
>> backref="user", lazy='dynamic')*
>>
>> and
>>
>> sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column
>> 'notifications.topic_id' could not find table 'topics' with which to
>> generate a foreign key to target column 'id'
>> which was caused by something like *topic_id=Column(Integer,
>> ForeignKey('
topics.id'))*
It sounds like config.scan() is importing the modules in the wrong
order. There's not much you can do about that. If your DeclarativeBase
is in one module, the child model is in a second module, and the
module with the relationship is in a third module, you have to import
them in that order, and config.scan() can't guess that. I think you
can define the models out of order by using a string in the
relationship, but I've only done that within a module so I'm not sure
it works across modules.
AM's includeme() would be one way to address this, but the primary
issue is what's *in* the includeme() and what's above it. You could
have the includeme() body import the modules in the right order and
configure everything. You would probably not want any unnecessary
global-level imports above it, which might throw things off.
I don't use includeme() in models but I do this instead:
def init_models(engine):
DBSession.bind = engine
Base.metadata.bind = engine
This is nice because it's one-stop initialization for both the
application and any standalone scripts. It would also be the place to
do any reflection or preloading memory indexes.
You *can* do circular imports this way:
# .__init__.py
import sqlalchemy.orm as orm
DBSession = orm.scoped_session(orm.sessionmaker(extension=...))
import submodule_a
# submodule_a.py
from . import DBSession
The trick is that the "import submodule_a" must *below* anything that
submodule_a imports from the parent module, because when submodule_a
is initializing only the top part of the parent's module has executed.
I didn't recommend it in the Cookbook article because I considered it
failure-prone, but several people have successfully used it since
then. I still prefer to avoid circular imports whenever possible.