Python: SQLAlchemy 处理 PostgreSQL on conflict

9 篇文章 0 订阅
4 篇文章 0 订阅

目录

一、数据模型定义

二、ON CONFLICT DO NOTHING - 忽略本条数据的插入

三、ON CONFLICT DO UPDATE - 更新旧数据


一、数据模型定义

models.py 文件内是数据模型定义。

import datetime
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Dog(Base):
    __tablename__ = "dog"

    id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True, autoincrement=True)
    name = sqlalchemy.Column(sqlalchemy.String(256), nullable=True)
    uuid = sqlalchemy.Column(sqlalchemy.String(64), unique=True)
    addtime = sqlalchemy.Column(sqlalchemy.DateTime())
    data = sqlalchemy.Column(sqlalchemy.Text(), default='{}')

    def __init__(self, name, uuid, data='{}', addtime=None):
        self.name = name
        self.uuid = uuid
        self.data = data
        self.addtime = addtime if None != addtime else datetime.datetime.now()

其中 uuid 字段是 unique 的,也就是不允许重复,当要插入的数据的 uuid 和表中的某个 uuid 重复时,就会产生冲突,数据库会返回错误

可以使用 PostgreSQL 提供的 ON CONFLICT 特性在数据冲突时做出处理。

处理方式有两种:

  • 遇到冲突时,忽略本条数据的插入
  • 遇到冲突时,更新旧的数据

二、ON CONFLICT DO NOTHING - 忽略本条数据的插入

在 SQLAlchemy 中使用 on_conflict_do_nothing 功能,需要手动写 insert 参数。

完整的 on_conflict_do_nothing 代码如下:

from sqlalchemy.dialects.postgresql import insert
import models
import dbopt
import uuid
import sqlalchemy.orm as orm
import sqlalchemy
import models

url = 'postgresql://postgres:dbpassword@127.0.0.1:5432/zoodb'
engine = sqlalchemy.create_engine(url)
metadata = sqlalchemy.schema.MetaData(bind=engine)
models.Base.metadata.create_all(engine)

Sess = orm.sessionmaker(bind=engine)

def add_random_dog(sess, n=3):
    doges = list()
    for i in range(n):
        dog = models.Dog(
                'dog-{0}'.format(i),
                str(uuid.uuid4()))
        sess.add(dog)
        doges.append(dog)
    sess.commit()

    return doges

def dog_on_conflict_do_nothing(sess, d):
    dog = models.Dog('Big' + d.name, d.uuid)

    insert_stmt = insert(models.Dog).values(
            name = dog.name,
            uuid = dog.uuid,
            addtime = dog.addtime,
            data = dog.data)

    do_nothing_stmt = insert_stmt.on_conflict_do_nothing(
            index_elements=['uuid']
            )
    sess.execute(do_nothing_stmt)
    sess.commit()

def show_dog(sess, doges, msg=''):
    print("{0} =======================================".format(msg))
    uuids = [d.uuid for d in doges]
    for d in sess.query(models.Dog).filter(models.Dog.uuid.in_(uuids)):
        print(d.name, d.uuid, d.addtime, d.data, sep=' | ')

def main():
    sess = Sess()
    doges = add_random_dog(sess, 2)
    show_dog(sess, doges, "after insert")

    # new 一个 dog 对象,但是数据库中此时没有 'new dog'
    doges.append(models.Dog('new dog', str(uuid.uuid4())))

    for d in doges:
        dog_on_conflict_do_nothing(sess, d)
    show_dog(sess, doges, "after update")

if "__main__" == __name__:
    main()

运行结果如下,可以看到 uuid 字段冲突数据的 name 没有发生变化,而没有 uuid 冲突的新数据插入成功:

after insert =======================================
dog-0 | 19d89e94-29da-43bb-8a9b-0acbc7c9d332 | 2021-12-08 18:05:54.522041 | {}
dog-1 | befc7010-2939-4bca-aeb8-bf9dec025266 | 2021-12-08 18:05:54.522369 | {}
after update =======================================
dog-0 | 19d89e94-29da-43bb-8a9b-0acbc7c9d332 | 2021-12-08 18:05:54.522041 | {}
dog-1 | befc7010-2939-4bca-aeb8-bf9dec025266 | 2021-12-08 18:05:54.522369 | {}
Bignew dog | 0f3c0d8f-9560-4308-a7f5-8b101ecb9524 | 2021-12-08 18:05:54.545064 | {}

三、ON CONFLICT DO UPDATE - 更新旧数据

完整的 on_conflict_do_update 代码如下:

from sqlalchemy.dialects.postgresql import insert
import models
import dbopt
import uuid
import sqlalchemy.orm as orm
import sqlalchemy
import models

url = 'postgresql://postgres:dbpassword@127.0.0.1:5432/zoodb'
engine = sqlalchemy.create_engine(url)
metadata = sqlalchemy.schema.MetaData(bind=engine)
models.Base.metadata.create_all(engine)

Sess = orm.sessionmaker(bind=engine)

def add_random_dog(sess, n=3):
    doges = list()
    for i in range(n):
        dog = models.Dog(
                'dog-{0}'.format(i),
                str(uuid.uuid4()))
        sess.add(dog)
        doges.append(dog)
    sess.commit()

    return doges

def dog_on_conflict_do_update(sess, d):
    dog = models.Dog('Big' + d.name, d.uuid)
    insert_stmt = insert(models.Dog).values(
            name=dog.name,
            uuid=dog.uuid,
            addtime=dog.addtime,
            data=dog.data)

    do_update_stmt = insert_stmt.on_conflict_do_update(
            index_elements=['uuid'],
            set_=dict(
                name=dog.name,
                addtime=dog.addtime,
                data=dog.data))
    sess.execute(do_update_stmt)

    sess.commit()

def show_dog(sess, doges, msg=''):
    print("{0} =======================================".format(msg))
    uuids = [d.uuid for d in doges]
    for d in sess.query(models.Dog).filter(models.Dog.uuid.in_(uuids)):
        print(d.name, d.uuid, d.addtime, d.data, sep=' | ')

def main():
    sess = Sess()
    doges = add_random_dog(sess, 2)
    show_dog(sess, doges, "after insert")

    # new 一个 dog 对象,但是数据库中此时没有 'new dog'
    doges.append(models.Dog('new dog', str(uuid.uuid4())))

    for d in doges:
        dog_on_conflict_do_update(sess, d)
    show_dog(sess, doges, "after on_conflict_do_update")

if "__main__" == __name__:
    main()

执行结果如下,可以看到 uuid 冲突的数据,其 name 字段被更新了,而没有 uuid 冲突的数据,添加成功了。

after insert =======================================
dog-0 | 39127e0c-5286-4ce6-a12c-3a967907be0c | 2021-12-08 18:11:42.997584 | {}
dog-1 | 7d3aea63-62be-4643-a99b-04e942c1d80c | 2021-12-08 18:11:42.997909 | {}
after on_conflict_do_update =======================================
Bigdog-0 | 39127e0c-5286-4ce6-a12c-3a967907be0c | 2021-12-08 18:11:43.055750 | {}
Bigdog-1 | 7d3aea63-62be-4643-a99b-04e942c1d80c | 2021-12-08 18:11:43.062493 | {}
Bignew dog | 0988df82-c484-4a22-9418-0fa18f4758ce | 2021-12-08 18:11:43.066149 | {}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值