Commit 81e1a35c authored by Karl Hobley's avatar Karl Hobley Committed by Tim Graham
Browse files

Fixed #24495 -- Allowed unsaved model instance assignment check to be bypassed.

parent 02d78bb1
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ class GenericForeignKey(object):
    one_to_one = False
    related_model = None

    allow_unsaved_instance_assignment = False

    def __init__(self, ct_field='content_type', fk_field='object_id', for_concrete_model=True):
        self.ct_field = ct_field
        self.fk_field = fk_field
@@ -250,7 +252,7 @@ class GenericForeignKey(object):
        if value is not None:
            ct = self.get_content_type(obj=value)
            fk = value._get_pk_val()
            if fk is None:
            if not self.allow_unsaved_instance_assignment and fk is None:
                raise ValueError(
                    'Cannot assign "%r": "%s" instance isn\'t saved in the database.' %
                    (value, value._meta.object_name)
+3 −2
Original line number Diff line number Diff line
@@ -513,7 +513,7 @@ class SingleRelatedObjectDescriptor(object):
                    raise ValueError('Cannot assign "%r": the current database router prevents this relation.' % value)

        related_pk = tuple(getattr(instance, field.attname) for field in self.related.field.foreign_related_fields)
        if None in related_pk:
        if not self.related.field.allow_unsaved_instance_assignment and None in related_pk:
            raise ValueError(
                'Cannot assign "%r": "%s" instance isn\'t saved in the database.' %
                (value, instance._meta.object_name)
@@ -684,7 +684,7 @@ class ReverseSingleRelatedObjectDescriptor(object):
        else:
            for lh_field, rh_field in self.field.related_fields:
                pk = value._get_pk_val()
                if pk is None:
                if not self.field.allow_unsaved_instance_assignment and pk is None:
                    raise ValueError(
                        'Cannot assign "%r": "%s" instance isn\'t saved in the database.' %
                        (value, self.field.rel.to._meta.object_name)
@@ -1534,6 +1534,7 @@ class ForeignObject(RelatedField):
    one_to_many = False
    one_to_one = False

    allow_unsaved_instance_assignment = False
    requires_unique_target = True
    related_accessor_class = ForeignRelatedObjectsDescriptor
    rel_class = ForeignObjectRel
+7 −0
Original line number Diff line number Diff line
@@ -299,6 +299,13 @@ model:
       is ``True``. This mirrors the ``for_concrete_model`` argument to
       :meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model`.

    .. attribute:: GenericForeignKey.allow_unsaved_instance_assignment

        .. versionadded:: 1.8

        Works analogously to :attr:`ForeignKey.allow_unsaved_instance_assignment
        <django.db.models.ForeignKey.allow_unsaved_instance_assignment>`.

.. admonition:: Primary key type compatibility

   The "object_id" field doesn't have to be the same type as the
+31 −1
Original line number Diff line number Diff line
@@ -1291,6 +1291,30 @@ The possible values for :attr:`~ForeignKey.on_delete` are found in

    If in doubt, leave it to its default of ``True``.

.. attribute:: ForeignKey.allow_unsaved_instance_assignment

    .. versionadded:: 1.8

        This flag was added for backwards compatibility as older versions of
        Django always allowed assigning unsaved model instances.

    Django prevents unsaved model instances from being assigned to a
    ``ForeignKey`` field to prevent accidental data loss (unsaved foreign keys
    are silently ignored when saving a model instance).

    If you require allowing the assignment of unsaved instances and aren't
    concerned about the data loss possibility (e.g. you never save the objects
    to the database), you can disable this check by creating a subclass of the
    field class and setting its ``allow_unsaved_instance_assignment`` attribute
    to ``True``. For example::

        class UnsavedForeignKey(models.ForeignKey):
            # A ForeignKey which can point to an unsaved object
            allow_unsaved_instance_assignment = True

        class Book(models.Model):
            author = UnsavedForeignKey(Author)

.. _ref-manytomany:

``ManyToManyField``
@@ -1483,6 +1507,12 @@ that control how the relationship functions.

    If in doubt, leave it to its default of ``True``.

.. attribute:: ManyToManyField.allow_unsaved_instance_assignment

    .. versionadded:: 1.8

    Works analogously to :attr:`ForeignKey.allow_unsaved_instance_assignment`.

:class:`ManyToManyField` does not support :attr:`~Field.validators`.

:attr:`~Field.null` has no effect since there is no way to require a
+6 −0
Original line number Diff line number Diff line
@@ -713,6 +713,12 @@ Now, an error will be raised to prevent data loss::
    ...
    ValueError: Cannot assign "<Author: John>": "Author" instance isn't saved in the database.

If you require allowing the assignment of unsaved instances (the old behavior)
and aren't concerned about the data loss possibility (e.g. you never save the
objects to the database), you can disable this check by using the
:attr:`~django.db.models.ForeignKey.allow_unsaved_instance_assignment`
attribute.

Management commands that only accept positional arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Loading