Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clone a sqlalchemy db 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 
like image 660
Harshit Agarwal Avatar asked Mar 05 '15 06:03

Harshit Agarwal


People also ask

Does SQLAlchemy require primary key?

¶ The SQLAlchemy ORM, in order to map to a particular table, needs there to be at least one column denoted as a primary key column; multiple-column, i.e. composite, primary keys are of course entirely feasible as well.

How do I create a composite primary key in SQLAlchemy?

To create a composite primary key, set primary_key to True on each column involved in the key. A boolean argument when set to False adds NOT NULL constraint while creating a column. Its default value is True .

What does First () do in SQLAlchemy?

Return the first result of this Query or None if the result doesn't contain any row. first() applies a limit of one within the generated SQL, so that only one primary entity row is generated on the server side (note this may consist of multiple result rows if join-loaded collections are present).

What is primary key in Flask SQLAlchemy?

Most ORMs require that objects have some kind of primary key defined because the object in memory must correspond to a uniquely identifiable row in the database table; at the very least, this allows the object can be targeted for UPDATE and DELETE statements which will affect only that object's row and no other.


2 Answers

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) 
like image 131
Tasos Vogiatzoglou Avatar answered Sep 16 '22 12:09

Tasos Vogiatzoglou


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

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

like image 36
zwirbeltier Avatar answered Sep 17 '22 12:09

zwirbeltier