Python 实例化对象会自动添加到SQLAlchemy会话。为什么?

Python 实例化对象会自动添加到SQLAlchemy会话。为什么?,python,session,sqlalchemy,Python,Session,Sqlalchemy,根据我对SQLAlchemy的理解,为了将模型添加到会话中。然而,出于某种原因,在我的代码中,SQLAlchemy似乎自动做到了这一点 它为什么要这样做,我怎样才能阻止它?我是否以正确的方式完成课程 示例 >>> from database import Session as db >>> import clients >>> from instances import Instance >>> from uuid impo

根据我对SQLAlchemy的理解,为了将模型添加到会话中。然而,出于某种原因,在我的代码中,SQLAlchemy似乎自动做到了这一点

它为什么要这样做,我怎样才能阻止它?我是否以正确的方式完成课程

示例

>>> from database import Session as db
>>> import clients
>>> from instances import Instance
>>> from uuid import uuid4
>>> len(db.query(Instance).all())
>>> 0 # Note, no instances in database/session
>>> i = Instance(str(uuid4()), clients.get_by_code('AAA001'), [str(uuid4())])
>>> len(db.query(Instance).all())
>>> 1 # Why?? I never called db.add(i)!
数据库.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base

import config

Base = declarative_base()

class Database():

    def __init__(self):
        db_url = 'postgresql://{:s}:{:s}@{:s}:{}/{:s}'.format(
            config.database['user'],
            config.database['password'],
            config.database['host'],
            config.database['port'],
            config.database['dbname']
        )
        self.engine = create_engine(db_url)
        session_factory = sessionmaker(bind=self.engine)
        self.session = scoped_session(session_factory)

Database = Database()

Session = Database.session
from sqlalchemy import Column, Text, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID, ARRAY

import database

Base = database.Base

class Instance(Base):
    __tablename__ = 'instances'

    uuid = Column(UUID, primary_key=True)
    client_code = Column(
        Text, ForeignKey('clients.code', ondelete='CASCADE'), nullable=False)
    mac_addresses = Column(ARRAY(Text, as_tuple=True),
                           primary_key=True)

    client = relationship("Client", back_populates="instances")

    def __init__(self, uuid, client, mac_addresses):
        self.uuid = uuid
        self.client = client
        self.mac_addresses = tuple(mac_addresses)
from sqlalchemy import Column, Text
from sqlalchemy.orm import relationship

import database
from database import Session as db

Base = database.Base

class Client(Base):
    __tablename__ = 'clients'

    code = Column(Text, primary_key=True)
    name = Column(Text)

    instances = relationship("Instance", back_populates='client')

    def __init__(self, code, name=None):
        self.code = code
        self.name = name

def get_by_code(code):
   client = db.query(Client).filter(Client.code == code).first()
   return client
instance.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base

import config

Base = declarative_base()

class Database():

    def __init__(self):
        db_url = 'postgresql://{:s}:{:s}@{:s}:{}/{:s}'.format(
            config.database['user'],
            config.database['password'],
            config.database['host'],
            config.database['port'],
            config.database['dbname']
        )
        self.engine = create_engine(db_url)
        session_factory = sessionmaker(bind=self.engine)
        self.session = scoped_session(session_factory)

Database = Database()

Session = Database.session
from sqlalchemy import Column, Text, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID, ARRAY

import database

Base = database.Base

class Instance(Base):
    __tablename__ = 'instances'

    uuid = Column(UUID, primary_key=True)
    client_code = Column(
        Text, ForeignKey('clients.code', ondelete='CASCADE'), nullable=False)
    mac_addresses = Column(ARRAY(Text, as_tuple=True),
                           primary_key=True)

    client = relationship("Client", back_populates="instances")

    def __init__(self, uuid, client, mac_addresses):
        self.uuid = uuid
        self.client = client
        self.mac_addresses = tuple(mac_addresses)
from sqlalchemy import Column, Text
from sqlalchemy.orm import relationship

import database
from database import Session as db

Base = database.Base

class Client(Base):
    __tablename__ = 'clients'

    code = Column(Text, primary_key=True)
    name = Column(Text)

    instances = relationship("Instance", back_populates='client')

    def __init__(self, code, name=None):
        self.code = code
        self.name = name

def get_by_code(code):
   client = db.query(Client).filter(Client.code == code).first()
   return client
client.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base

import config

Base = declarative_base()

class Database():

    def __init__(self):
        db_url = 'postgresql://{:s}:{:s}@{:s}:{}/{:s}'.format(
            config.database['user'],
            config.database['password'],
            config.database['host'],
            config.database['port'],
            config.database['dbname']
        )
        self.engine = create_engine(db_url)
        session_factory = sessionmaker(bind=self.engine)
        self.session = scoped_session(session_factory)

Database = Database()

Session = Database.session
from sqlalchemy import Column, Text, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID, ARRAY

import database

Base = database.Base

class Instance(Base):
    __tablename__ = 'instances'

    uuid = Column(UUID, primary_key=True)
    client_code = Column(
        Text, ForeignKey('clients.code', ondelete='CASCADE'), nullable=False)
    mac_addresses = Column(ARRAY(Text, as_tuple=True),
                           primary_key=True)

    client = relationship("Client", back_populates="instances")

    def __init__(self, uuid, client, mac_addresses):
        self.uuid = uuid
        self.client = client
        self.mac_addresses = tuple(mac_addresses)
from sqlalchemy import Column, Text
from sqlalchemy.orm import relationship

import database
from database import Session as db

Base = database.Base

class Client(Base):
    __tablename__ = 'clients'

    code = Column(Text, primary_key=True)
    name = Column(Text)

    instances = relationship("Instance", back_populates='client')

    def __init__(self, code, name=None):
        self.code = code
        self.name = name

def get_by_code(code):
   client = db.query(Client).filter(Client.code == code).first()
   return client

当您创建一个SQLAlchemy对象并将其直接链接到另一个SQLAlchemy对象时,两个对象都会在会话中结束

原因是SQLAlchemy需要确保您可以查询这些对象。 以一个有地址的用户为例

如果您在代码中创建了一个带有地址的用户,那么该用户和地址都将在会话中结束,因为该地址链接到该用户,SQLAlchemy需要确保您可以使用user.addresses.all()查询用户的所有地址

在这种情况下,需要获取所有(可能)现有地址,以及刚刚添加的新地址。为此,需要将新添加的地址保存在数据库中。
为了防止发生这种情况(例如,如果您只需要使用对象进行计算),可以使用对象的ID/外键链接对象:

address.user_id = user.user_id
但是,如果这样做,您将无法再访问SQLAlchemy属性。因此
user.addresses
address.user
将不再产生结果

反之亦然;不久前我自己问了一个问题,为什么通过ID链接两个对象不会导致SQLAlchemy在ORM中链接这些对象:


我认为添加到会话中的实例不是新创建的对象。我怀疑它是由
客户端加载的
客户端的实例。
实例的查询肯定不会返回
客户端的实例?查询是
db.query(Instance).all()
not
db.query(Client).all()
,因此我不希望在响应中得到任何
Client
对象。也就是说,我已经找到了“问题”的根源。SQLAlchemy将
实例
对象添加到会话中的原因是关系中的
back\u填充
。如果我不添加
返回\u填充
db.query(Instances).all()
按预期返回一个空列表。这样做的副作用是,关系不能很好地工作,因为它不能将
客户端
实例加载到会话中,并且您不能执行
实例.Client
来加载相关对象。我已经停止使用SQLAlchemy的mapper,因为它给我带来了太多的麻烦,但希望这能帮助其他人。