Many-to-one relationship in SQLAlchemy

2019-08-08 03:53发布

问题:

This is a beginner-level question.

I have a catalog of mtypes:

mtype_id name
       1 'mtype1'
       2 'mtype2'
[etc]

and a catalog of Objects, which must have an associated mtype:

obj_id mtype_id name
     1        1 'obj1'
     2        1 'obj2'
     3        2 'obj3'
[etc]

I am trying to do this in SQLAlchemy by creating the following schemas:

mtypes_table = Table('mtypes', metadata,
 Column('mtype_id', Integer, primary_key=True),
 Column('name', String(50), nullable=False, unique=True),
)

objs_table = Table('objects', metadata,
 Column('obj_id', Integer, primary_key=True),
 Column('mtype_id', None, ForeignKey('mtypes.mtype_id')),
 Column('name', String(50), nullable=False, unique=True),
)

mapper(MType, mtypes_table)
mapper(MyObject, objs_table, 
 properties={'mtype':Relationship(MType, backref='objs', cascade="all, delete-orphan")}
)

When I try to add a simple element like:

mtype1 = MType('mtype1')
obj1 = MyObject('obj1')
obj1.mtype=mtype1
session.add(obj1)

I get the error:

AttributeError: 'NoneType' object has no attribute 'cascade_iterator'

Any ideas?

回答1:

Have you tried:

Column('mtype_id', ForeignKey('mtypes.mtype_id')),

instead of:

Column('mtype_id', None, ForeignKey('mtypes.mtype_id')),

See also: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/schema.html#sqlalchemy.schema.ForeignKey



回答2:

I was able to run the code you have shown above so I guess the problem was removed when you simplified it for the purpose of this question. Is that correct?

You didn't show a traceback so only some general tips can be given.

In SQLAlchemy (at least in 0.5.8 and above) there are only two objects with "cascade_iterator" attribute: sqlalchemy.orm.mapper.Mapper and sqlalchemy.orm.interfaces.MapperProperty.

Since you didn't get sqlalchemy.orm.exc.UnmappedClassError exception (all mappers are right where they should be) my wild guess is that some internal sqlalchemy code gets None somewhere where it should get a MapperProperty instance instead.

Put something like this just before session.add() call that causes the exception:

from sqlalchemy.orm import class_mapper
from sqlalchemy.orm.interfaces import MapperProperty

props = [p for p in class_mapper(MyObject).iterate_properties]
test = [isinstance(p, MapperProperty) for p in props]
invalid_prop = None
if False in test:
    invalid_prop = props[test.index(False)]

and then use your favourite method (print, python -m, pdb.set_trace(), ...) to check the value of invalid_prop. It's likely that for some reason it won't be None and there lies your culprit.

If type(invalid_prop) is a sqlalchemy.orm.properties.RelationshipProperty then you have introduced a bug in mapper configuration (for relation named invalid_prop.key). Otherwise it's hard to tell without more information.