Python peewee save() doesn't work as expected
Asked Answered
L

4

6

I'm inserting/updating objects into a MySQL database using the peewee ORM for Python. I have a model like this:

class Person(Model):
    person_id = CharField(primary_key=True)
    name = CharField()

I create the objects/rows with a loop, and each time through the loop have a dictionary like:

pd = {"name":"Alice","person_id":"A123456"}

Then I try creating an object and saving it.

po = Person()
for key,value in pd.items():
    setattr(po,key,value)
po.save()

This takes a while to execute, and runs without errors, but it doesn't save anything to the database -- no records are created.

This works:

Person.create(**pd)

But also throws an error (and terminates the script) when the primary key already exists. From reading the manual, I thought save() was the function I needed -- that peewee would perform the update or insert as required.

Not sure what I need to do here -- try getting each record first? Catch errors and try updating a record if it can't be created? I'm new to peewee, and would normally just write INSERT ... ON DUPLICATE KEY UPDATE or even REPLACE.

Leyba answered 4/5, 2015 at 19:15 Comment(0)
L
2

I've had a chance to re-test my answer, and I think it should be replaced. Here's the pattern I can now recommend; first, use get_or_create() on the model, which will create the database row if it doesn't exist. Then, if it is not created (object is retrieved from db instead), set all the attributes from the data dictionary and save the object.

po, created = Person.get_or_create(person_id=pd["person_id"],defaults=pd)
if created is False:
    for key in pd:
        setattr(fa,key,pd[key])
    po.save()

As before, I should mention that these are two distinct transactions, so this should not be used with multi-user databases requiring a true upsert in one transaction.

Leyba answered 14/5, 2015 at 15:43 Comment(2)
It should be noted this is only OK for single user databases, because this is clearly two transactions. If you want to properly replicate upsert, in a way that properly handles concurrency, you must use raw queries peewee.readthedocs.org/en/latest/peewee/…Leyba
avoid sqlite in multi-user setupsNinetieth
B
9
Person.save(force_insert=True)

It's documented: http://docs.peewee-orm.com/en/latest/peewee/models.html#non-integer-primary-keys-composite-keys-and-other-tricks

Boyhood answered 4/5, 2015 at 20:25 Comment(3)
This doesn't work for me. It throws an error when the primary key exists, just like Person.create(). It seems to be equivalent to Person.create().Leyba
All jokes intended: funniest function invocation I've seen this week. HA. I'd hate to be that Person object!Volga
It is not logical to "hate" Mr Spock :PIndicate
L
2

I've had a chance to re-test my answer, and I think it should be replaced. Here's the pattern I can now recommend; first, use get_or_create() on the model, which will create the database row if it doesn't exist. Then, if it is not created (object is retrieved from db instead), set all the attributes from the data dictionary and save the object.

po, created = Person.get_or_create(person_id=pd["person_id"],defaults=pd)
if created is False:
    for key in pd:
        setattr(fa,key,pd[key])
    po.save()

As before, I should mention that these are two distinct transactions, so this should not be used with multi-user databases requiring a true upsert in one transaction.

Leyba answered 14/5, 2015 at 15:43 Comment(2)
It should be noted this is only OK for single user databases, because this is clearly two transactions. If you want to properly replicate upsert, in a way that properly handles concurrency, you must use raw queries peewee.readthedocs.org/en/latest/peewee/…Leyba
avoid sqlite in multi-user setupsNinetieth
N
1

I think you might try get_or_create()? http://peewee.readthedocs.org/en/latest/peewee/querying.html#get-or-create

Nafis answered 4/5, 2015 at 19:35 Comment(0)
G
0

You may do something like:

po = Person()
for key,value in pd.items():
    setattr(po,key,value)
updated = po.save()
if not updated:
    po.save(force_insert=True)
Gem answered 4/11, 2022 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.