V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Sponsored by
LinkedIn
不坐班的神仙工作 · 去任何你想去的地方远程,赚一线城市的工资
2000 个不用出门 Social 的全球远程工作,帮助 V2EX 的小伙伴开启全新的工作方式。
Promoted by LinkedIn
wuming
V2EX  ›  问与答

Django 3.0 中的 model.save() 行为问题

  •  
  •   wuming · 2020-02-14 11:55:21 +08:00 · 1350 次点击
    这是一个创建于 959 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在 release note 中写的是

    Model.save() no longer attempts to find a row when saving a new Model instance and a default value for the primary key is provided, and always performs a single INSERT query. 
    
    This makes calling Model.save() while providing a default primary key value equivalent to passing force_insert=True to model’s save(). Attempts to use a new Model instance to update an existing row will result in an IntegrityError.
    
    In order to update an existing model for a specific primary key value, use the update_or_create() method or QuerySet.filter(pk=…).update(…) instead. 
    

    貌似是 model 中有 id 时,只会执行插入而不会更新,从而可能触发 IntegrityError。

    但是我在测试的时候发现

    Class Order(models.Model):
        status = models.PositiveSmallIntegerField(default=1)
    
    order = Order.objects.filter(id=1).first()
    order.status = 2
    order.save()
    
    或者
    
    order = Order()
    order.id =1
    order.status=2
    order.save()
    

    这里 id=1 的行已经存在,但是都能够正常保存。请问我在哪里理解错误了?又是什么情况才会触发文档中的错误。

    6 条回复    2020-02-14 16:26:52 +08:00
    vicalloy
        1
    vicalloy  
       2020-02-14 12:51:19 +08:00
    你没有看清楚,“force_insert=True”的时候才会强制插入导致报错。
    wuming
        2
    wuming  
    OP
       2020-02-14 13:30:39 +08:00
    @vicalloy 这上面不是说的是当提供主键时,相当于给 model.save() 传递 force_insert =True 么。也就是不需要我主动传递 force_insert
    hzwjz
        3
    hzwjz  
       2020-02-14 13:32:05 +08:00 via Android
    Attempts to use a new Model instance to update an existing row will result in an IntegrityError.


    old_order 已存在的情况下且 id 我假设为 1,然后
    new_order = Order()
    new_order.id = 1
    new_order.save(). # IntegrityError
    wuming
        4
    wuming  
    OP
       2020-02-14 13:48:36 +08:00
    @hzwjz 刚才这样做了,还是没有任何错误。

    old_order = Order.objects.get(id=1)
    print(old_order.id) #执行完之后看到的 id=1
    new_order = Order()
    new_order.id = 1
    new_order.save() #执行完之后没有任何错误。
    qqxx520
        5
    qqxx520  
       2020-02-14 15:58:01 +08:00   ❤️ 1
    我的理解, "Default value for primary key is provided"意思是在 model 建立时的主键字段有 default=xxx, 例如
    Class Order(models.Model):
    id=models.AutoField('ID', primary_key=True, auto_created=True, default=True)
    status = models.PositiveSmallIntegerField(default=1)

    测试代码:
    >>> a=Order()
    >>> a.save()
    >>> a
    <Order: Order object (1)>
    >>> a.id
    1
    >>> b=Order()
    >>> b.save()
    Traceback (most recent call last):
    ...
    sqlite3.IntegrityError: UNIQUE constraint failed: order_order.id
    >>> b.id=2
    >>> b.save()
    >>> a,b
    (<Order: Order object (1)>, <Order: Order object (2)>)

    但是 id 字段的 default value 是 True,似乎没什么意思,可以改进为如下的定义:
    import time
    from django.db import models

    def get_pk():
    return int(time.time() * 10000)

    class Order(models.Model):
    id=models.AutoField('ID', primary_key=True, auto_created=True, default=get_pk)
    status = models.PositiveSmallIntegerField(default=1)

    测试代码:
    >>> c=Order()
    >>> c.save()
    >>> c.id
    15816669672836
    >>> d=Order()
    >>> d.save()
    >>> d.id
    15816669758912

    -- 完毕 --
    wuming
        6
    wuming  
    OP
       2020-02-14 16:26:52 +08:00
    @qqxx520 感谢
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2205 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 47ms · UTC 12:21 · PVG 20:21 · LAX 05:21 · JFK 08:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.