Commit 04e13c89 authored by ZachLiuGIS's avatar ZachLiuGIS Committed by Tim Graham
Browse files

Fixed #26179 -- Removed null assignment check for non-nullable foreign key fields.

parent 353aecbf
Loading
Loading
Loading
Loading
+10 −25
Original line number Diff line number Diff line
@@ -193,14 +193,8 @@ class ForwardManyToOneDescriptor(object):
        - ``instance`` is the ``child`` instance
        - ``value`` in the ``parent`` instance on the right of the equal sign
        """
        # If null=True, we can assign null here, but otherwise the value needs
        # to be an instance of the related class.
        if value is None and self.field.null is False:
            raise ValueError(
                'Cannot assign None: "%s.%s" does not allow null values.' %
                (instance._meta.object_name, self.field.name)
            )
        elif value is not None and not isinstance(value, self.field.remote_field.model._meta.concrete_model):
        # An object must be an instance of the related class.
        if value is not None and not isinstance(value, self.field.remote_field.model._meta.concrete_model):
            raise ValueError(
                'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % (
                    value,
@@ -379,10 +373,7 @@ class ReverseOneToOneDescriptor(object):
        # ForwardManyToOneDescriptor is annoying, but there's a bunch
        # of small differences that would make a common base class convoluted.

        # If null=True, we can assign null here, but otherwise the value needs
        # to be an instance of the related class.
        if value is None:
            if self.related.field.null:
            # Update the cached related instance (if any) & clear the cache.
            try:
                rel_obj = getattr(instance, self.cache_name)
@@ -391,14 +382,8 @@ class ReverseOneToOneDescriptor(object):
            else:
                delattr(instance, self.cache_name)
                setattr(rel_obj, self.related.field.name, None)
            else:
                raise ValueError(
                    'Cannot assign None: "%s.%s" does not allow null values.' % (
                        instance._meta.object_name,
                        self.related.get_accessor_name(),
                    )
                )
        elif not isinstance(value, self.related.related_model):
            # An object must be an instance of the related class.
            raise ValueError(
                'Cannot assign "%r": "%s.%s" must be a "%s" instance.' % (
                    value,
+8 −0
Original line number Diff line number Diff line
@@ -479,6 +479,14 @@ creates the possibility of a deadlock. To adapt your code in the case of RQ,
you can `provide your own worker script <http://python-rq.org/docs/workers/>`_
that calls ``django.setup()``.

Removed null assignment check for non-null foreign key fields
-------------------------------------------------------------

In older versions, assigning ``None`` to a non-nullable ``ForeignKey`` or
``OneToOneField`` raised ``ValueError('Cannot assign None: "model.field" does
not allow null values.')``. For consistency with other model fields which don't
have a similar check, this check is removed.

Miscellaneous
-------------

+3 −6
Original line number Diff line number Diff line
@@ -717,14 +717,11 @@ class ProxyRelatedModelTest(TestCase):


class TestInitWithNoneArgument(SimpleTestCase):
    def test_none_not_allowed(self):
        # TaggedItem requires a content_type, initializing with None should
        # raise a ValueError.
        msg = 'Cannot assign None: "TaggedItem.content_type" does not allow null values'
        with self.assertRaisesMessage(ValueError, msg):
            TaggedItem(content_object=None)

    def test_none_allowed(self):
        # AllowsNullGFK doesn't require a content_type, so None argument should
        # also be allowed.
        AllowsNullGFK(content_object=None)
        # TaggedItem requires a content_type but initializing with None should
        # be allowed.
        TaggedItem(content_object=None)
+9 −8
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ from copy import deepcopy

from django.core.exceptions import FieldError, MultipleObjectsReturned
from django.db import models, transaction
from django.db.utils import IntegrityError
from django.test import TestCase
from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning
@@ -486,19 +487,19 @@ class ManyToOneTests(TestCase):
        p = Parent.objects.get(name="Parent")
        self.assertIsNone(p.bestchild)

        # Assigning None fails: Child.parent is null=False.
        with self.assertRaises(ValueError):
        # Assigning None will not fail: Child.parent is null=False.
        setattr(c, "parent", None)

        # You also can't assign an object of the wrong type here
        with self.assertRaises(ValueError):
            setattr(c, "parent", First(id=1, second=1))

        # Nor can you explicitly assign None to Child.parent during object
        # creation (regression for #9649).
        with self.assertRaises(ValueError):
        # You can assign None to Child.parent during object creation.
        Child(name='xyzzy', parent=None)
        with self.assertRaises(ValueError):

        # But when trying to save a Child with parent=None, the database will
        # raise IntegrityError.
        with self.assertRaises(IntegrityError), transaction.atomic():
            Child.objects.create(name='xyzzy', parent=None)

        # Creation using keyword argument should cache the related object.
+2 −3
Original line number Diff line number Diff line
@@ -228,8 +228,7 @@ class OneToOneTests(TestCase):
        ug_bar.place = None
        self.assertIsNone(ug_bar.place)

        # Assigning None fails: Place.restaurant is null=False
        with self.assertRaises(ValueError):
        # Assigning None will not fail: Place.restaurant is null=False
        setattr(p, 'restaurant', None)

        # You also can't assign an object of the wrong type here