At the bottom of the post there is a testcase that succeeds on 1.11.x and
fails on 2.x
I think the commit that changed the behavior is
bfb746f983aa741afa3709794e70f1e0ab6040b5
So my question is two fold:
* Is the behavior in >=2.0 correct? It seems quite unexpected.
* What is the recommended way to clone a model instance? To date we have
been using copy() in a similar fashion to the test without issue. deepcopy
seems to work fine in >=2.0 but we haven’t done too much testing yet.
Test (placed in tests/model_fields/test_field_caching_change.py):
{{{#!python
import copy
from django.test import TestCase
from .models import Bar, Foo
class ForeignKeyCachingBehaviorTest(TestCase):
def test_copy(self):
foo1 = Foo.objects.create(a='foo1', d=1)
foo2 = Foo.objects.create(a='foo2', d=2)
bar1 = Bar.objects.create(a=foo1, b='bar1')
bar2 = copy.copy(bar1)
bar2.pk = None
bar2.a = foo2
# bar2 points to foo2
self.assertEqual(bar2.a, foo2)
self.assertEqual(bar2.a.id, bar2.a_id)
# bar1 is unchanged and must still point to foo1
# These fail on Django >= 2.0
self.assertEqual(bar1.a, foo1)
self.assertEqual(bar1.a.id, bar1.a_id)
}}}
and executed that via:
python3.6 tests/runtests.py --parallel 1 model_fields
In https://groups.google.com/g/django-
developers/c/QMhVPIqVVP4/m/mbezfaBEAwAJ Simon suggests:
> ..... Model.__copy__ should make sure to make a deep-copy of self._state
now that fields are cached in self._state.fields_cache.
which I will attempt to implement.
--
Ticket URL: <https://code.djangoproject.com/ticket/31863>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* stage: Unreviewed => Accepted
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:1>
* has_patch: 0 => 1
Comment:
Can someone please double check the fields on the ticket and PR, not sure
if I got them right.
https://github.com/django/django/pull/13281/
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:2>
* needs_better_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:3>
* needs_better_patch: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:4>
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:5>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"94ea79be137f3cb30949bf82198e96e094f2650d" 94ea79be]:
{{{
#!CommitTicketReference repository=""
revision="94ea79be137f3cb30949bf82198e96e094f2650d"
Fixed #31863 -- Prevented mutating model state by copies of model
instances.
Regression in bfb746f983aa741afa3709794e70f1e0ab6040b5.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:6>
* version: 3.1 => 2.2
Comment:
This qualifies for a backport as a data loss bug.
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:7>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"85c47b9a734365a84e56470d96fbe0fdb3b7a8b3" 85c47b9a]:
{{{
#!CommitTicketReference repository=""
revision="85c47b9a734365a84e56470d96fbe0fdb3b7a8b3"
[3.1.x] Fixed #31863 -- Prevented mutating model state by copies of model
instances.
Regression in bfb746f983aa741afa3709794e70f1e0ab6040b5.
Backport of 94ea79be137f3cb30949bf82198e96e094f2650d from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:8>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"c33b6ceff4c3462dec8555e395c6f45faa61c490" c33b6ce]:
{{{
#!CommitTicketReference repository=""
revision="c33b6ceff4c3462dec8555e395c6f45faa61c490"
[3.0.x] Fixed #31863 -- Prevented mutating model state by copies of model
instances.
Regression in bfb746f983aa741afa3709794e70f1e0ab6040b5.
Backport of 94ea79be137f3cb30949bf82198e96e094f2650d from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:9>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"0a7d321bf7e126612a43b8aade81cdef1c7dd308" 0a7d321b]:
{{{
#!CommitTicketReference repository=""
revision="0a7d321bf7e126612a43b8aade81cdef1c7dd308"
[2.2.x] Fixed #31863 -- Prevented mutating model state by copies of model
instances.
Regression in bfb746f983aa741afa3709794e70f1e0ab6040b5.
Backport of 94ea79be137f3cb30949bf82198e96e094f2650d from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:10>
Comment (by GitHub <noreply@…>):
In [changeset:"21768a99f47ee73a2f93405151550ef7c3d9c8a2" 21768a99]:
{{{
#!CommitTicketReference repository=""
revision="21768a99f47ee73a2f93405151550ef7c3d9c8a2"
Refs #31863 -- Added release notes for
94ea79be137f3cb30949bf82198e96e094f2650d.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:11>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"18e87ac85f450af8fe90a8bd01d6a92cc7b87ce4" 18e87ac]:
{{{
#!CommitTicketReference repository=""
revision="18e87ac85f450af8fe90a8bd01d6a92cc7b87ce4"
[3.1.x] Refs #31863 -- Added release notes for
94ea79be137f3cb30949bf82198e96e094f2650d.
Backport of 21768a99f47ee73a2f93405151550ef7c3d9c8a2 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:12>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"ab5491c7cc46f81be9ca388ae3dcda02c30d013d" ab5491c]:
{{{
#!CommitTicketReference repository=""
revision="ab5491c7cc46f81be9ca388ae3dcda02c30d013d"
[3.0.x] Refs #31863 -- Added release notes for
94ea79be137f3cb30949bf82198e96e094f2650d.
Backport of 21768a99f47ee73a2f93405151550ef7c3d9c8a2 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:13>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"dc39e62e6b652f006a77b91df02e1dc801597396" dc39e62e]:
{{{
#!CommitTicketReference repository=""
revision="dc39e62e6b652f006a77b91df02e1dc801597396"
[2.2.x] Refs #31863 -- Added release notes for
94ea79be137f3cb30949bf82198e96e094f2650d.
Backport of 21768a99f47ee73a2f93405151550ef7c3d9c8a2 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/31863#comment:14>