简体   繁体   中英

How to clone a SQLAlchemy object with new primary key

I want to clone a 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.

Here is the code of 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.

  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.


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? (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. Add it to your model's class. Please note the the id of the new object will be created during commit (see comment):

    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. 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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