【Python百日进阶-Web开发-Peewee】Day255 - Peewee 更新、删除记录

8.3 更新现有记录

一旦模型实例具有主键,任何后续调用都 save()将导致UPDATE而不是另一个INSERT。模型的主键不会改变:

>>> user.save()  # save() returns the number of rows modified.
1
>>> user.id
1
>>> user.save()
>>> user.id
1
>>> huey.save()
1
>>> huey.id
2

如果要更新多条记录,请发出UPDATE查询。以下示例将更新所有Tweet对象,将它们标记为已发布,如果它们是在今天之前创建的。Model.update()接受关键字参数,其中键对应于模型的字段名称:

>>> today = datetime.today()
>>> query = Tweet.update(is_published=True).where(Tweet.creation_date < today)
>>> query.execute()  # Returns the number of rows that were updated.
4

有关详细信息,请参阅有关Model.update()和 Update的文档Model.bulk_update()。

笔记

如果您想了解有关执行原子更新的更多信息(例如增加列的值),请查看原子更新 配方。

8.4 Atomic updates原子更新

Peewee 允许您执行原子更新。假设我们需要更新一些计数器。天真的方法是这样写:

>>> for stat in Stat.select().where(Stat.url == request.url):
...     stat.counter += 1
...     stat.save()

不要这样做!这不仅速度慢,而且如果多个进程同时更新计数器,它也容易受到竞争条件的影响。

相反,您可以使用以下方法自动更新计数器update():

>>> query = Stat.update(counter=Stat.counter + 1).where(Stat.url == request.url)
>>> query.execute()

您可以根据需要使这些更新语句变得复杂。让我们给所有员工的奖金等于他们之前的奖金加上他们工资的 10%:

>>> query = Employee.update(bonus=(Employee.bonus + (Employee.salary * .1)))
>>> query.execute()  # Give everyone a bonus!

我们甚至可以使用子查询来更新列的值。假设我们在模型上有一个非规范化列,User用于存储用户发布的推文数量,并且我们会定期更新此值。以下是编写此类查询的方法:

>>> subquery = Tweet.select(fn.COUNT(Tweet.id)).where(Tweet.user == User.id)
>>> update = User.update(num_tweets=subquery)
>>> update.execute()

8.4.1 Upsert

Peewee 提供对不同类型的 upsert 功能的支持。对于 3.24.0 之前的 SQLite 和 MySQL,Peewee 提供了replace(),它允许您插入记录,或者在违反约束的情况下替换现有记录。

使用replace()and的例子on_conflict_replace():

class User(Model):
    username = TextField(unique=True)
    last_login = DateTimeField(null=True)

# Insert or update the user. The "last_login" value will be updated
# regardless of whether the user existed previously.
user_id = (User
           .replace(username='the-user', last_login=datetime.now())
           .execute())

# This query is equivalent:
user_id = (User
           .insert(username='the-user', last_login=datetime.now())
           .on_conflict_replace()
           .execute())

笔记

除了replace之外,如果您只想插入并忽略任何潜在的约束违规,SQLite、MySQL 和 Postgresql 还提供忽略
操作(请参阅:)。on_conflict_ignore()

MySQL通过ON DUPLICATE KEY UPDATE子句支持 upsert 。例如:

class User(Model):
    username = TextField(unique=True)
    last_login = DateTimeField(null=True)
    login_count = IntegerField()

# Insert a new user.
User.create(username='huey', login_count=0)

# Simulate the user logging in. The login count and timestamp will be
# either created or updated correctly.
now = datetime.now()
rowid = (User
         .insert(username='huey', last_login=now, login_count=1)
         .on_conflict(
             preserve=[User.last_login],  # Use the value we would have inserted.
             update={User.login_count: User.login_count + 1})
         .execute())

在上面的示例中,我们可以安全地多次调用 upsert 查询。登录计数将自动增加,最后登录列将被更新,并且不会创建重复的行。

Postgresql 和 SQLite(3.24.0 和更新版本)提供了不同的语法,允许更精细地控制哪些约束违反应该触发冲突解决,以及应该更新或保留哪些值。

on_conflict()用于执行 Postgresql 样式的 upsert(或 SQLite 3.24+)的示例:

class User(Model):
    username = TextField(unique=True)
    last_login = DateTimeField(null=True)
    login_count = IntegerField()

# Insert a new user.
User.create(username='huey', login_count=0)

# Simulate the user logging in. The login count and timestamp will be
# either created or updated correctly.
now = datetime.now()
rowid = (User
         .insert(username='huey', last_login=now, login_count=1)
         .on_conflict(
             conflict_target=[User.username],  # Which constraint?
             preserve=[User.last_login],  # Use the value we would have inserted.
             update={User.login_count: User.login_count + 1})
         .execute())

在上面的示例中,我们可以安全地多次调用 upsert 查询。登录计数将自动增加,最后登录列将被更新,并且不会创建重复的行。

笔记

MySQL 和 Postgresql/SQLite 之间的主要区别在于 Postgresql 和 SQLite
要求您指定一个conflict_target.

这是一个使用命名空间的更高级(如果是人为的)示例EXCLUDED 。EXCLUDED帮助器允许我们引用冲突数据中的值。对于我们的示例,我们将假设一个简单的表将唯一键(字符串)映射到值(整数):

class KV(Model):
    key = CharField(unique=True)
    value = IntegerField()

# Create one row.
KV.create(key='k1', value=1)

# Demonstrate usage of EXCLUDED.
# Here we will attempt to insert a new value for a given key. If that
# key already exists, then we will update its value with the *sum* of its
# original value and the value we attempted to insert -- provided that
# the new value is larger than the original value.
query = (KV.insert(key='k1', value=10)
         .on_conflict(conflict_target=[KV.key],
                      update={KV.value: KV.value + EXCLUDED.value},
                      where=(EXCLUDED.value > KV.value)))

# Executing the above query will result in the following data being
# present in the "kv" table:
# (key='k1', value=11)
query.execute()

# If we attempted to execute the query *again*, then nothing would be
# updated, as the new value (10) is now less than the value in the
# original row (11).

有关详细信息,请参阅Insert.on_conflict()和 OnConflict。

8.5 删除记录

要删除单个模型实例,您可以使用 Model.delete_instance()快捷方式。delete_instance() 将删除给定的模型实例,并且可以选择递归地删除任何依赖对象(通过指定recursive=True)。

>>> user = User.get(User.id == 1)
>>> user.delete_instance()  # Returns the number of rows deleted.
1

>>> User.get(User.id == 1)
UserDoesNotExist: instance matching query does not exist:
SQL: SELECT t1."id", t1."username" FROM "user" AS t1 WHERE t1."id" = ?
PARAMS: [1]

要删除任意一组行,您可以发出DELETE查询。以下将删除所有Tweet超过一年的对象:

>>> query = Tweet.delete().where(Tweet.creation_date < one_year_ago)
>>> query.execute()  # Returns the number of rows deleted.
7

有关更多信息,请参阅以下文档:

  • Model.delete_instance()
  • Model.delete()
  • DeleteQuery
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岳涛@心馨电脑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值