Fixed #25160 -- Moved unsaved model instance data loss check to Model.save()

This mostly reverts 5643a3b51b and
81e1a35c36.

Thanks Carl Meyer for review.
This commit is contained in:
Tim Graham 2015-07-24 07:51:40 -04:00
parent 12f91f6ebd
commit 5980b05c1f
15 changed files with 106 additions and 198 deletions

View file

@ -299,13 +299,6 @@ 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

View file

@ -1383,30 +1383,6 @@ 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, on_delete=models.CASCADE)
.. _ref-manytomany:
``ManyToManyField``
@ -1607,12 +1583,6 @@ 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

View file

@ -27,3 +27,7 @@ Bugfixes
* Fixed the recording of squashed migrations when running the ``migrate``
command (:ticket:`25231`).
* Moved the :ref:`unsaved model instance assignment data loss check
<unsaved-model-instance-check-18>` to ``Model.save()`` to allow easier usage
of in-memory models (:ticket:`25160`).

View file

@ -685,9 +685,23 @@ This has one backwards incompatible side effect, signal handlers triggered from
these methods are now executed within the method's transaction and any
exception in a signal handler will prevent the whole operation.
.. _unsaved-model-instance-check-18:
Assigning unsaved objects to relations raises an error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
To more easily allow in-memory usage of models, this change was reverted in
Django 1.8.4 and replaced with a check during ``model.save()``. For example::
>>> book = Book.objects.create(name="Django")
>>> book.author = Author(name="John")
>>> book.save()
Traceback (most recent call last):
...
ValueError: save() prohibited to prevent data loss due to unsaved related object 'author'.
Assigning unsaved objects to a :class:`~django.db.models.ForeignKey`,
:class:`~django.contrib.contenttypes.fields.GenericForeignKey`, and
:class:`~django.db.models.OneToOneField` now raises a :exc:`ValueError`.
@ -714,8 +728,8 @@ Now, an error will be raised to prevent data loss::
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.
``ForeignKey.allow_unsaved_instance_assignment`` attribute. (This attribute was
removed in 1.8.4 as it's no longer relevant.)
Management commands that only accept positional arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -55,19 +55,17 @@ relationship. For example, creating an ``Article`` with unsaved ``Reporter``
raises ``ValueError``::
>>> r3 = Reporter(first_name='John', last_name='Smith', email='john@example.com')
>>> Article(headline="This is a test", pub_date=date(2005, 7, 27), reporter=r3)
>>> Article.objects.create(headline="This is a test", pub_date=date(2005, 7, 27), reporter=r3)
Traceback (most recent call last):
...
ValueError: 'Cannot assign "<Reporter: John Smith>": "Reporter" instance isn't saved in the database.'
ValueError: save() prohibited to prevent data loss due to unsaved related object 'reporter'.
If you want to disable the unsaved instance check, you can use the
:attr:`~django.db.models.ForeignKey.allow_unsaved_instance_assignment`
attribute.
.. versionchanged:: 1.8.4
.. versionchanged:: 1.8
Previously, assigning unsaved objects did not raise an error and could
result in silent data loss.
Previously, saving an object with unsaved related objects did not raise an
error and could result in silent data loss. In 1.8-1.8.3, unsaved model
instances couldn't be assigned to related fields, but this restriction was
removed to allow easier usage of in-memory models.
Article objects have access to their related Reporter objects::

View file

@ -96,23 +96,17 @@ relationship. For example, creating an ``Restaurant`` with unsaved ``Place``
raises ``ValueError``::
>>> p3 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> Restaurant(place=p3, serves_hot_dogs=True, serves_pizza=False)
>>> Restaurant.objects.create(place=p3, serves_hot_dogs=True, serves_pizza=False)
Traceback (most recent call last):
...
ValueError: 'Cannot assign "<Place: Demon Dogs>": "Place" instance isn't saved in the database.'
>>> p.restaurant = Restaurant(place=p, serves_hot_dogs=True, serves_pizza=False)
Traceback (most recent call last):
...
ValueError: 'Cannot assign "<Restaurant: Demon Dogs the restaurant>": "Restaurant" instance isn't saved in the database.'
ValueError: save() prohibited to prevent data loss due to unsaved related object 'place'.
If you want to disable the unsaved instance check, you can use the
:attr:`~django.db.models.ForeignKey.allow_unsaved_instance_assignment`
attribute.
.. versionchanged:: 1.8.4
.. versionchanged:: 1.8
Previously, assigning unsaved objects did not raise an error and could
result in silent data loss.
Previously, saving an object with unsaved related objects did not raise an
error and could result in silent data loss. In 1.8-1.8.3, unsaved model
instances couldn't be assigned to related fields, but this restriction was
removed to allow easier usage of in-memory models.
Restaurant.objects.all() just returns the Restaurants, not the Places. Note
that there are two restaurants - Ace Hardware the Restaurant was created in the