简体   繁体   English

Django 3 - Model.save() 为主键提供默认值时

[英]Django 3 - Model.save() when providing a default for the primary key

I'm working on upgrading my project from Django 2 to Django 3, I have read their release notes of Django 3 and there is a point that I don't really understand what it will impact to my current project.我正在努力将我的项目从 Django 2 升级到 Django 3,我已经阅读了他们的 Django 3 的发行说明,并且我确实了解我目前项目不会产生什么影响。 Here they say:他们在这里说:

在此处输入图像描述

As I understand, if we try to call Model.save() , it would always create a new record instead of updating if the model is an existing record.据我了解,如果我们尝试调用Model.save() ,如果 model 是现有记录,它总是会创建一个新记录而不是更新。 For example:例如:

car = Car.objects.first()
car.name = 'Honda'
car.save() # does it INSERT or UPDATE? I suspect it is an "INSERT" statement as their explanation and "UPDATE" statement in Django 2.

I have had an experimented and it is still the same behaviour as Django 2, not sure what they mean.我已经进行了实验,它仍然与 Django 2 的行为相同,不确定它们的含义。

In [5]: u = User.objects.first()                                                                                                                                                                          
(0.001) SELECT "accounts_user"."id", "accounts_user"."password", "accounts_user"."last_login", "accounts_user"."is_superuser", "accounts_user"."username", "accounts_user"."first_name", "accounts_user"."last_name", "accounts_user"."is_staff", "accounts_user"."is_active", "accounts_user"."date_joined", "accounts_user"."email", "accounts_user"."avatar", "accounts_user"."last_location"::bytea, "accounts_user"."uuid", "accounts_user"."country", "accounts_user"."city", "accounts_user"."phone" FROM "accounts_user" ORDER BY "accounts_user"."id" ASC LIMIT 1; args=()

In [6]: u.save()                                                                                                                                                                                          
(0.006) UPDATE "accounts_user" SET "password" = 'pbkdf2_sha256_sha512$180000$FbFcNuPMrOZ6$GwIftEo+7+OpsORwn99lycye46aJn/aJNAtc50N478Y=', "last_login" = NULL, "is_superuser" = false, "username" = 'email0@mail.com', "first_name" = 'Noah', "last_name" = 'Spencer', "is_staff" = false, "is_active" = true, "date_joined" = '2020-05-12T07:06:20.605650+00:00'::timestamptz, "email" = 'email0@mail.com', "avatar" = 'account/user_avatar/example_HseJquC.jpg', "last_location" = NULL, "uuid" = 'f6992866-e476-409e-9f1b-098afadce5b7'::uuid, "country" = NULL, "city" = NULL, "phone" = NULL WHERE "accounts_user"."id" = 1; args=('pbkdf2_sha256_sha512$180000$FbFcNuPMrOZ6$GwIftEo+7+OpsORwn99lycye46aJn/aJNAtc50N478Y=', False, 'email0@mail.com', 'Noah', 'Spencer', False, True, datetime.datetime(2020, 5, 12, 7, 6, 20, 605650, tzinfo=<UTC>), 'email0@mail.com', 'account/user_avatar/example_HseJquC.jpg', UUID('f6992866-e476-409e-9f1b-098afadce5b7'), 1)

Update:更新:

In [38]: u1 = User.objects.first()                                                                                                                                                                        
(0.000) SELECT "accounts_user"."id", "accounts_user"."password", "accounts_user"."last_login", "accounts_user"."is_superuser", "accounts_user"."username", "accounts_user"."first_name", "accounts_user"."last_name", "accounts_user"."is_staff", "accounts_user"."is_active", "accounts_user"."date_joined", "accounts_user"."email", "accounts_user"."avatar", "accounts_user"."last_location"::bytea, "accounts_user"."uuid", "accounts_user"."country", "accounts_user"."city", "accounts_user"."phone" FROM "accounts_user" ORDER BY "accounts_user"."id" ASC LIMIT 1; args=()

In [39]: u1.pk                                                                                                                                                                                            
Out[39]: 1

