【问题标题】:using flask-sqlalchemy without the subclassed declarative base使用没有子类声明基础的烧瓶 sqlalchemy
【发布时间】:2013-07-24 09:50:24
【问题描述】:

我将 Flask 用于我的 python wsgi 服务器,并将 sqlalchemy 用于我的所有数据库访问。

认为我想在我的应用程序中使用 Flask-Sqlalchemy 扩展,但我不想使用声明性基类 (db.Model),而是想使用来自 sqlalchemy.ext.declarative 的基础。

这是否违背了使用扩展程序的全部目的?


我的用例:

我希望该扩展帮助我更好地管理会话/引擎,但我想分别处理所有模型。

我实际上不介意使用扩展,但我想编写 strict 模型。我正在从非烧瓶应用程序中移植代码,并且我将在进行时将更改推送回该项目。例如,如果 flask-sqlalchemy 允许我在表 元数据 上作弊,那么当代码被推出时,这将导致问题。我的代码的某些部分也进行了大量类型检查(多态标识),我还记得阅读过在使用扩展时不建议对 Table 进行类型检查。

【问题讨论】:

    标签: python sqlalchemy flask flask-sqlalchemy


    【解决方案1】:

    您可以让 Flask-SQLAlchemy 公开您自己的基础模型,而不是内置模型。只需继承 SQLAlchemy 并覆盖 make_declarative_base

    from flask.ext.sqlalchemy import SQLAlchemy
    
    
    class CustomAlchemy(SQLAlchemy):
        def make_declarative_base(self):
            base = declarative_base(...)
            ...
            return base
    
    db = CustomAlchemy()
    

    【讨论】:

      【解决方案2】:

      我实际上在烧瓶中使用 sqlalchemy 而不使用声明性基础,我没有任何问题。如果您愿意,您可以随时这样做,没有义务使用对象关系映射器,ORM 只是 sqlalchemy 的一部分。您始终可以使用 alchemy sql 表达式语言,在模型对象中定义表,并在那里定义一些将使用表达式语言的方法。我有这样的代码(模型是我之前定义的对象),connect 是一个连接到 db 的装饰器,它对我来说很好用。

      def connect(func):
          eng = create_engine(app.config["DATABASE"])
          @wraps(func)
          def wrapped(*args,**kwargs):
              with closing(eng.connect()) as con:
                  result = con.execute(func(*args,**kwargs))
              return result
          return wrapped
      
      class User_(Model):
          def __init__(self):
              Model.__init__(self)
              self.metadata = MetaData()
              self.structure = Table("users", self.metadata,
                                     Column("id",Integer,primary_key=True),
                                     Column("username",VARCHAR(64)),
                                     Column("password",TEXT),
                                     Column("email",VARCHAR(100)),
                                     Column("about_me",TEXT),
                                     Column("deadline",DATETIME),
                                     Column("points",INTEGER)),
                                     Column("date_created",DATETIME))
      
          @connect
          def get_hashed_pass(self,username):
              """  """
              t = self.structure
              s = select([t.c.password]).where(t.c.username == str(username))
              return s
       #other methods follow
      

      Flask's documentation concerning alchemy 明确表示完全可以这样做:

      如果你只是想使用数据库系统(和 SQL)抽象层你基本上只需要引擎

      附:哦,还有一件事,他们在文档中说,如果您想快速入门,最好使用扩展程序,但我个人不太确定,如果您像我一样并且感觉更熟悉使用 sql 查询而不是使用 ORM,您无需扩展即可快速入门。

      【讨论】:

      • 我花了一段时间才明白你的意思(我没有看到方法上的连接装饰器)。在模型中使用会话将是我的下一个问题!
      【解决方案3】:

      SQLAlchemy 自己实际上建议您将 Flask 包装器 (db.Model) 用于 Flask 项目。话虽如此,我已经在我的几个 Flask 项目中使用了 declarative_base 模型,它更有意义。

      它确实违背了 flask-sqlalchemy 中的 SQLAlchemy 类的全部目的。

      这里有一些示例代码:

      from sqlalchemy import *
      from sqlalchemy.ext.declarative import declarative_base
      from sqlalchemy.orm import relationship, sessionmaker
      import datetime
      
      #set up sqlalchemy
      engine = create_engine('postgresql://<username>:<password>@localhost/flask_database')
      Base = declarative_base()
      metadata = Base.metadata
      metadata.bind = engine
      Session = sessionmaker(bind=engine, autoflush=True)
      session = Session()
      
      
      class User(Base):
          __tablename__ = 'user'
          id = Column(Integer, primary_key=True)
          api_owner_id = Column(Integer, ForeignKey('api.id'))
          email = Column(String(120), unique=True)
          username = Column(String(120), unique=True)
          first_name = Column(String(120))
          last_name = Column(String(120))
          business_name = Column(String(120))
          account_type = Column(String(60))
          mobile_phone = Column(String(120))
          street = Column(String(120))
          street2 = Column(String(120))
          city = Column(String(120))
          state = Column(String(120))
          zip_code = Column(String(120))
          country = Column(String(120))
          creation_date = Column(DateTime, default=datetime.datetime.now())
          password = Column(String(120))
          #github stuffs
          github_link = Column(Boolean, default=False)
          github_usn = Column(String(120))
          github_oauth_token = Column(String(160))
          #balanced stuffs
          balanced_account_uri = Column(String(120))
          ach_verified = Column(Boolean, default=False)
          active = Column(Boolean, default=True)
          profile_updated = Column(Boolean, default=False)
          account_balance = Column(Numeric(precision=10, scale=2), default=0.00)
          admin = Column(Boolean, default=False)
          devapp = relationship('DevApp', backref="user", lazy="dynamic")
          projects = relationship('Project', backref="user", lazy="dynamic")
          proposals = relationship('Proposal', backref="user", lazy="dynamic")
          transactions = relationship('Monies', backref="user", lazy="dynamic")
      
          def __repr__(self):
              return self.email
      

      【讨论】:

      • 您的会话/引擎位有助于说明您的答案。因此,在我的烧瓶应用程序中,我需要传递会话以供使用。每次使用后我是否需要以任何方式清理会话? (也许使用 app.do_teardown_xxx?)
      • 使用 sessionmaker 中的 autoflush=True 参数不需要清理。 user = User(email="nicholas.woodhams@gmail.com") session.add(user) session.commit()
      • 我不打算跨多个线程运行它,但我将有独立的模块,每个模块都使用自己的新会话。我计划通过 sessionmaker。这些模块将进行渐进式更新。提交每个会话实例后,是否有任何理由担心数据完整性? (假设我的提交不会相互干扰)。
      • 我敢肯定,只要每个会话都有自己的数据库连接,您就不会遇到会话相互冲突的问题。但我不完全确定,所以如果这是一个问题,我会问另一个问题。最佳
      猜你喜欢
      • 1970-01-01
      • 2014-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      • 2012-06-23
      • 2022-01-05
      • 2015-07-09
      相关资源
      最近更新 更多