【问题标题】:Django - save() update on duplicate keyDjango - save() 更新重复键
【发布时间】:2011-09-16 23:45:18
【问题描述】:

我有一个小应用程序可以让用户给视频评分。

用户只能评分一次。 所以我已经定义了模型的唯一性。

但他应该能够改变他的汇率。 所以save() 应该更新重复键

class VideoRate(models.Model):
  """Users can Rate each Video on the criterias defined for the topic"""
  user = models.ForeignKey(User)
  video = models.ForeignKey(VideoFile)
  crit = models.ForeignKey(VideoCrit)
  rate = models.DecimalField(max_digits=2, decimal_places=1, choices=RATE_CHOICES)
  class Meta:
    unique_together = (('user', 'video', 'crit'),)
    verbose_name = 'Video Rating'

如果我

rate = VideoRate(user_id=1, video_id=1, crit_id=1, rate=2)
rate.save()

它正在保存评分,但如果我

rate = VideoRate(user_id=1, video_id=1, crit_id=1, rate=3)
rate.save()

我得到正常错误

IntegrityError: (1062, "Duplicate entry '1-1-1' for key 'user_id'")

即使我使用force_update=True(因为仅基于主键)

如果评级已经存在,有没有办法更新评级而无需之前检查数据?

【问题讨论】:

    标签: django save unique-constraint insert-update


    【解决方案1】:

    要更新现有评级,您实际上必须拥有要更新的评级。如果您知道该对象可能不存在,请使用get_or_create

    rate, created = VideoRate.objects.get_or_create(user_id=1, video_id=1, crit_id=1)
    rate.rate = 2
    rate.save()
    

    您可以使用update() 来缩短流程:

    VideoRate.objects.filter(user_id=1, video_id=1, crit_id=1).update(rate=2)
    

    但如果评级不存在,这将静默失败 - 它不会创建一个。

    【讨论】:

    • +1:第一个选项将执行 2 或 3 个查询,而第二个选项将执行 1 个。
    • 看起来还不错。你的意思是 Django 不能执行 INSERT INTO ... ON DUPLICATE KEY UPDATE ...
    • 不,因为这是特定于 MySQL 的扩展,而 Django 可以与一系列数据库一起使用。
    • 如何在基本模型中添加 put() 方法并结合使用 unique_together?对我来说,这看起来比使用 get_or_create() 更干净。
    • 你可以做到,只需使用低级 API,例如:connection.cursor().execute(sql) where sql is "INSERT INTO example (a, b, c) VALUES (1,2, 3) 在重复键更新时 a = VALUES(a), b = VALUES(b), c = VALUES(c);"
    【解决方案2】:

    首先,您必须检查评级是否存在。因此,您可以使用 Daniel Roseman 所说的内容或使用 exists,但您无法通过简单的更新来解决此问题,因为更新不会创建新记录...

    rating = 2
    rate, created = VideoRate.objects.get_or_create(user_id=1, video_id=1, crit_id=1,
        defaults={'rate':rating})#if create, also save the rate infdormation
    
    if not created:# update
        rate.rate = rating
        rate.save()
    

    您可以使用 defaults 来传递 exrta 参数,因此如果是 insert,将使用所有必需的信息创建数据库记录,您无需更新它又...

    Documentation

    更新:这个答案和问题一样已经很老了。正如@peterthomassen 提到的,Django 现在有update_or_create() 方法

    【讨论】:

    • defaults这个关键字也很有意思
    • defaults 参数中的值也用于在不需要在数据库中创建对象的情况下进行更新,请参阅docs.djangoproject.com/en/1.11/ref/models/querysets/…。因此,您的代码中的 if 部分是无操作的。
    • @peterthomassen 感谢您的通知。更新了答案。
    猜你喜欢
    • 2015-04-04
    • 2023-04-01
    • 2020-10-21
    • 2022-01-12
    • 1970-01-01
    • 2013-08-02
    • 1970-01-01
    • 2015-03-20
    • 2022-01-22
    相关资源
    最近更新 更多