In [40]: u2 = User(pk=1)                                                                                                                                                                                  

In [41]: u2.email = 'email@email.com'                                                                                                                                                                     

In [42]: u2.save()                                                                                                                                                                                        
(0.006) UPDATE "accounts_user" SET "password" = '', "last_login" = NULL, "is_superuser" = false, "username" = 'email@email.com', "first_name" = '', "last_name" = '', "is_staff" = false, "is_active" = true, "date_joined" = '2020-05-13T01:20:47.718449+00:00'::timestamptz, "email" = 'email@email.com', "avatar" = '', "last_location" = NULL, "uuid" = '89ba0924-03a7-44d2-bc6d-5fd2dcb0de0b'::uuid, "country" = NULL, "city" = NULL, "phone" = NULL WHERE "accounts_user"."id" = 1; args=('', False, 'email@email.com', '', '', False, True, datetime.datetime(2020, 5, 13, 1, 20, 47, 718449, tzinfo=<UTC>), 'email@email.com', '', UUID('89ba0924-03a7-44d2-bc6d-5fd2dcb0de0b'), 1)

Consider this example.考虑这个例子。 Suppose we have a simple model as假设我们有一个简单的 model 为

CONSTANT = 10


def foo_pk_default():
    return CONSTANT


class Foo(models.Model):
    id = models.IntegerField(primary_key=True, default=foo_pk_default)
    name = models.CharField(max_length=10)

The main thing I have done in this example is, I did set a default callable function for Primary Keys .我在这个例子中所做的主要事情是,我确实为 Primary Keys 设置了一个默认的可调用 function Also, I returned only a single value from the function, for the sake of demonstration.此外,为了演示,我只从 function 返回了一个值。

## Django 2.2
In [5]: foo_instance_1 = Foo(name='foo_name_1')

In [6]: foo_instance_1.save()

In [7]: print(foo_instance_1.__dict__)
{'_state': , 'id': 10, 'name': 'foo_name_1'}

In [8]: foo_instance_2 = Foo(name='foo_name_2')

In [9]: foo_instance_2.save()

In [10]: print(foo_instance_2.__dict__)
{'_state': , 'id': 10, 'name': 'foo_name_2'}

## Django 3.X
In [6]: foo_instance_1 = Foo(name='foo_name_1')

In [7]: foo_instance_1.save()

In [8]: print(foo_instance_1.__dict__)
{'_state': , 'id': 10, 'name': 'foo_name_1'}

In [9]: foo_instance_2 = Foo(name='foo_name_2')

In [10]: foo_instance_2.save()
# Raised "IntegrityError: UNIQUE constraint failed: music_foo.id"

Conclusion结论

In Django<3.0 , the Model.save() will do an update or insert operation if there is a PK value associated with the model instance whereas in Django>=3.0 , only perform an insert operation hence the UNIQUE constraint failed exception.Django<3.0中,如果存在与 model 实例关联的 PK 值,则Model.save()将执行更新或插入操作,而在Django>=3.0中,仅执行插入操作,因此UNIQUE constraint failed异常。

Impact of this change in the current project此更改对当前项目的影响

Since this Django change is only applicable when a new instance is created and we usually don't set any default value functions for Primary Keys.由于此 Django 更改仅适用于创建新实例时,我们通常不会为主键设置任何默认值函数。

In short, this change will not make any problem unless you are providing default value during model instance creation.简而言之,除非您在 model 实例创建期间提供默认值,否则此更改不会产生任何问题。

It should be read carefully应该仔细阅读

no longer attempts to find a row when saving a new Model instance and a default value for the primary key is provided ,保存新的 Model 实例提供主键的默认值时不再尝试查找行,

So in case you are going to create new object with id that is already in database it would now fail instead of having behavior similar to update_or_create as it will now preform INSERT statement instead of UPDATE因此,如果您要使用已经在数据库中的 id创建新的 object ,它现在将失败,而不是具有类似于 update_or_create 的行为,因为它现在将INSERT语句而不是UPDATE

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM