【问题标题】:Are there any side effects with adding a method to a declerative class in sqlalchemy?在 sqlalchemy 中向 declerative 类添加方法是否有任何副作用?
【发布时间】:2018-11-03 13:21:59
【问题描述】:

我曾问过这个问题How to create instance of a table entry but not added in ponyorm?,我问的是如何在不立即添加的情况下创建定义为 ponyorm 表表示的类的实例。通过使用 sqlalchemy,在 session 实例上需要显式 add,我认为我使用以下代码成功了。

我首先创建了一个名为 AddInstance 的类,它有一个 add 方法,然后在我的所有表定义中都继承自它。这似乎可行(即,我可以创建一个类的实例并仅在我想相对容易的情况下添加它)但我不确定是否有任何意外的副作用,或者这与最佳实践相去甚远。

from sqlalchemy import create_engine                                               
from sqlalchemy.ext.declarative import declarative_base                         
from sqlalchemy.orm import sessionmaker, relationship                           
from sqlalchemy import Column, String, Integer                                  


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


class AddInstance:                                                                 

    def add(self):                                                              
        session = Session()                                                        
        session.add(self)                                                       
        session.commit()


class Pizza(Base, AddInstance):                                                    
    __tablename__ = 'pizzas'                                                       
    id = Column(Integer, primary_key=True)                                         
    name = Column(String(50))                                                      
    toppings = relationship('Topping', back_populates='name')                      


class Topping(Base, AddInstance):                                                  
    __tablename__ = 'fruits'                                                       
    id = Column(Integer, primary_key=True)                                         
    name = Column(String(50))                                                      
    pizzas = relationship('Pizza', back_populates='name')                          


Base.metadata.create_all(engine)                                                   

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

    不会有副作用,SQLAlchemy 明确支持添加方法。这些方法是在 mixin 类上定义还是直接在派生自 Base 的类上定义都没有关系。

    引用SQLAlchemy Object Relational Tutorial documentation section:

    除了映射过程对我们的类所做的事情之外,该类仍然主要是一个普通的 Python 类,我们可以在其中定义应用程序所需的任意数量的普通属性和方法。

    粗体强调我的)。

    有很多代码库的例子完全一样。 Flask-SQLAlchemy 为add a query attribute 提供了一个Model 基类(在declarative base is created 时设置),例如,它可以让您直接从模型类中执行Topping.query(...)

    您的add() 方法确实有缺点:它创建一个新的Session() 实例只是为了添加和提交您的对象。这使它不在正常的session state management semantics 之外,如果您想对新创建的对象执行任何其他操作,则必须使用merge it into an existing session

    涉及 SQLAlchemy 对象的代码的正常最佳实践是创建一个会话来管理一个事务,这是一组必须一起成功或失败的操作。这包括创建对象;在许多实际应用程序中,当依赖这些行的其他操作失败时,您希望避免在数据库中创建额外的行。您的 .add() 方法无条件地将每个对象提交到单独的事务中。你可能想重新审视这个模式。

    【讨论】:

    • 当你说他们需要一起成功或失败时,例如,如果你有一个 PizzaTopping 表,你需要同时提交新的披萨和新的 Topping时间或一起失败,否则您最终可能会得到一个 Pizza 引用(或尝试引用)一个不存在的 Topping,反之亦然?
    • @evan54:完全正确。虽然外键约束可以在一定程度上帮助避免此类问题,但现实生活中的应用程序通常需要更多相关的更改,您希望将这些更改一起提交或在出现故障时回滚,从而导致数据库处于不一致状态。
    猜你喜欢
    • 2015-06-03
    • 2014-04-18
    • 2012-07-10
    • 2014-11-03
    • 2015-07-29
    • 2013-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多