Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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. Here they say:

enter image description here

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. 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.

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" = '[email protected]', "first_name" = 'Noah', "last_name" = 'Spencer', "is_staff" = false, "is_active" = true, "date_joined" = '2020-05-12T07:06:20.605650+00:00'::timestamptz, "email" = '[email protected]', "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, '[email protected]', 'Noah', 'Spencer', False, True, datetime.datetime(2020, 5, 12, 7, 6, 20, 605650, tzinfo=<UTC>), '[email protected]', '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 protected]'                                                                                                                                                                     

In [42]: u2.save()                                                                                                                                                                                        
(0.006) UPDATE "accounts_user" SET "password" = '', "last_login" = NULL, "is_superuser" = false, "username" = '[email protected]', "first_name" = '', "last_name" = '', "is_staff" = false, "is_active" = true, "date_joined" = '2020-05-13T01:20:47.718449+00:00'::timestamptz, "email" = '[email protected]', "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 protected]', '', '', False, True, datetime.datetime(2020, 5, 13, 1, 20, 47, 718449, tzinfo=<UTC>), '[email protected]', '', UUID('89ba0924-03a7-44d2-bc6d-5fd2dcb0de0b'), 1)

like image 251
Thanh Nguyen Avatar asked May 12 '20 14:05

Thanh Nguyen


People also ask

What is Save () in Django?

save() , django will save the current object state to record. So if some changes happens between get() and save() by some other process, then those changes will be lost.

What is the default primary key in Django?

If you don't specify primary_key=True for any fields in your model, Django will automatically add an IntegerField to hold the primary key, so you don't need to set primary_key=True on any of your fields unless you want to override the default primary-key behavior. For more, see Automatic primary key fields.

How does Django save data?

Creating objects To create an object, instantiate it using keyword arguments to the model class, then call save() to save it to the database. This performs an INSERT SQL statement behind the scenes. Django doesn't hit the database until you explicitly call save() . The save() method has no return value.

Does Django create save?

create() will automatically save, so even if you fix your error - you will still have to make sure the arguments to create fulfill the database requirements to save a record.


1 Answers

Consider this example. Suppose we have a simple model as

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. Also, I returned only a single value from the function, for the sake of demonstration.

## 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.

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.

In short, this change will not make any problem unless you are providing default value during model instance creation.

like image 133
JPG Avatar answered Oct 31 '22 19:10

JPG