Django: save() vs update()来更新数据库?

我正在编写一个 Django 应用程序,我需要一个函数来更新数据库中的一个字段。有什么理由使用这些方法中的一种而不是另一种呢?

def save_db_field(name,field,value):
obj = MyModel.objects.get(name=name)
obj.field = value
obj.save()


def update_db_field(name,field,value):
MyModel.objects.get(name=name).update(field=value)

第二个似乎更好,因为它是在一个 DB 调用中完成的,而不是在两个 DB 调用中完成。为什么获取、然后更新会更好呢?

150014 次浏览

Save ()方法可用于插入新记录和更新现有记录,通常用于在数据库中保存单个记录(mysql 中的行)的实例。

Update ()不用于插入记录,可用于更新数据库中的多个记录(mysql 中的行)。

更新仅在更新查询集时有效。如果您想同时更新多个字段,比如从一个对象实例的 dict 中,您可以执行以下操作:

obj.__dict__.update(your_dict)
obj.save()

请记住,字典必须包含正确的映射,其中键需要是字段名,值需要插入。

两者看起来相似,但有一些关键点:

  1. save()将触发任何重写的 Model.save()方法,但是 update()将不触发此方法,而是直接在数据库级别进行更新。因此,如果您有一些带有重写保存方法的模型,那么您必须要么避免使用 update,要么找到另一种方法来对这些重写的 save()方法执行任何操作。

  2. 如果你不小心,obj.save()可能会有一些副作用。使用 get(...)检索对象,并将所有模型字段值传递给 obj。调用 obj.save()时,django 将保存要记录的当前对象状态。因此,如果 get()save()之间发生了其他进程的变化,那么这些变化将会丢失。使用 save(update_fields=[.....])来避免这类问题。

  3. 在 Django 版本1.5之前,Django 在 INSERT/UPDATE之前执行 SELECT,因此它需要执行2个查询。在版本1.5中,不推荐使用该方法。

在这里 ,有一个很好的指南或者 save()update()方法,以及它们是如何执行的。

有几个关键的区别。

update用于查询集,因此可以一次更新多个对象。

正如 @ 堕落天使所指出的,定制 save()方法触发器的方式有所不同,但是记住 signalsModelManagers也很重要。我已经建立了一个小的测试应用程序,以显示一些有价值的差异。我使用的是 Python 2.7.5,Django = = 1.7.7和 SQLite,请注意,最终的 SQL 可能因 Django 的不同版本和不同的数据库引擎而有所不同。

这是示例代码。

返回文章页面

from __future__ import print_function
from django.db import models
from django.db.models import signals
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver


__author__ = 'sobolevn'


class CustomManager(models.Manager):
def get_queryset(self):
super_query = super(models.Manager, self).get_queryset()
print('Manager is called', super_query)
return super_query




class ExtraObject(models.Model):
name = models.CharField(max_length=30)


def __unicode__(self):
return self.name




class TestModel(models.Model):


name = models.CharField(max_length=30)
key = models.ForeignKey('ExtraObject')
many = models.ManyToManyField('ExtraObject', related_name='extras')


objects = CustomManager()


def save(self, *args, **kwargs):
print('save() is called.')
super(TestModel, self).save(*args, **kwargs)


def __unicode__(self):
# Never do such things (access by foreing key) in real life,
# because it hits the database.
return u'{} {} {}'.format(self.name, self.key.name, self.many.count())




@receiver(pre_save, sender=TestModel)
@receiver(post_save, sender=TestModel)
def reicever(*args, **kwargs):
print('signal dispatched')

返回文章页面

def index(request):
if request and request.method == 'GET':


from models import ExtraObject, TestModel


# Create exmple data if table is empty:
if TestModel.objects.count() == 0:
for i in range(15):
extra = ExtraObject.objects.create(name=str(i))
test = TestModel.objects.create(key=extra, name='test_%d' % i)
test.many.add(test)
print test


to_edit = TestModel.objects.get(id=1)
to_edit.name = 'edited_test'
to_edit.key = ExtraObject.objects.create(name='new_for')
to_edit.save()


new_key = ExtraObject.objects.create(name='new_for_update')
to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key)
# return any kind of HttpResponse

这导致了这些 SQL 查询:

# to_edit = TestModel.objects.get(id=1):
QUERY = u'SELECT "main_testmodel"."id", "main_testmodel"."name", "main_testmodel"."key_id"
FROM "main_testmodel"
WHERE "main_testmodel"."id" = %s LIMIT 21'
- PARAMS = (u'1',)


# to_edit.save():
QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s
WHERE "main_testmodel"."id" = %s'
- PARAMS = (u"'edited_test'", u'2', u'1')


# to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key):
QUERY = u'UPDATE "main_testmodel" SET "name" = %s, "key_id" = %s
WHERE "main_testmodel"."id" = %s'
- PARAMS = (u"'updated_name'", u'3', u'2')

我们只有一个 update()查询和两个 save()查询。

接下来,让我们讨论重写 save()方法。显然,对于 save()方法只调用一次。值得一提的是,.objects.create()也调用 save()方法。

但是 update()并不在模型上调用 save()。如果没有为 update()调用 save()方法,那么信号也不会被触发。产出:

Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.


# TestModel.objects.get(id=1):
Manager is called [<TestModel: edited_test new_for 0>]
Manager is called [<TestModel: edited_test new_for 0>]
save() is called.
signal dispatched
signal dispatched


# to_update = TestModel.objects.filter(id=2).update(name='updated_name', key=new_key):
Manager is called [<TestModel: edited_test new_for 0>]

你可以看到 save()触发 Managerget_queryset()两次。当 update()只有一次。

决心。如果您需要“悄悄地”更新您的值,而不调用 save()-使用 update。用例: last_seen用户字段。当您需要正确地更新您的模型时,请使用 save()

对于包含多个对象的查询集,更新将提供更好的性能,因为它将为每个查询集执行一个数据库调用。

然而,保存是有用的,因为它很容易覆盖模型中的保存方法并在其中添加额外的逻辑。例如,在我自己的应用程序中,当其他字段更改时,我会更新日期。

Class myModel(models.Model):
name = models.CharField()
date_created = models.DateField()


def save(self):
if not self.pk :
### we have a newly created object, as the db id is not set
self.date_created = datetime.datetime.now()
super(myModel , self).save()

直接使用更新是更多的 有效率,也可以防止 正直问题。

来自官方文档 < a href = “ https://docs.djangoproject.com/en/3.0/ref/model/querysets/# django.db.mods.query.QuerySet.update”rel = “ norefrer”> https://docs.djangoproject.com/en/3.0/ref/models/querysets/#django.db.models.query

如果您只是更新一个记录,并且不需要使用 对于模型对象,最有效的方法是调用 update () , 而不是将模型对象加载到内存中 这样做:

e = Entry.objects.get(id=10)
e.comments_on = False
e.save()

这样做:

Entry.objects.filter(id=10).update(comments_on=False)

使用 update ()还可以防止出现竞态条件 在加载之间的短时间内更改数据库 对象并调用 save ()。

使用 _ state.add 区分更新和创建 https://docs.djangoproject.com/en/3.2/ref/models/instances/

def save(self, *args, **kwargs):
# Check how the current values differ from ._loaded_values. For example,
# prevent changing the creator_id of the model. (This example doesn't
# support cases where 'creator_id' is deferred).
if not self._state.adding and (
self.creator_id != self._loaded_values['creator_id']):
raise ValueError("Updating the value of creator isn't allowed")
super().save(*args, **kwargs)

save() :

  • 可以与 模型对象一起使用,但不能与 ABC0或 Manager对象一起使用。
  • select_for_update()可以运行 SELECT FOR UPDATE查询

update()

  • 可以与 ABC0或 Manager对象一起使用,但不能与 模型对象一起使用。
  • select_for_update()不能运行 SELECT FOR UPDATE查询

例如,我有如下所示的 Person:

# "store/models.py"


from django.db import models


class Person(models.Model):
name = models.CharField(max_length=30)


def __str__(self):
return self.name

然后,你可以使用 save()Person模型对象,如下所示:

# "store/views.py"


from .models import Person
from django.http import HttpResponse


def test(request):
person = Person.objects.get(id=1)
person.name = 'Tom'
person.save() # Here


return HttpResponse("Test")
# "store/views.py"


from .models import Person
from django.http import HttpResponse


def test(request):
person = Person.objects.filter(pk=1).first()
person.name = 'Tom'
person.save() # Here


return HttpResponse("Test")

但是,你不能把 save()QuerySet对象一起使用,如下所示:

# "store/views.py"


from .models import Person
from django.http import HttpResponse


def test(request):
person = Person.objects.filter(pk=1)
person.name = 'Tom'
person.save() # Here


return HttpResponse("Test")

然后,出现以下错误:

AttributeError: ‘ QuerySet’对象没有‘ save’属性

而且,你不能把 save()Manager对象一起使用,如下所示:

# "store/views.py"


from .models import Person
from django.http import HttpResponse


def test(request):
person = Person.objects
person.name = 'Tom'
person.save() # Here


return HttpResponse("Test")

然后,出现以下错误:

AttributeError: “ Manager”对象没有“ save”属性

然后,你可以使用 update()QuerySet对象,如下所示:

# "store/views.py"


from .models import Person
from django.http import HttpResponse


def test(request):
person = Person.objects.filter(pk=1)
person.update(name="Tom") # Here


return HttpResponse("Test")

而且,你可以使用 update()Manager对象,如下所示:

# "store/views.py"


from .models import Person
from django.http import HttpResponse


def test(request):
person = Person.objects
person.update(name="Tom") # Here


return HttpResponse("Test")

但是,你不能把 update()Person模型对象一起使用,如下所示:

# "store/views.py"


from .models import Person
from django.http import HttpResponse


def test(request):
person = Person.objects.get(id=1)
person.update(name="Tom") # Here


return HttpResponse("Test")
# "store/views.py"


from .models import Person
from django.http import HttpResponse


def test(request):
person = Person.objects.filter(pk=1).first()
person.update(name="Tom") # Here


return HttpResponse("Test")

然后,出现以下错误:

AttributeError: ‘ Person’对象没有属性‘ update’

接下来,例如,select_for_update()用于在 Django 中更新数据时阻止 竞态条件(丢失更新或写倾斜)

而且,我有 test视图save()select_for_update().filter(pk=1).first()如下所示:

# "store/views.py"


from django.db import transaction
from .models import Person
from django.http import HttpResponse


@transaction.atomic
def test(request):
person = Person.objects.select_for_update().filter(pk=1).first() # Here
person.name = 'Tom'
person.save() # Here


return HttpResponse("Test")

然后,当我运行 test视图时,如下所示运行 ABC1和 UPDATE查询。* 我使用的是 PostgreSQL,下面的日志是 PostgreSQL 的查询日志,你可以查看 在 PostgreSQL 上,如何使用“ BEGIN”和“ COMMIT”等事务查询记录 SQL 查询:

enter image description here

现在,我删除 first()来使用 update(),如下所示:

# "store/views.py"


from django.db import transaction
from .models import Person
from django.http import HttpResponse


@transaction.atomic
def test(request):
person = Person.objects.select_for_update().filter(pk=1) # Here
person.update(name="Tom") # Here


return HttpResponse("Test")

然后,当我运行 test视图时,SELECT FOR UPDATE查询不会运行,只有 UPDATE查询会运行,如下所示:

enter image description here

而且,我有 test视图save()select_for_update().get(pk=1)如下所示:

# "store/views.py"


from django.db import transaction
from .models import Person
from django.http import HttpResponse


@transaction.atomic
def test(request):
person = Person.objects.select_for_update().get(pk=1) # Here
person.name = 'Tom'
person.save() # Here


return HttpResponse("Test")

然后,当我运行 test视图时,如下所示运行 ABC1和 UPDATE查询:

enter image description here

现在,我删除 get()来使用 update(),如下所示:

# "store/views.py"


from django.db import transaction
from .models import Person
from django.http import HttpResponse


@transaction.atomic
def test(request):
person = Person.objects.select_for_update() # Here
person.update(name="Tom") # Here


return HttpResponse("Test")

然后,当我运行 test视图时,SELECT FOR UPDATE查询不会运行,只有 UPDATE查询会运行,如下所示:

enter image description here

因此,具有 select_for_update()save()可以运行 SELECT FOR UPDATE查询,而具有 select_for_update()update()不能。

其中一个可能会引起很多麻烦的差异是,save更新,但是 update不更新类型为 DateTimeField(auto_now=True)的列 或 ModificationDateTimeField 当一个对象被保存到数据库时,这些字段自动(应该)设置它们的日期。