如何使用新主键克隆sqlalchemy数据库对象

Har*_*wal 22 python sqlalchemy python-2.7 flask-sqlalchemy

我想克隆一个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)
Run Code Online (Sandbox Code Playgroud)

它只是更新old_object

这是products.save函数的代码

class Service(object):

        __model__ = None

       def save(self, model):
            self._isinstance(model)
            db.session.add(model)
            db.session.commit()
            return model
Run Code Online (Sandbox Code Playgroud)

Tas*_*lou 26

这应该工作:

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)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!还有像我这样的新手的注意事项:(1)在`sqlalchemy.orm.session import make_transient`中找到make_transient,以及(2)将主键设置为None,然后在`session.add(obj)上使用自动生成主键`和`session.commit()`. (4认同)
  • 这个“解决方案”使我的很多值都为空。 (3认同)
  • make_transient 不删除主键似乎很烦人。如果创建条目的副本是主要用例,这难道没有意义吗? (2认同)
  • @SebK问题是你可能需要一个对象的副本而不改变PK(无论出于何种原因).通过保持PK,API更具包容性,因为在您需要时更改ID比在需要时保留ID引用更清晰. (2认同)
  • 这是非常危险的,因为现在对该对象的所有引用都将引用新对象。甚至通过不同的执行途径获得不同名称的引用。按属性复制对象属性更安全。 (2认同)
  • 我同意 Muposat 的观点,即这个解决方案是非常危险的。引用同一对象的所有先前变量随后都会更改并指向副本。 (2认同)

Ber*_*pac 7

一种可能的方法是使用dictalchemy

new_instance = InstanceModel(**old_instance.asdict())
Run Code Online (Sandbox Code Playgroud)

  • 它不存在——这只是虚构示例类的名称。我可能因为没有使用与原始问题相同的变量名而变得不清楚,但是如果您想象原始问题中产品的模型类是 `ProductModel`,那么第一行将类似于 `product_obj = ProductModel( **products.all()[0].asdict())`(前提是`dictalchemy`)。或者,您可以使用 `type` 找到对象的类:`product_obj = type(products.all()[0])(**products.all()[0].asdict())`。 (3认同)

小智 6

此方法将克隆任何 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
Run Code Online (Sandbox Code Playgroud)

  • 您无需提交即可自动填充自动生成的“id”字段;`db.session.flush()` 应该足够了。 (3认同)

zwi*_*ier 5

For sqlalchemy 1.3 I ended up using a helper function.

  1. It copies all the non-primary-key columns from the input model to a new model instance.
  2. It allows you to pass data directly as keyword arguments.
  3. It leaves the original model object unmodified.
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]
    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
Run Code Online (Sandbox Code Playgroud)

With this function you can solve the above problem using:

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

Depending on your use-case you might want to remove the call to db.session.commit() from this function.


This answer is based on /sf/answers/962670971/ (How to get a model’s columns?) and How do I get the name of an SQLAlchemy object's primary key? (How do I get a model’s primary keys?).

  • 有人用这种方法处理人际关系吗? (2认同)