Opened 2 months ago

Closed 2 months ago

Last modified 2 months ago

#28210 closed Bug (fixed)

`<orm_object>._state.adding` behaviour and model inheritance

Reported by: Ivaylo Donchev Owned by: nobody
Component: Database layer (models, ORM) Version: 1.11
Severity: Release blocker Keywords:
Cc: Simon Charette Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Tim Graham)

Let's say we have the following models:

class User(models.Model):
    email = models.CharField(max_length=255, unique=True)
    password = models.CharField(max_length=32)

class Admin(User):
    pass

class ThirdPartyUser(User):
    pass

Steps to reproduce:

  1. admin = Admin.objects.create(email='admin@admin.com')

This will create an Admin instance and User instance, with admin.user_ptr._state.adding == True

  1. ThirdPartyUser.objects.create(user_ptr = admin.user_ptr)

This will raise the following error {'email': ['User with this Email address already exists.'], 'id': ['User with this ID already exists.'] }
When the admin is created, the admin.user_ptr._state.adding is True, but admin._state.adding is False (as it is already created).
If we set admin.user_ptr._state.adding = False , step 2 is passing without errors.

Change History (11)

comment:1 Changed 2 months ago by Simon Charette

This looks similar to #28166 which has been fixed in 1.11.1. Can you confirm it's the case?

comment:2 Changed 2 months ago by Ivaylo Donchev

Yeah, but it's not about the _state.db.Basically, if we have these two models:

    class Parent(models.Model):
        pass
    class ChildA(Parent):
        pass
    class ChildB(Parent):
        pass

and we create "ChildA" object with obj = ChildA.objects.create(), we'll have 2 new instances - *(ChildA) obj* and *(Parent) obj.parent_ptr)*. Now, if you try to create "ChildB" instance as follows:

    parent = obj.parent_ptr  # parent._state.adding == True
    child_b = ChildB(parent_ptr=obj.parent_ptr)
    child_b.__dict__.update(parent)  # to set all required attributes (if any)
    child_b.full_clean()  # this will throw an error for all of the unique fields of the parent instance ("id"for example) 
    child_b.save()

So, if you set obj.parent_ptr._state.adding = False as soon as obj is created, the code above is working without errors.
Basically, the problem is that if we don't modify the _state's adding, it's no possible to create a new instance with the same parent_ptr.

Last edited 2 months ago by Ivaylo Donchev (previous) (diff)

comment:3 Changed 2 months ago by Simon Charette

Component: UncategorizedDatabase layer (models, ORM)
Triage Stage: UnreviewedAccepted
Type: UncategorizedBug

Could you confirm whether or not it's a regression from 1.10? It could be related to 38575b007a722d6af510ea46d46393a4cda9ca29 as well.

comment:4 in reply to:  3 Changed 2 months ago by Ivaylo Donchev

Well, I hit this bug while upgrading Django from 1.10 to 1.11.1, but I didn't reproduce all the corner cases in the Django1.10.
Replying to Simon Charette:

Could you confirm whether or not it's a regression from 1.10? It could be related to 38575b007a722d6af510ea46d46393a4cda9ca29 as well.

comment:5 Changed 2 months ago by Simon Charette

Cc: Simon Charette added
Severity: NormalRelease blocker

Tentatively escalating as a release blocker as everything seems to indicate this is a regression.

comment:6 Changed 2 months ago by Tim Graham

Description: modified (diff)

I can't reproduce using the steps described in the description. How does ThirdPartyUser.objects.create() raise a dictionary? Please clarify.

comment:7 Changed 2 months ago by Simon Charette

Tim, I think the reporter was referring to form validation generating a form.errors along the dict he provided.

Unique field validation relies on the _state.adding flag to determine whether or not it should run.

An appropriate test in this case would be along the lines of

child = Child.objects.create()
self.assertFalse(child.parent_ptr._state.adding)

And the actual patch should be similar to f3217ab59696ea095a42c7fb4d98f21bb000ca8e but with _state.adding instead of _state.db.

Last edited 2 months ago by Simon Charette (previous) (diff)

comment:8 Changed 2 months ago by Tim Graham

Has patch: set

comment:9 Changed 2 months ago by Simon Charette

Triage Stage: AcceptedReady for checkin

comment:10 Changed 2 months ago by Tim Graham <timograham@…>

Resolution: fixed
Status: newclosed

In 59ab1b26:

Fixed #28210 -- Fixed Model._state.adding on MTI parent model after saving child model.

Regression in 38575b007a722d6af510ea46d46393a4cda9ca29.

comment:11 Changed 2 months ago by Tim Graham <timograham@…>

In f2b8fa17:

[1.11.x] Fixed #28210 -- Fixed Model._state.adding on MTI parent model after saving child model.

Regression in 38575b007a722d6af510ea46d46393a4cda9ca29.

Backport of 59ab1b2683b6c090dc409d9eb8303aadbd590c04 from master

Note: See TracTickets for help on using tickets.
Back to Top