5

I'm trying to write Schedule class which contains records like this: ... session, base, engine declaration somewhere here...

class Schedule(Base):
    __tablename__ = 'schedule'
    id = Column(Integer, primary_key=True)
    # and here i need ref to Station class
    station_id = Column(Integer, ForeignKey('station.id'))
    station = relationship('Station') # Also tried Station
    arr_time = Column(Time)

    def __init__(self, station_name, arrive_time):
         self.metadata.create_all()
         self.arrive_time = arrive_time

         # And now I'm trying to find station object by a given name
         # and add ref to it in self.station. 
         # The selection of a station works properly here:
         station = session.query(Station).filter(Station.name == station_name).first()
         # But on this statement I get an error: None object has no method 'append'
         self.station.append(station)
         session.add(self)
         session.commit()

After that I implement class 'Station'

class Station(Base):
    __tablename__ = 'stations'
    id = Column(Integer, primary_key=True)
    name = Column(String)

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

So, when I'm trying to add new Schedule record I get an error:

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

The one-to-many case (where the foreign key in the first class and relationsip in the second one) works correctly.

What's wrong with my code?

Update: Also tried example from documentation:

engine = create_engine('sqlite:///:memory:')
Base = declarative_base(engine)
session = sessionmaker(bind=engine)()

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    child_id = Column(Integer, ForeignKey('child.id'))
    child = relationship("Child")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)


if __name__ == '__main__':
    Base.metadata.create_all()
    parent = Parent()
    session.add(parent)
    child = Child()
    session.add(child)
    print(hasattr(Parent, 'child'))
    print(hasattr(parent, 'child'))
    print(type(Parent.child))
    print(type(parent.child))

I get:

 >>> True
 >>> True
 >>> <class 'sqlalchemy.orm.attributes.InstrumentedAttribute'>
 >>> <class 'NoneType'>
Mikhail Elizarev
  • 965
  • 3
  • 10
  • 22

3 Answers3

4

I've got it)) The problem was that as default uselist flag in constructor of relationship is set for True. But - I don't really understand why it is not written in documentation - in the case of many-to-one relation this flag is set for False.

So, to solve my problem I simply have to change this:

station = relationship("Station", uselist=True)

or use in constructor:

self.station = station

This completely solves my problem.

Mikhail Elizarev
  • 965
  • 3
  • 10
  • 22
2

I had a similar issue. I think the problem is that the relationship does not get triggered until you add and commit.

engine = create_engine('sqlite:///:memory:')
Base = declarative_base(engine)
session = sessionmaker(bind=engine)()

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    child_id = Column(Integer, ForeignKey('child.id'))
    child = relationship("Child")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)


if __name__ == '__main__':
    Base.metadata.create_all()
    parent = Parent()
    session.add(parent)
    child = Child()
    session.add(child)
    session.commit() # IMPORTANT: this is necessary for relationship to work
    print(hasattr(Parent, 'child'))
    print(hasattr(parent, 'child'))
    print(type(Parent.child))
    print(type(parent.child))

The only line I added is

        session.commit() # IMPORTANT: this is necessary for relationship to work
oxer
  • 975
  • 1
  • 10
  • 16
0
station = session.query(Station).filter(Station.name == station.name).first()

self.station is None....

Try to use debugger or just print station , print self.station

Edit: Class Variable Scopes:

There are 2 ways for class scope vars:

  1. Instance variable - when using self, this is called instance variable and by that to each instance of that class you will have a copy of the variable.

  2. Class attribute - in your case it's Schedule.station it's owned by the class, not the instances of the class.

Please refer to the Docs about namespace it will clear your problem.

Kobi K
  • 7,743
  • 6
  • 42
  • 86
  • No, station in not None))) And even if it is None, you are able to add it to any list: `x = []; x.append(None)` gives `[None]`. Error exists on `relationsip`, not on station itself. – Mikhail Elizarev Oct 14 '13 at 08:48
  • this is the line that gives the error self.station.append(station) isn't it? that's what i understand from AttributeError: 'NoneType' object has no attribute 'append' – Kobi K Oct 14 '13 at 08:50
  • sorry, I have misspelling in my code example here. `Station.name == station_name`, not `station.name`. In my code everything properly (I've typed this code here, not copied) – Mikhail Elizarev Oct 14 '13 at 08:54
  • np:) just edit the post so it will be clear. also do you have self.station? and what is it's content? – Kobi K Oct 14 '13 at 08:56
  • self.station - is in fact request to the Schedule.station member, as I understand. Isn't it correct usage? – Mikhail Elizarev Oct 14 '13 at 09:01
  • Well, if i try to use self.classmember I will get a copy of class member in my instance. So it should be of type ``, such as in class. But in fact I get ``. I.e. property exists, but it is not defined. – Mikhail Elizarev Oct 14 '13 at 09:17
  • I've added example from sqlalchemy documentation with exactly the same problem – Mikhail Elizarev Oct 14 '13 at 09:28