在Python中的类中包装SQLAlchemy

时间:2015-07-13 23:16:45

标签: python sqlite python-2.7 sqlalchemy

我正在使用SQLAlchemy 1.0.6和Python 2.7.6。在我从上一篇文章(Dynamic Datasets and SQLAlchemy)中收集到的伟大见解之后,我现在希望模块化我的代码,以便更容易地实现到我的框架中。从这个完整的代码

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."

(注意:Empty_Init类的目的是将它继承到其他表,如清单类中所示。)

我想

  • 将与SQLAlchemy相关的内容包装到它自己的自包含类中
  • 通过import
  • 将与SQLAlchemy相关的类移植到其自己的模块中

认为第一个目标需要在第二个目标之前实现,这就是我遇到的问题

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."

运行此命令会提供以下完整的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`']

我理解错误说的是什么(数据库是创建的,但是没有表),但为什么呢?为什么SQLAlchemy没有创建表?我在DataBase __init__下以相同的顺序执行了相同的代码。更令人费解的是,如果我运行第一个代码,那么通过继承DataBaseBase SQLAlchemy类来初始化engine类,如此

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)

并运行它的方法,它完美地写入数据库,所以我强烈怀疑我没有正确地继承SQLAlchemy的类(在第二个代码中),或者在很小的程度上,我&#39 ;不要让SQLAlchemy通过将它包装成一个类来进行幕后魔术工作 - 我只是不明白为什么。我错过了一些明显的东西吗?

1 个答案:

答案 0 :(得分:0)

更新:我认为我认为我尝试构建课程的方式不是让SQLAlchemy的幕后魔术工作正确。因此,在使用类继承之后,这个完整的代码可以完美地运行:

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."

请注意类继承的轻微变化。

现在第一个目标已经完成,我想继​​续将DataBaseEmpty_InitListing放入外部模块。这部分有点棘手,但这是一起如何协同工作

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)

由于我还是SQLAlchemy的新人,我欢迎你提出任何意见。但由于这解决了我的两个目标,我将把这个问题标记为已解决。我希望将来可以帮助某人。