如何使用新主键克隆 SQLAlchemy 对象

问题描述 投票:0回答:5

我想克隆一个 SQLAlchemy 对象:

我试过:

product_obj = products.all()[0] #here products is service name

product_obj.product_uid = 'soemthing' #here product_uid is the pk of product model

products.save(product_obj)

但它只是更新旧对象。

这里是

products.save
函数的代码:

class Service(object):

        __model__ = None

       def save(self, model):
            self._isinstance(model)
            db.session.add(model)
            db.session.commit()
            return model
python sqlalchemy flask-sqlalchemy
5个回答
38
投票

这应该有效:

product_obj = products.all()[0]

db.session.expunge(product_obj)  # expunge the object from session
make_transient(product_obj)  # http://docs.sqlalchemy.org/en/rel_1_1/orm/session_api.html#sqlalchemy.orm.session.make_transient

product_obj.product_uid = 'something'
db.session.add(product_obj)

make_transient


23
投票

对于 sqlalchemy 1.3,我最终使用了一个辅助函数。

  1. 它将所有非主键列从输入模型复制到新模型实例。
  2. 它允许您直接将数据作为关键字参数传递。
  3. 它使原始模型对象保持不变。
def clone_model(model, **kwargs):
    """Clone an arbitrary sqlalchemy model object without its primary key values."""
    # Ensure the model’s data is loaded before copying.
    model.id

    table = model.__table__
    non_pk_columns = [k for k in table.columns.keys() if k not in table.primary_key.columns.keys()]
    data = {c: getattr(model, c) for c in non_pk_columns}
    data.update(kwargs)

    clone = model.__class__(**data)
    db.session.add(clone)
    db.session.commit()
    return clone

使用此功能,您可以使用以下方法解决上述问题:

product_obj = products.all()[0]  # Get the product from somewhere.
cloned_product_obj = clone_model(product_obj, product_uid='something')

根据您的用例,您可能希望从此函数中删除对

db.session.commit()
的调用。


此答案基于https://stackoverflow.com/a/13752442/769486(如何获取模型的列?)和如何获取 SQLAlchemy 对象的主键的名称?(如何获取模型的列?获取模型的主键?)。


10
投票

一种可能的方法是使用 dictalchemy,它扩展了 SQLAlchemy:

Dictalchemy 将 utils.asdict() 和 utils.fromdict() 方法添加到 SQLAlchemy 模型。

例子:

new_instance = InstanceModel(**old_instance.asdict())

3
投票

此方法将克隆任何 sqlalchemy 数据库对象。将它添加到模型的类中。 请注意,新对象的 ID 将在提交期间创建(见评论):

def clone(self):
    d = dict(self.__dict__)
    d.pop("id") # get rid of id
    d.pop("_sa_instance_state") # get rid of SQLAlchemy special attr
    copy = self.__class__(**d)
    db.session.add(copy)
    # db.session.commit() if you need the id immediately
    return copy

0
投票

我把这个函数放在一起克隆对象。它不分配主键,但它会将现有的主键设置为

None
,并且一旦刷新到数据库,它们就会由自动增强(如果设置)分配。

from sqlalchemy.orm.session import make_transient
from sqlalchemy import inspect

def clone_sql_object(obj, session):

    # remove the object from the session (set its state to detached)
    session.expunge(obj)

    # make it transient (set its state to transient)
    make_transient(obj)

    # now set all primary keys to None so there are no conflicts later
    for pk in inspect(obj).mapper.primary_key:
        setattr(obj, pk.name, None)

    return obj
© www.soinside.com 2019 - 2024. All rights reserved.