Reputation: 1270
I am using SQLAlchemy 1.0.6 and Python 2.7.6. After the great insight I gleaned from my last post (Dynamic Datasets and SQLAlchemy), I am now looking to modularize my code for easier implementation into my framework. Going from this full-working code
from time import time
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class Empty_Init():
def __init__(self, **kwargs):
for k,v in kwargs.items():
#This class and the structure of the table need to be sync'd.
if hasattr(self, k):
setattr(self, k, v)
else:
if k not in engine.execute("SELECT * FROM `{tbl}`".format(tbl = self.__tablename__))._metadata.keys:
engine.execute("ALTER TABLE `{tbl}` ADD COLUMN {col} {typ}".format(
tbl = self.__tablename__,
col = k,
typ = "INT" if type(v) is int else ("DOUBLE" if type(v) is float else "VARCHAR")))
setattr(self.__class__, k, Column(k, String))
setattr(self, k, v)
class Listing(Empty_Init, Base):
__tablename__ = 'Listings'
__table_args__ = {'sqlite_autoincrement': True}
id = Column(Integer, primary_key=True, nullable=False)
make = Column(String)
model = Column(String)
year = Column(Integer)
t = time()
engine = create_engine('sqlite:///')
Base.metadata.create_all(engine)
session = sessionmaker()
session.configure(bind=engine)
s = session()
try:
data = {'make':'Chevy',
'model' : 'Corvette',
'year' : 1964,
'doors' : 2,
'price' : 50000}
record = Listing(**data)
s.add(record)
data = {'make':'Chevy',
'model' : 'Camaro',
'year' : 1967,
'doors' : 2,
'HP' : 375,
"0-60" : 6.1}
record = Listing(**data)
s.add(record)
s.commit() #attempt to commit the changes
except:
s.rollback() #rollback the changes on error
finally:
s.close() #Close the connection
print str(time() - t) + " s."
(Note: the purpose of the Empty_Init class is to have it be inherited to other tables, as seen in the Listing class.)
I'd like to
import
Thinking that the first goal needs to be achieved before the second, here's where I'm stuck
from time import time
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
class DataBase(object):
def __init__(self):
self.Base = declarative_base()
self.engine = create_engine('sqlite:///')
self.session = sessionmaker()
self.session.configure(bind=self.engine)
self.s = self.session()
self.Base.metadata.create_all(self.engine)
def Add_Record(self, data):
record = Listing(self.engine, self.Base, **data)
self.s.add(record)
def Commit(self):
self.s.commit()
class Empty_Init():
def __init__(self, engine, Base, **kwargs):
for k,v in kwargs.items():
#This class and the structure of the table need to be sync'd.
if hasattr(self, k):
setattr(self, k, v)
else:
if k not in engine.execute("SELECT * FROM `{tbl}`".format(tbl = self.__tablename__))._metadata.keys:
engine.execute("ALTER TABLE `{tbl}` ADD COLUMN {col} {typ}".format(
tbl = self.__tablename__,
col = k,
typ = "INT" if type(v) is int else ("DOUBLE" if type(v) is float else "VARCHAR")))
setattr(self.__class__, k, Column(k, String))
setattr(self, k, v)
class Listing(Empty_Init):
__tablename__ = 'Listings'
__table_args__ = {'sqlite_autoincrement': True}
id = Column(Integer, primary_key=True, nullable=False)
make = Column(String)
model = Column(String)
year = Column(Integer)
t = time()
engine = create_engine('sqlite:///')
DBC = DataBase()
data = {'make':'Chevy',
'model' : 'Corvette',
'year' : 1964,
'price' : 50000}
DBC.Add_Record(data)
data = {'make':'Chevy',
'model' : 'Camaro',
'year' : 1967,
'HP' : 375,
"0-60" : 6.1}
DBC.Add_Record(data)
DBC.Commit()
print str(time() - t) + " s."
Running this gives the following full Traceback
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/dist-packages/spyderlib/widgets/externalshell/sitecustomize.py", line 540, in runfile
execfile(filename, namespace)
File "/home/manny/sqlalchemy_basic_master_class.py", line 65, in <module>
DBC.Add_Record(data)
File "/home/manny/sqlalchemy_basic_master_class.py", line 23, in Add_Record
record = Listing(self.engine, self.Base, **data)
File "/home/manny/sqlalchemy_basic_master_class.py", line 40, in __init__
if k not in engine.execute("SELECT * FROM `{tbl}`".format(tbl = self.__tablename__))._metadata.keys:
File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1987, in execute
return connection.execute(statement, *multiparams, **params)
File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 906, in execute
return self._execute_text(object, multiparams, params)
File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1054, in _execute_text
statement, parameters
File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1146, in _execute_context
context)
File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1341, in _handle_dbapi_exception
exc_info
File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/compat.py", line 199, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb)
File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1139, in _execute_context
context)
File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 450, in do_execute
cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: Listings [SQL: 'SELECT * FROM `Listings`']
I understand what the error is saying (the database was created, but there are no tables), but why? Why didn't SQLAlchemy create the table? I executed the same code in the same sequence under DataBase's __init__
. What's even more baffling, is that if I run the first code, then initialize the DataBase
class by inheriting the Base
and engine
SQLAlchemy classes, like so
class DataBase(object):
def __init__(self, Base, engine):
self.Base = Base
self.engine = engine
self.session = sessionmaker()
self.session.configure(bind=self.engine)
self.s = self.session()
self.Base.metadata.create_all(self.engine)
Base = declarative_base()
engine = create_engine('sqlite:///')
DBC = DataBase(Base, engine)
and run its methods, it writes to the database perfectly, so I strongly suspect I'm not inheriting SQLAlchemy's classes correctly (in the 2nd code) or, to a much lesser degree, I'm not letting SQLAlchemy's behind-the-scenes magic work by wrapping it into a class -- I just can't see why. Am I missing something obvious?
Upvotes: 2
Views: 3682
Reputation: 1270
Update: I think I was correct in thinking that the way I was trying to structure my classes wasn't letting SQLAlchemy's behind-the-scenes magic work. Thus, after playing around with the class inheritance, this full-working code works perfectly fine:
from time import time
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class DataBase(object):
def __init__(self, Base):
self.Base = Base
self.engine = create_engine('sqlite:///')
self.session = sessionmaker()
self.session.configure(bind=self.engine)
self.s = self.session()
self.Base.metadata.create_all(self.engine)
def Add_Record(self, data):
record = Listing(self.engine, **data)
self.s.add(record)
def Commit(self):
self.s.commit()
def Close(self):
self.s.close()
class Empty_Init():
def __init__(self, engine, **kwargs):
#This class and the structure of the table need to be sync'd.
for k,v in kwargs.items():
if hasattr(self, k): #if the class already has the attribute initialized,
setattr(self, k, v) # then assign it the new value
else: # if it doesn't...
if k not in engine.execute("SELECT * FROM `{tbl}`".format(tbl = self.__tablename__))._metadata.keys:
engine.execute("ALTER TABLE `{tbl}` ADD COLUMN {col} {typ}".format(
tbl = self.__tablename__,
col = k,
typ = "INT" if type(v) is int else ("DOUBLE" if type(v) is float else "VARCHAR")))
setattr(self.__class__, k, Column(k, String))
setattr(self, k, v)
class Listing(Empty_Init, Base):
__tablename__ = 'Listings'
__table_args__ = {'sqlite_autoincrement': True}
id = Column(Integer, primary_key=True, nullable=False)
make = Column(String)
model = Column(String)
year = Column(Integer)
t = time()
DBC = DataBase(Base)
data = {'make':'Chevy',
'model' : 'Corvette',
'year' : 1964,
'price' : 50000}
DBC.Add_Record(data)
data = {'make':'Chevy',
'model' : 'Camaro',
'year' : 1967,
'doors' : 2,
'wheels_rwd' : 4.1,
"test" : "bladads"}
DBC.Add_Record(data)
DBC.Commit()
DBC.Close()
print str(time() - t) + " s."
Note the slight variation in the class inheritance.
Now that the first goal is complete, I would like to move onto the second of placing DataBase
, Empty_Init
, and Listing
into an external module. This is part was a bit tricky, but here's how it all works together
main.py
from time import time
from datetime import datetime
import sqla_lib
t = time()
DBC = sqla_lib.DataBase(sqla_lib.Base)
data = {'make':'Chevy',
'model' : 'Corvette',
'year' : 1964,
'price' : 50000}
DBC.Add_Record(data)
data = {'make':'Chevy',
'model' : 'Camaro',
'year' : 1967,
'doors' : 2,
'wheels_rwd' : 4.1,
"test" : "bladads"}
DBC.Add_Record(data)
DBC.Commit()
DBC.Close()
print str(time() - t) + " s."
sqla_lib.py
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class DataBase(object):
def __init__(self, Base):
self.Base = Base
self.engine = create_engine('sqlite:///')
self.session = sessionmaker()
self.session.configure(bind=self.engine)
self.s = self.session()
self.Base.metadata.create_all(self.engine)
def Add_Record(self, data):
record = self.Listing(self.engine, **data)
self.s.add(record)
def Commit(self):
self.s.commit()
def Close(self):
self.s.close()
class Empty_Init():
def __init__(self, engine, **kwargs):
#This class and the structure of the table need to be sync'd.
for k,v in kwargs.items():
if hasattr(self, k): #if the class already has the attribute initialized,
setattr(self, k, v) # then assign it the new value
else: # if it doesn't...
if k not in engine.execute("SELECT * FROM `{tbl}`".format(tbl = self.__tablename__))._metadata.keys:
engine.execute("ALTER TABLE `{tbl}` ADD COLUMN {col} {typ}".format(
tbl = self.__tablename__,
col = k,
typ = "INT" if type(v) is int else ("DOUBLE" if type(v) is float else "VARCHAR")))
setattr(self.__class__, k, Column(k, String))
setattr(self, k, v)
class Listing(Empty_Init, Base):
__tablename__ = 'Listings'
__table_args__ = {'sqlite_autoincrement': True}
id = Column(Integer, primary_key=True, nullable=False)
make = Column(String)
model = Column(String)
year = Column(Integer)
Since I'm still new at SQLAlchemy, I welcome any feedback you may have. But since this solves my two goals, I will mark this question as solved. I hope that it may help someone in the future.
Upvotes: 2