简体   繁体   English

如何使用新主键克隆 SQLAlchemy object

[英]How to clone a SQLAlchemy object with new primary key

I want to clone a SQLAlchemy object:我想克隆一个 SQLAlchemy object:

I tried:我试过:

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)

but it just updates the old object.但它只是更新旧的 object。

Here is the code of products.save function:这是products.save function 的代码:

class Service(object):

        __model__ = None

       def save(self, model):
            self._isinstance(model)
            db.session.add(model)
            db.session.commit()
            return model

This should work: 这应该工作:

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)

For sqlalchemy 1.3 I ended up using a helper function. 对于sqlalchemy 1.3,我最终使用了辅助函数。

  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

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')

Depending on your use-case you might want to remove the call to db.session.commit() from this function. 根据您的用例,您可能希望从此函数中删除对db.session.commit()的调用。


This answer is based on https://stackoverflow.com/a/13752442/769486 (How to get a model's columns?) and How do I get the name of an SQLAlchemy object's primary key? 这个答案基于https://stackoverflow.com/a/13752442/769486 (如何获取模型的列?)以及如何获取SQLAlchemy对象的主键名称? (How do I get a model's primary keys?). (如何获得模型的主键?)。

一种可能的方法是使用dictalchemy

new_instance = InstanceModel(**old_instance.asdict())
try this out 
@classmethod
def clone(cls,tempversion,**kwargs):
    attributestoremove=["tempversion","id"]
    relationshipAttributes = cls.get_relationshipAttributes()
    kwargs = { key:(None if value=='nan' else value)  for key,value in kwargs.items() if hasattr(cls, key) and key not in relationshipAttributes and\
    key not in attributestoremove}
    kwargs["tempversion"]=tempversion
    record = cls(**kwargs)
    db.session.add(record)
    db.session.commit()
    return record
    try:
        db.session.commit()
    except IntegrityError:
        db.session.rollback()
        record = None
    return record

This method will clone any sqlalchemy db object.此方法将克隆任何 sqlalchemy db 对象。 Add it to your model's class.将它添加到模型的类中。 Please note the the id of the new object will be created during commit (see comment):请注意新对象的 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

I put together this function to clone objects.我把这个 function 放在一起克隆对象。 It does not assign a primary key, but it will set the existing primary keys to None and they are assigned by the autoincement (if set) once flushed to the database.它不分配主键,但它会将现有的主键设置为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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM