mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Refs #28147 -- Fixed setting of OneToOne and Foreign Key fields to None when using attnames.
Regression in 519016e5f2
.
This commit is contained in:
parent
619c9a4f49
commit
4122d9d3f1
@ -19,9 +19,9 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from . import Field
|
from . import Field
|
||||||
from .mixins import FieldCacheMixin
|
from .mixins import FieldCacheMixin
|
||||||
from .related_descriptors import (
|
from .related_descriptors import (
|
||||||
ForwardManyToOneDescriptor, ForwardOneToOneDescriptor,
|
ForeignKeyDeferredAttribute, ForwardManyToOneDescriptor,
|
||||||
ManyToManyDescriptor, ReverseManyToOneDescriptor,
|
ForwardOneToOneDescriptor, ManyToManyDescriptor,
|
||||||
ReverseOneToOneDescriptor,
|
ReverseManyToOneDescriptor, ReverseOneToOneDescriptor,
|
||||||
)
|
)
|
||||||
from .related_lookups import (
|
from .related_lookups import (
|
||||||
RelatedExact, RelatedGreaterThan, RelatedGreaterThanOrEqual, RelatedIn,
|
RelatedExact, RelatedGreaterThan, RelatedGreaterThanOrEqual, RelatedIn,
|
||||||
@ -764,7 +764,7 @@ class ForeignKey(ForeignObject):
|
|||||||
By default ForeignKey will target the pk of the remote model but this
|
By default ForeignKey will target the pk of the remote model but this
|
||||||
behavior can be changed by using the ``to_field`` argument.
|
behavior can be changed by using the ``to_field`` argument.
|
||||||
"""
|
"""
|
||||||
|
descriptor_class = ForeignKeyDeferredAttribute
|
||||||
# Field flags
|
# Field flags
|
||||||
many_to_many = False
|
many_to_many = False
|
||||||
many_to_one = True
|
many_to_one = True
|
||||||
|
@ -67,9 +67,17 @@ from django.core.exceptions import FieldError
|
|||||||
from django.db import connections, router, transaction
|
from django.db import connections, router, transaction
|
||||||
from django.db.models import Q, signals
|
from django.db.models import Q, signals
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
from django.db.models.query_utils import DeferredAttribute
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
|
||||||
|
class ForeignKeyDeferredAttribute(DeferredAttribute):
|
||||||
|
def __set__(self, instance, value):
|
||||||
|
if instance.__dict__.get(self.field.attname) != value and self.field.is_cached(instance):
|
||||||
|
self.field.delete_cached_value(instance)
|
||||||
|
instance.__dict__[self.field.attname] = value
|
||||||
|
|
||||||
|
|
||||||
class ForwardManyToOneDescriptor:
|
class ForwardManyToOneDescriptor:
|
||||||
"""
|
"""
|
||||||
Accessor to the related object on the forward side of a many-to-one or
|
Accessor to the related object on the forward side of a many-to-one or
|
||||||
|
@ -490,6 +490,10 @@ Miscellaneous
|
|||||||
* ``intword`` template filter now translates ``1.0`` as a singular phrase and
|
* ``intword`` template filter now translates ``1.0`` as a singular phrase and
|
||||||
all other numeric values as plural. This may be incorrect for some languages.
|
all other numeric values as plural. This may be incorrect for some languages.
|
||||||
|
|
||||||
|
* Assigning a value to a model's :class:`~django.db.models.ForeignKey` or
|
||||||
|
:class:`~django.db.models.OneToOneField` ``'_id'`` attribute now unsets the
|
||||||
|
corresponding field. Accessing the field afterwards will result in a query.
|
||||||
|
|
||||||
.. _deprecated-features-3.0:
|
.. _deprecated-features-3.0:
|
||||||
|
|
||||||
Features deprecated in 3.0
|
Features deprecated in 3.0
|
||||||
|
@ -168,6 +168,18 @@ class ManyToOneTests(TestCase):
|
|||||||
parent.bestchild_id = child2.pk
|
parent.bestchild_id = child2.pk
|
||||||
self.assertTrue(Parent.bestchild.is_cached(parent))
|
self.assertTrue(Parent.bestchild.is_cached(parent))
|
||||||
|
|
||||||
|
def test_assign_fk_id_none(self):
|
||||||
|
parent = Parent.objects.create(name='jeff')
|
||||||
|
child = Child.objects.create(name='frank', parent=parent)
|
||||||
|
parent.bestchild = child
|
||||||
|
parent.save()
|
||||||
|
parent.bestchild_id = None
|
||||||
|
parent.save()
|
||||||
|
self.assertIsNone(parent.bestchild_id)
|
||||||
|
self.assertFalse(Parent.bestchild.is_cached(parent))
|
||||||
|
self.assertIsNone(parent.bestchild)
|
||||||
|
self.assertTrue(Parent.bestchild.is_cached(parent))
|
||||||
|
|
||||||
def test_selects(self):
|
def test_selects(self):
|
||||||
self.r.article_set.create(headline="John's second story", pub_date=datetime.date(2005, 7, 29))
|
self.r.article_set.create(headline="John's second story", pub_date=datetime.date(2005, 7, 29))
|
||||||
self.r2.article_set.create(headline="Paul's story", pub_date=datetime.date(2006, 1, 17))
|
self.r2.article_set.create(headline="Paul's story", pub_date=datetime.date(2006, 1, 17))
|
||||||
|
@ -217,6 +217,15 @@ class OneToOneTests(TestCase):
|
|||||||
b.place_id = self.p2.pk
|
b.place_id = self.p2.pk
|
||||||
self.assertTrue(UndergroundBar.place.is_cached(b))
|
self.assertTrue(UndergroundBar.place.is_cached(b))
|
||||||
|
|
||||||
|
def test_assign_o2o_id_none(self):
|
||||||
|
b = UndergroundBar.objects.create(place=self.p1)
|
||||||
|
b.place_id = None
|
||||||
|
b.save()
|
||||||
|
self.assertIsNone(b.place_id)
|
||||||
|
self.assertFalse(UndergroundBar.place.is_cached(b))
|
||||||
|
self.assertIsNone(b.place)
|
||||||
|
self.assertTrue(UndergroundBar.place.is_cached(b))
|
||||||
|
|
||||||
def test_related_object_cache(self):
|
def test_related_object_cache(self):
|
||||||
""" Regression test for #6886 (the related-object cache) """
|
""" Regression test for #6886 (the related-object cache) """
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user