Just a heads up, I’m new to programming in general so I’m guessing I’m missing something simple. I’ve been trying to follow the official tutorial here and I’m at the Building Relationships section. It works up to the last session.commit() when it says there is no ‘addresses’ table. This is what I think the code should be doing;
- Address sets up a table.
- metadata.create_all(engine) creates the table in the current session.
- I set up the new user ‘jack’ including some addresses.
- I try to add ‘jack’, then commit.
I’m unsure what I’m doing wrong, I’d appreciate any help.
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, backref
Base = declarative_base()
metadata = MetaData()
engine = create_engine('sqlite:///:memory:', echo=False)
Session = sessionmaker(bind=engine)
session = Session()
users_table = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String),
Column('fullname', String),
Column('password', String)
)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
password = Column(String)
def __init__(self, name, fullname, password):
self.name = name
self.fullname = fullname
self.password = password
def __repr__(self):
return "<User('%s', '%s', '%s')>" % (self.name, self.fullname, self.password)
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
email_address = Column(String, nullable=False)
user_id = Column(Integer, ForeignKey('users.id'))
user = relationship(User, backref=backref('addresses', order_by=id))
def __init__(self, email_address):
self.email_address = email_address
def __repr__(self):
return "<Address('%s')>" % self.email_address
metadata.create_all(engine)
session.add_all([User('jim', 'Jim Gregson', 'secretword'),
User('john', 'John Smith', 'password'),
User('jane', 'Jane Doe', 'qweasdzxc'),
User('bob', 'Bob Johnson', '122765')])
session.commit()
for instance in session.query(User).order_by(User.id): print instance
jack = User('jack', 'Jack Bean', 'gjffdd')
print "jack.addresses: ", jack.addresses
jack.addresses = [Address(email_address='jack@google.com'), Address(email_address='j25@yahoo.com')]
print "jack.addresses: ", jack.addresses
print "jack.addresses[1].user: ", jack.addresses[1].user
session.add(jack)
session.commit()
Error below when 'session.commit()' is called;
sqlalchemy.exc.OperationalError: (OperationalError) no such table: addresses u'INSERT INTO addresses (email_address, user_id) VALUES (?, ?)' ('jack@google.com', 5)
The
Baseobject contains it’s own metadata instance that it adds more metadata to as your entities are defined. So, when the User class is defined, the Base object realizes this, and adds all of the metadata for the User class to it’s own metadata instance (Base.metadata).In your script, you’ve created your own metadata instance (
metadata = MetaData()), and then later on used that empty metadata to create your tables (metadata.create_all(engine)).Don’t create your own metadata, and use the base one instead:
Edit:
Another note: You probably would have received the same error for the users table, but it seems that you have defined the users table metadata in what I called in my answer the “empty” metadata. You don’t need to do this; defining the User table in the way that you’ve done it (defining the “User” class) is good enough to do everything you did when you said “users_table = Table(…)”. You’re basically doing the same thing in two ways, and you really only need to do one.