Fixed #25550 -- Deprecated direct assignment to the reverse side of a related set.
This commit is contained in:
parent
0b5d32faca
commit
9c5e272860
|
@ -201,7 +201,7 @@ class DeserializedObject(object):
|
||||||
models.Model.save_base(self.object, using=using, raw=True, **kwargs)
|
models.Model.save_base(self.object, using=using, raw=True, **kwargs)
|
||||||
if self.m2m_data and save_m2m:
|
if self.m2m_data and save_m2m:
|
||||||
for accessor_name, object_list in self.m2m_data.items():
|
for accessor_name, object_list in self.m2m_data.items():
|
||||||
setattr(self.object, accessor_name, object_list)
|
getattr(self.object, accessor_name).set(object_list)
|
||||||
|
|
||||||
# prevent a second (possibly accidental) call to save() from saving
|
# prevent a second (possibly accidental) call to save() from saving
|
||||||
# the m2m data twice.
|
# the m2m data twice.
|
||||||
|
|
|
@ -1600,7 +1600,7 @@ class ManyToManyField(RelatedField):
|
||||||
return getattr(obj, self.attname).all()
|
return getattr(obj, self.attname).all()
|
||||||
|
|
||||||
def save_form_data(self, instance, data):
|
def save_form_data(self, instance, data):
|
||||||
setattr(instance, self.attname, data)
|
getattr(instance, self.attname).set(data)
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
db = kwargs.pop('using', None)
|
db = kwargs.pop('using', None)
|
||||||
|
|
|
@ -62,11 +62,13 @@ and two directions (forward and reverse) for a total of six combinations.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import warnings
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
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.utils.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
|
||||||
|
@ -477,6 +479,11 @@ class ReverseManyToOneDescriptor(object):
|
||||||
- ``instance`` is the ``parent`` instance
|
- ``instance`` is the ``parent`` instance
|
||||||
- ``value`` in the ``children`` sequence on the right of the equal sign
|
- ``value`` in the ``children`` sequence on the right of the equal sign
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
'Direct assignment to the reverse side of a related set is '
|
||||||
|
'deprecated due to the implicit save() that happens. Use %s.set() '
|
||||||
|
'instead.' % self.rel.get_accessor_name(), RemovedInDjango20Warning, stacklevel=2,
|
||||||
|
)
|
||||||
manager = self.__get__(instance)
|
manager = self.__get__(instance)
|
||||||
manager.set(value)
|
manager.set(value)
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,9 @@ details on these changes.
|
||||||
|
|
||||||
* The ``makemigrations --exit`` option will be removed.
|
* The ``makemigrations --exit`` option will be removed.
|
||||||
|
|
||||||
|
* Support for direct assignment to a reverse foreign key or many-to-many
|
||||||
|
relation will be removed.
|
||||||
|
|
||||||
.. _deprecation-removed-in-1.10:
|
.. _deprecation-removed-in-1.10:
|
||||||
|
|
||||||
1.10
|
1.10
|
||||||
|
|
|
@ -337,7 +337,7 @@ Fields
|
||||||
|
|
||||||
Many-to-many field to :class:`~django.contrib.auth.models.Permission`::
|
Many-to-many field to :class:`~django.contrib.auth.models.Permission`::
|
||||||
|
|
||||||
group.permissions = [permission_list]
|
group.permissions.set([permission_list])
|
||||||
group.permissions.add(permission, permission, ...)
|
group.permissions.add(permission, permission, ...)
|
||||||
group.permissions.remove(permission, permission, ...)
|
group.permissions.remove(permission, permission, ...)
|
||||||
group.permissions.clear()
|
group.permissions.clear()
|
||||||
|
|
|
@ -179,8 +179,6 @@ Related objects reference
|
||||||
<intermediary-manytomany>` for a many-to-many relationship, some of the
|
<intermediary-manytomany>` for a many-to-many relationship, some of the
|
||||||
related manager's methods are disabled.
|
related manager's methods are disabled.
|
||||||
|
|
||||||
.. _direct-assignment:
|
|
||||||
|
|
||||||
Direct Assignment
|
Direct Assignment
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
@ -200,3 +198,12 @@ added to the existing related object set.
|
||||||
In earlier versions, direct assignment used to perform ``clear()`` followed
|
In earlier versions, direct assignment used to perform ``clear()`` followed
|
||||||
by ``add()``. It now performs a ``set()`` with the keyword argument
|
by ``add()``. It now performs a ``set()`` with the keyword argument
|
||||||
``clear=False``.
|
``clear=False``.
|
||||||
|
|
||||||
|
.. deprecated:: 1.10
|
||||||
|
|
||||||
|
Direct assignment is deprecated in favor of the
|
||||||
|
:meth:`~django.db.models.fields.related.RelatedManager.set` method::
|
||||||
|
|
||||||
|
>>> e.related_set.set([obj1, obj2, obj3])
|
||||||
|
|
||||||
|
This prevents confusion about an assignment resulting in an implicit save.
|
||||||
|
|
|
@ -269,6 +269,21 @@ Miscellaneous
|
||||||
Features deprecated in 1.10
|
Features deprecated in 1.10
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
Direct assignment to a reverse foreign key or many-to-many relation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Instead of assigning related objects using direct assignment::
|
||||||
|
|
||||||
|
>>> new_list = [obj1, obj2, obj3]
|
||||||
|
>>> e.related_set = new_list
|
||||||
|
|
||||||
|
Use the :meth:`~django.db.models.fields.related.RelatedManager.set` method
|
||||||
|
added in Django 1.9::
|
||||||
|
|
||||||
|
>>> e.related_set.set([obj1, obj2, obj3])
|
||||||
|
|
||||||
|
This prevents confusion about an assignment resulting in an implicit save.
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -673,13 +673,12 @@ Related object operations are run in a transaction
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Some operations on related objects such as
|
Some operations on related objects such as
|
||||||
:meth:`~django.db.models.fields.related.RelatedManager.add()` or
|
:meth:`~django.db.models.fields.related.RelatedManager.add()` or direct
|
||||||
:ref:`direct assignment<direct-assignment>` ran multiple data modifying
|
assignment ran multiple data modifying queries without wrapping them in
|
||||||
queries without wrapping them in transactions. To reduce the risk of data
|
transactions. To reduce the risk of data corruption, all data modifying methods
|
||||||
corruption, all data modifying methods that affect multiple related objects
|
that affect multiple related objects (i.e. ``add()``, ``remove()``,
|
||||||
(i.e. ``add()``, ``remove()``, ``clear()``, and :ref:`direct assignment
|
``clear()``, and direct assignment) now perform their data modifying queries
|
||||||
<direct-assignment>`) now perform their data modifying queries from within a
|
from within a transaction, provided your database supports transactions.
|
||||||
transaction, provided your database supports transactions.
|
|
||||||
|
|
||||||
This has one backwards incompatible side effect, signal handlers triggered from
|
This has one backwards incompatible side effect, signal handlers triggered from
|
||||||
these methods are now executed within the method's transaction and any
|
these methods are now executed within the method's transaction and any
|
||||||
|
|
|
@ -747,11 +747,10 @@ setuptools is not installed.
|
||||||
Related set direct assignment
|
Related set direct assignment
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
:ref:`Direct assignment <direct-assignment>` of related objects in the ORM used
|
Direct assignment of related objects in the ORM used to perform a ``clear()``
|
||||||
to perform a ``clear()`` followed by a call to ``add()``. This caused
|
followed by a call to ``add()``. This caused needlessly large data changes and
|
||||||
needlessly large data changes and prevented using the
|
prevented using the :data:`~django.db.models.signals.m2m_changed` signal to
|
||||||
:data:`~django.db.models.signals.m2m_changed` signal to track individual
|
track individual changes in many-to-many relations.
|
||||||
changes in many-to-many relations.
|
|
||||||
|
|
||||||
Direct assignment now relies on the the new
|
Direct assignment now relies on the the new
|
||||||
:meth:`~django.db.models.fields.related.RelatedManager.set` method on related
|
:meth:`~django.db.models.fields.related.RelatedManager.set` method on related
|
||||||
|
|
|
@ -181,11 +181,11 @@ fields: ``groups`` and ``user_permissions``.
|
||||||
objects in the same way as any other :doc:`Django model
|
objects in the same way as any other :doc:`Django model
|
||||||
</topics/db/models>`::
|
</topics/db/models>`::
|
||||||
|
|
||||||
myuser.groups = [group_list]
|
myuser.groups.set([group_list])
|
||||||
myuser.groups.add(group, group, ...)
|
myuser.groups.add(group, group, ...)
|
||||||
myuser.groups.remove(group, group, ...)
|
myuser.groups.remove(group, group, ...)
|
||||||
myuser.groups.clear()
|
myuser.groups.clear()
|
||||||
myuser.user_permissions = [permission_list]
|
myuser.user_permissions.set([permission_list])
|
||||||
myuser.user_permissions.add(permission, permission, ...)
|
myuser.user_permissions.add(permission, permission, ...)
|
||||||
myuser.user_permissions.remove(permission, permission, ...)
|
myuser.user_permissions.remove(permission, permission, ...)
|
||||||
myuser.user_permissions.clear()
|
myuser.user_permissions.clear()
|
||||||
|
|
|
@ -221,11 +221,11 @@ And from the other end::
|
||||||
>>> a5.publications.all()
|
>>> a5.publications.all()
|
||||||
<QuerySet []>
|
<QuerySet []>
|
||||||
|
|
||||||
Relation sets can be assigned. Assignment clears any existing set members::
|
Relation sets can be set::
|
||||||
|
|
||||||
>>> a4.publications.all()
|
>>> a4.publications.all()
|
||||||
<QuerySet [<Publication: Science News>]>
|
<QuerySet [<Publication: Science News>]>
|
||||||
>>> a4.publications = [p3]
|
>>> a4.publications.set([p3])
|
||||||
>>> a4.publications.all()
|
>>> a4.publications.all()
|
||||||
<QuerySet [<Publication: Science Weekly>]>
|
<QuerySet [<Publication: Science Weekly>]>
|
||||||
|
|
||||||
|
@ -282,18 +282,3 @@ referenced objects should be gone::
|
||||||
<QuerySet []>
|
<QuerySet []>
|
||||||
>>> p1.article_set.all()
|
>>> p1.article_set.all()
|
||||||
<QuerySet [<Article: NASA uses Python>]>
|
<QuerySet [<Article: NASA uses Python>]>
|
||||||
|
|
||||||
An alternate to calling
|
|
||||||
:meth:`~django.db.models.fields.related.RelatedManager.clear` is to assign the
|
|
||||||
empty set::
|
|
||||||
|
|
||||||
>>> p1.article_set = []
|
|
||||||
>>> p1.article_set.all()
|
|
||||||
<QuerySet []>
|
|
||||||
|
|
||||||
>>> a2.publications = [p1, new_publication]
|
|
||||||
>>> a2.publications.all()
|
|
||||||
<QuerySet [<Publication: Highlights for Children>, <Publication: The Python Journal>]>
|
|
||||||
>>> a2.publications = []
|
|
||||||
>>> a2.publications.all()
|
|
||||||
<QuerySet []>
|
|
||||||
|
|
|
@ -510,15 +510,15 @@ the intermediate model::
|
||||||
>>> beatles.members.all()
|
>>> beatles.members.all()
|
||||||
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
|
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
|
||||||
|
|
||||||
Unlike normal many-to-many fields, you *can't* use ``add``, ``create``,
|
Unlike normal many-to-many fields, you *can't* use ``add()``, ``create()``,
|
||||||
or assignment (i.e., ``beatles.members = [...]``) to create relationships::
|
or ``set()`` to create relationships::
|
||||||
|
|
||||||
# THIS WILL NOT WORK
|
# THIS WILL NOT WORK
|
||||||
>>> beatles.members.add(john)
|
>>> beatles.members.add(john)
|
||||||
# NEITHER WILL THIS
|
# NEITHER WILL THIS
|
||||||
>>> beatles.members.create(name="George Harrison")
|
>>> beatles.members.create(name="George Harrison")
|
||||||
# AND NEITHER WILL THIS
|
# AND NEITHER WILL THIS
|
||||||
>>> beatles.members = [john, paul, ringo, george]
|
>>> beatles.members.set([john, paul, ringo, george])
|
||||||
|
|
||||||
Why? You can't just create a relationship between a ``Person`` and a ``Group``
|
Why? You can't just create a relationship between a ``Person`` and a ``Group``
|
||||||
- you need to specify all the detail for the relationship required by the
|
- you need to specify all the detail for the relationship required by the
|
||||||
|
@ -575,7 +575,6 @@ Another way to access the same information is by querying the
|
||||||
>>> ringos_membership.invite_reason
|
>>> ringos_membership.invite_reason
|
||||||
'Needed a new drummer.'
|
'Needed a new drummer.'
|
||||||
|
|
||||||
|
|
||||||
One-to-one relationships
|
One-to-one relationships
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -1223,12 +1223,11 @@ be found in the :doc:`related objects reference </ref/models/relations>`.
|
||||||
``set(objs)``
|
``set(objs)``
|
||||||
Replace the set of related objects.
|
Replace the set of related objects.
|
||||||
|
|
||||||
To assign the members of a related set in one fell swoop, just assign to it
|
To assign the members of a related set, use the ``set()`` method with an
|
||||||
from any iterable object. The iterable can contain object instances, or just
|
iterable of object instances or a list of primary key values. For example::
|
||||||
a list of primary key values. For example::
|
|
||||||
|
|
||||||
b = Blog.objects.get(id=1)
|
b = Blog.objects.get(id=1)
|
||||||
b.entry_set = [e1, e2]
|
b.entry_set.set([e1, e2]
|
||||||
|
|
||||||
In this example, ``e1`` and ``e2`` can be full Entry instances, or integer
|
In this example, ``e1`` and ``e2`` can be full Entry instances, or integer
|
||||||
primary key values.
|
primary key values.
|
||||||
|
|
|
@ -263,8 +263,7 @@ class ListFiltersTests(TestCase):
|
||||||
title='Gipsy guitar for dummies', year=2002, is_best_seller=True,
|
title='Gipsy guitar for dummies', year=2002, is_best_seller=True,
|
||||||
date_registered=self.one_week_ago,
|
date_registered=self.one_week_ago,
|
||||||
)
|
)
|
||||||
self.gipsy_book.contributors = [self.bob, self.lisa]
|
self.gipsy_book.contributors.set([self.bob, self.lisa])
|
||||||
self.gipsy_book.save()
|
|
||||||
|
|
||||||
# Departments
|
# Departments
|
||||||
self.dev = Department.objects.create(code='DEV', description='Development')
|
self.dev = Department.objects.create(code='DEV', description='Development')
|
||||||
|
|
|
@ -15,7 +15,7 @@ class CustomColumnsTests(TestCase):
|
||||||
self.authors = [self.a1, self.a2]
|
self.authors = [self.a1, self.a2]
|
||||||
|
|
||||||
self.article = Article.objects.create(headline="Django lets you build Web apps easily", primary_author=self.a1)
|
self.article = Article.objects.create(headline="Django lets you build Web apps easily", primary_author=self.a1)
|
||||||
self.article.authors = self.authors
|
self.article.authors.set(self.authors)
|
||||||
|
|
||||||
def test_query_all_available_authors(self):
|
def test_query_all_available_authors(self):
|
||||||
self.assertQuerysetEqual(
|
self.assertQuerysetEqual(
|
||||||
|
|
|
@ -284,7 +284,8 @@ class ManyToManyExclusionTestCase(TestCase):
|
||||||
'multi_choice_int': [opt1.pk],
|
'multi_choice_int': [opt1.pk],
|
||||||
}
|
}
|
||||||
instance = ChoiceFieldModel.objects.create(**initial)
|
instance = ChoiceFieldModel.objects.create(**initial)
|
||||||
instance.multi_choice = instance.multi_choice_int = [opt2, opt3]
|
instance.multi_choice.set([opt2, opt3])
|
||||||
|
instance.multi_choice_int.set([opt2, opt3])
|
||||||
form = ChoiceFieldExclusionForm(data=data, instance=instance)
|
form = ChoiceFieldExclusionForm(data=data, instance=instance)
|
||||||
self.assertTrue(form.is_valid())
|
self.assertTrue(form.is_valid())
|
||||||
self.assertEqual(form.cleaned_data['multi_choice'], data['multi_choice'])
|
self.assertEqual(form.cleaned_data['multi_choice'], data['multi_choice'])
|
||||||
|
|
|
@ -313,31 +313,31 @@ class GenericRelationsTests(TestCase):
|
||||||
fatty = bacon.tags.create(tag="fatty")
|
fatty = bacon.tags.create(tag="fatty")
|
||||||
salty = bacon.tags.create(tag="salty")
|
salty = bacon.tags.create(tag="salty")
|
||||||
|
|
||||||
bacon.tags = [fatty, salty]
|
bacon.tags.set([fatty, salty])
|
||||||
self.assertQuerysetEqual(bacon.tags.all(), [
|
self.assertQuerysetEqual(bacon.tags.all(), [
|
||||||
"<TaggedItem: fatty>",
|
"<TaggedItem: fatty>",
|
||||||
"<TaggedItem: salty>",
|
"<TaggedItem: salty>",
|
||||||
])
|
])
|
||||||
|
|
||||||
bacon.tags = [fatty]
|
bacon.tags.set([fatty])
|
||||||
self.assertQuerysetEqual(bacon.tags.all(), [
|
self.assertQuerysetEqual(bacon.tags.all(), [
|
||||||
"<TaggedItem: fatty>",
|
"<TaggedItem: fatty>",
|
||||||
])
|
])
|
||||||
|
|
||||||
bacon.tags = []
|
bacon.tags.set([])
|
||||||
self.assertQuerysetEqual(bacon.tags.all(), [])
|
self.assertQuerysetEqual(bacon.tags.all(), [])
|
||||||
|
|
||||||
def test_assign_with_queryset(self):
|
def test_assign_with_queryset(self):
|
||||||
# Ensure that querysets used in reverse GFK assignments are pre-evaluated
|
# Ensure that querysets used in reverse GFK assignments are pre-evaluated
|
||||||
# so their value isn't affected by the clearing operation in
|
# so their value isn't affected by the clearing operation in
|
||||||
# ManyToManyDescriptor.__set__. Refs #19816.
|
# ManyRelatedManager.set() (#19816).
|
||||||
bacon = Vegetable.objects.create(name="Bacon", is_yucky=False)
|
bacon = Vegetable.objects.create(name="Bacon", is_yucky=False)
|
||||||
bacon.tags.create(tag="fatty")
|
bacon.tags.create(tag="fatty")
|
||||||
bacon.tags.create(tag="salty")
|
bacon.tags.create(tag="salty")
|
||||||
self.assertEqual(2, bacon.tags.count())
|
self.assertEqual(2, bacon.tags.count())
|
||||||
|
|
||||||
qs = bacon.tags.filter(tag="fatty")
|
qs = bacon.tags.filter(tag="fatty")
|
||||||
bacon.tags = qs
|
bacon.tags.set(qs)
|
||||||
|
|
||||||
self.assertEqual(1, bacon.tags.count())
|
self.assertEqual(1, bacon.tags.count())
|
||||||
self.assertEqual(1, qs.count())
|
self.assertEqual(1, qs.count())
|
||||||
|
@ -705,7 +705,7 @@ class ProxyRelatedModelTest(TestCase):
|
||||||
base.save()
|
base.save()
|
||||||
newrel = ConcreteRelatedModel.objects.create()
|
newrel = ConcreteRelatedModel.objects.create()
|
||||||
|
|
||||||
newrel.bases = [base]
|
newrel.bases.set([base])
|
||||||
newrel = ConcreteRelatedModel.objects.get(pk=newrel.pk)
|
newrel = ConcreteRelatedModel.objects.get(pk=newrel.pk)
|
||||||
self.assertEqual(base, newrel.bases.get())
|
self.assertEqual(base, newrel.bases.get())
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ class GetObjectOr404Tests(TestCase):
|
||||||
self.assertRaises(Http404, get_object_or_404, Article, title="Foo")
|
self.assertRaises(Http404, get_object_or_404, Article, title="Foo")
|
||||||
|
|
||||||
article = Article.objects.create(title="Run away!")
|
article = Article.objects.create(title="Run away!")
|
||||||
article.authors = [a1, a2]
|
article.authors.set([a1, a2])
|
||||||
# get_object_or_404 can be passed a Model to query.
|
# get_object_or_404 can be passed a Model to query.
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
get_object_or_404(Article, title__contains="Run"),
|
get_object_or_404(Article, title__contains="Run"),
|
||||||
|
|
|
@ -677,13 +677,13 @@ class LookupTests(TestCase):
|
||||||
season_2011.games.create(home="Houston Astros", away="St. Louis Cardinals")
|
season_2011.games.create(home="Houston Astros", away="St. Louis Cardinals")
|
||||||
season_2011.games.create(home="Houston Astros", away="Milwaukee Brewers")
|
season_2011.games.create(home="Houston Astros", away="Milwaukee Brewers")
|
||||||
hunter_pence = Player.objects.create(name="Hunter Pence")
|
hunter_pence = Player.objects.create(name="Hunter Pence")
|
||||||
hunter_pence.games = Game.objects.filter(season__year__in=[2009, 2010])
|
hunter_pence.games.set(Game.objects.filter(season__year__in=[2009, 2010]))
|
||||||
pudge = Player.objects.create(name="Ivan Rodriquez")
|
pudge = Player.objects.create(name="Ivan Rodriquez")
|
||||||
pudge.games = Game.objects.filter(season__year=2009)
|
pudge.games.set(Game.objects.filter(season__year=2009))
|
||||||
pedro_feliz = Player.objects.create(name="Pedro Feliz")
|
pedro_feliz = Player.objects.create(name="Pedro Feliz")
|
||||||
pedro_feliz.games = Game.objects.filter(season__year__in=[2011])
|
pedro_feliz.games.set(Game.objects.filter(season__year__in=[2011]))
|
||||||
johnson = Player.objects.create(name="Johnson")
|
johnson = Player.objects.create(name="Johnson")
|
||||||
johnson.games = Game.objects.filter(season__year__in=[2011])
|
johnson.games.set(Game.objects.filter(season__year__in=[2011]))
|
||||||
|
|
||||||
# Games in 2010
|
# Games in 2010
|
||||||
self.assertEqual(Game.objects.filter(season__year=2010).count(), 3)
|
self.assertEqual(Game.objects.filter(season__year=2010).count(), 3)
|
||||||
|
|
|
@ -74,7 +74,7 @@ class M2MRegressionTests(TestCase):
|
||||||
t2 = Tag.objects.create(name='t2')
|
t2 = Tag.objects.create(name='t2')
|
||||||
|
|
||||||
c1 = TagCollection.objects.create(name='c1')
|
c1 = TagCollection.objects.create(name='c1')
|
||||||
c1.tags = [t1, t2]
|
c1.tags.set([t1, t2])
|
||||||
c1 = TagCollection.objects.get(name='c1')
|
c1 = TagCollection.objects.get(name='c1')
|
||||||
|
|
||||||
self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"], ordered=False)
|
self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"], ordered=False)
|
||||||
|
@ -104,10 +104,10 @@ class M2MRegressionTests(TestCase):
|
||||||
t1 = Tag.objects.create(name='t1')
|
t1 = Tag.objects.create(name='t1')
|
||||||
t2 = Tag.objects.create(name='t2')
|
t2 = Tag.objects.create(name='t2')
|
||||||
c1 = TagCollection.objects.create(name='c1')
|
c1 = TagCollection.objects.create(name='c1')
|
||||||
c1.tags = [t1, t2]
|
c1.tags.set([t1, t2])
|
||||||
|
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
c1.tags = 7
|
c1.tags.set(7)
|
||||||
|
|
||||||
c1.refresh_from_db()
|
c1.refresh_from_db()
|
||||||
self.assertQuerysetEqual(c1.tags.order_by('name'), ["<Tag: t1>", "<Tag: t2>"])
|
self.assertQuerysetEqual(c1.tags.order_by('name'), ["<Tag: t1>", "<Tag: t2>"])
|
||||||
|
|
|
@ -270,7 +270,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
self.assertEqual(self.m2m_changed_messages, expected_messages)
|
self.assertEqual(self.m2m_changed_messages, expected_messages)
|
||||||
|
|
||||||
# direct assignment clears the set first, then adds
|
# direct assignment clears the set first, then adds
|
||||||
self.vw.default_parts = [self.wheelset, self.doors, self.engine]
|
self.vw.default_parts.set([self.wheelset, self.doors, self.engine])
|
||||||
expected_messages.append({
|
expected_messages.append({
|
||||||
'instance': self.vw,
|
'instance': self.vw,
|
||||||
'action': 'pre_remove',
|
'action': 'pre_remove',
|
||||||
|
@ -362,7 +362,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
# Check that signals still work when model inheritance is involved
|
# Check that signals still work when model inheritance is involved
|
||||||
c4 = SportsCar.objects.create(name='Bugatti', price='1000000')
|
c4 = SportsCar.objects.create(name='Bugatti', price='1000000')
|
||||||
c4b = Car.objects.get(name='Bugatti')
|
c4b = Car.objects.get(name='Bugatti')
|
||||||
c4.default_parts = [self.doors]
|
c4.default_parts.set([self.doors])
|
||||||
expected_messages.append({
|
expected_messages.append({
|
||||||
'instance': c4,
|
'instance': c4,
|
||||||
'action': 'pre_add',
|
'action': 'pre_add',
|
||||||
|
@ -407,7 +407,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
|
|
||||||
def test_m2m_relations_with_self_add_friends(self):
|
def test_m2m_relations_with_self_add_friends(self):
|
||||||
self._initialize_signal_person()
|
self._initialize_signal_person()
|
||||||
self.alice.friends = [self.bob, self.chuck]
|
self.alice.friends.set([self.bob, self.chuck])
|
||||||
self.assertEqual(self.m2m_changed_messages, [
|
self.assertEqual(self.m2m_changed_messages, [
|
||||||
{
|
{
|
||||||
'instance': self.alice,
|
'instance': self.alice,
|
||||||
|
@ -426,7 +426,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
|
|
||||||
def test_m2m_relations_with_self_add_fan(self):
|
def test_m2m_relations_with_self_add_fan(self):
|
||||||
self._initialize_signal_person()
|
self._initialize_signal_person()
|
||||||
self.alice.fans = [self.daisy]
|
self.alice.fans.set([self.daisy])
|
||||||
self.assertEqual(self.m2m_changed_messages, [
|
self.assertEqual(self.m2m_changed_messages, [
|
||||||
{
|
{
|
||||||
'instance': self.alice,
|
'instance': self.alice,
|
||||||
|
@ -445,7 +445,7 @@ class ManyToManySignalsTest(TestCase):
|
||||||
|
|
||||||
def test_m2m_relations_with_self_add_idols(self):
|
def test_m2m_relations_with_self_add_idols(self):
|
||||||
self._initialize_signal_person()
|
self._initialize_signal_person()
|
||||||
self.chuck.idols = [self.alice, self.bob]
|
self.chuck.idols.set([self.alice, self.bob])
|
||||||
self.assertEqual(self.m2m_changed_messages, [
|
self.assertEqual(self.m2m_changed_messages, [
|
||||||
{
|
{
|
||||||
'instance': self.chuck,
|
'instance': self.chuck,
|
||||||
|
|
|
@ -97,7 +97,7 @@ class M2mThroughTests(TestCase):
|
||||||
members = list(Person.objects.filter(name__in=['Bob', 'Jim']))
|
members = list(Person.objects.filter(name__in=['Bob', 'Jim']))
|
||||||
|
|
||||||
with self.assertRaisesMessage(AttributeError, msg):
|
with self.assertRaisesMessage(AttributeError, msg):
|
||||||
setattr(self.rock, 'members', members)
|
self.rock.members.set(members)
|
||||||
|
|
||||||
self.assertQuerysetEqual(
|
self.assertQuerysetEqual(
|
||||||
self.rock.members.all(),
|
self.rock.members.all(),
|
||||||
|
@ -166,7 +166,7 @@ class M2mThroughTests(TestCase):
|
||||||
members = list(Group.objects.filter(name__in=['Rock', 'Roll']))
|
members = list(Group.objects.filter(name__in=['Rock', 'Roll']))
|
||||||
|
|
||||||
with self.assertRaisesMessage(AttributeError, msg):
|
with self.assertRaisesMessage(AttributeError, msg):
|
||||||
setattr(self.bob, 'group_set', members)
|
self.bob.group_set.set(members)
|
||||||
|
|
||||||
self.assertQuerysetEqual(
|
self.assertQuerysetEqual(
|
||||||
self.bob.group_set.all(),
|
self.bob.group_set.all(),
|
||||||
|
|
|
@ -49,10 +49,22 @@ class M2MThroughTestCase(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_cannot_use_setattr_on_reverse_m2m_with_intermediary_model(self):
|
def test_cannot_use_setattr_on_reverse_m2m_with_intermediary_model(self):
|
||||||
self.assertRaises(AttributeError, setattr, self.bob, "group_set", [])
|
msg = (
|
||||||
|
"Cannot set values on a ManyToManyField which specifies an "
|
||||||
|
"intermediary model. Use m2m_through_regress.Membership's Manager "
|
||||||
|
"instead."
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(AttributeError, msg):
|
||||||
|
self.bob.group_set.set([])
|
||||||
|
|
||||||
def test_cannot_use_setattr_on_forward_m2m_with_intermediary_model(self):
|
def test_cannot_use_setattr_on_forward_m2m_with_intermediary_model(self):
|
||||||
self.assertRaises(AttributeError, setattr, self.roll, "members", [])
|
msg = (
|
||||||
|
"Cannot set values on a ManyToManyField which specifies an "
|
||||||
|
"intermediary model. Use m2m_through_regress.Membership's Manager "
|
||||||
|
"instead."
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(AttributeError, msg):
|
||||||
|
self.roll.members.set([])
|
||||||
|
|
||||||
def test_cannot_use_create_on_m2m_with_intermediary_model(self):
|
def test_cannot_use_create_on_m2m_with_intermediary_model(self):
|
||||||
self.assertRaises(AttributeError, self.rock.members.create, name="Anne")
|
self.assertRaises(AttributeError, self.rock.members.create, name="Anne")
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.test import TestCase
|
from django.test import TestCase, ignore_warnings
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
|
|
||||||
from .models import Article, InheritedArticleA, InheritedArticleB, Publication
|
from .models import Article, InheritedArticleA, InheritedArticleB, Publication
|
||||||
|
|
||||||
|
@ -371,8 +372,17 @@ class ManyToManyTests(TestCase):
|
||||||
self.a4.publications.set([], clear=True)
|
self.a4.publications.set([], clear=True)
|
||||||
self.assertQuerysetEqual(self.a4.publications.all(), [])
|
self.assertQuerysetEqual(self.a4.publications.all(), [])
|
||||||
|
|
||||||
def test_assign(self):
|
def test_assign_deprecation(self):
|
||||||
# Relation sets can be assigned. Assignment clears any existing set members
|
msg = (
|
||||||
|
"Direct assignment to the reverse side of a related set is "
|
||||||
|
"deprecated due to the implicit save() that happens. Use "
|
||||||
|
"article_set.set() instead."
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(RemovedInDjango20Warning, msg):
|
||||||
|
self.p2.article_set = [self.a4, self.a3]
|
||||||
|
|
||||||
|
@ignore_warnings(category=RemovedInDjango20Warning)
|
||||||
|
def test_assign_deprecated(self):
|
||||||
self.p2.article_set = [self.a4, self.a3]
|
self.p2.article_set = [self.a4, self.a3]
|
||||||
self.assertQuerysetEqual(self.p2.article_set.all(),
|
self.assertQuerysetEqual(self.p2.article_set.all(),
|
||||||
[
|
[
|
||||||
|
@ -393,9 +403,29 @@ class ManyToManyTests(TestCase):
|
||||||
self.a4.publications = []
|
self.a4.publications = []
|
||||||
self.assertQuerysetEqual(self.a4.publications.all(), [])
|
self.assertQuerysetEqual(self.a4.publications.all(), [])
|
||||||
|
|
||||||
|
def test_assign(self):
|
||||||
|
# Relation sets can be assigned using set().
|
||||||
|
self.p2.article_set.set([self.a4, self.a3])
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
self.p2.article_set.all(), [
|
||||||
|
'<Article: NASA finds intelligent life on Earth>',
|
||||||
|
'<Article: Oxygen-free diet works wonders>',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertQuerysetEqual(self.a4.publications.all(), ['<Publication: Science News>'])
|
||||||
|
self.a4.publications.set([self.p3.id])
|
||||||
|
self.assertQuerysetEqual(self.p2.article_set.all(), ['<Article: NASA finds intelligent life on Earth>'])
|
||||||
|
self.assertQuerysetEqual(self.a4.publications.all(), ['<Publication: Science Weekly>'])
|
||||||
|
|
||||||
|
# An alternate to calling clear() is to set an empty set.
|
||||||
|
self.p2.article_set.set([])
|
||||||
|
self.assertQuerysetEqual(self.p2.article_set.all(), [])
|
||||||
|
self.a4.publications.set([])
|
||||||
|
self.assertQuerysetEqual(self.a4.publications.all(), [])
|
||||||
|
|
||||||
def test_assign_ids(self):
|
def test_assign_ids(self):
|
||||||
# Relation sets can also be set using primary key values
|
# Relation sets can also be set using primary key values
|
||||||
self.p2.article_set = [self.a4.id, self.a3.id]
|
self.p2.article_set.set([self.a4.id, self.a3.id])
|
||||||
self.assertQuerysetEqual(self.p2.article_set.all(),
|
self.assertQuerysetEqual(self.p2.article_set.all(),
|
||||||
[
|
[
|
||||||
'<Article: NASA finds intelligent life on Earth>',
|
'<Article: NASA finds intelligent life on Earth>',
|
||||||
|
@ -403,7 +433,7 @@ class ManyToManyTests(TestCase):
|
||||||
])
|
])
|
||||||
self.assertQuerysetEqual(self.a4.publications.all(),
|
self.assertQuerysetEqual(self.a4.publications.all(),
|
||||||
['<Publication: Science News>'])
|
['<Publication: Science News>'])
|
||||||
self.a4.publications = [self.p3.id]
|
self.a4.publications.set([self.p3.id])
|
||||||
self.assertQuerysetEqual(self.p2.article_set.all(),
|
self.assertQuerysetEqual(self.p2.article_set.all(),
|
||||||
['<Article: NASA finds intelligent life on Earth>'])
|
['<Article: NASA finds intelligent life on Earth>'])
|
||||||
self.assertQuerysetEqual(self.a4.publications.all(),
|
self.assertQuerysetEqual(self.a4.publications.all(),
|
||||||
|
@ -412,11 +442,11 @@ class ManyToManyTests(TestCase):
|
||||||
def test_forward_assign_with_queryset(self):
|
def test_forward_assign_with_queryset(self):
|
||||||
# Ensure that querysets used in m2m assignments are pre-evaluated
|
# Ensure that querysets used in m2m assignments are pre-evaluated
|
||||||
# so their value isn't affected by the clearing operation in
|
# so their value isn't affected by the clearing operation in
|
||||||
# ManyToManyDescriptor.__set__. Refs #19816.
|
# ManyRelatedManager.set() (#19816).
|
||||||
self.a1.publications = [self.p1, self.p2]
|
self.a1.publications.set([self.p1, self.p2])
|
||||||
|
|
||||||
qs = self.a1.publications.filter(title='The Python Journal')
|
qs = self.a1.publications.filter(title='The Python Journal')
|
||||||
self.a1.publications = qs
|
self.a1.publications.set(qs)
|
||||||
|
|
||||||
self.assertEqual(1, self.a1.publications.count())
|
self.assertEqual(1, self.a1.publications.count())
|
||||||
self.assertEqual(1, qs.count())
|
self.assertEqual(1, qs.count())
|
||||||
|
@ -424,11 +454,11 @@ class ManyToManyTests(TestCase):
|
||||||
def test_reverse_assign_with_queryset(self):
|
def test_reverse_assign_with_queryset(self):
|
||||||
# Ensure that querysets used in M2M assignments are pre-evaluated
|
# Ensure that querysets used in M2M assignments are pre-evaluated
|
||||||
# so their value isn't affected by the clearing operation in
|
# so their value isn't affected by the clearing operation in
|
||||||
# ManyToManyDescriptor.__set__. Refs #19816.
|
# ManyRelatedManager.set() (#19816).
|
||||||
self.p1.article_set = [self.a1, self.a2]
|
self.p1.article_set.set([self.a1, self.a2])
|
||||||
|
|
||||||
qs = self.p1.article_set.filter(headline='Django lets you build Web apps easily')
|
qs = self.p1.article_set.filter(headline='Django lets you build Web apps easily')
|
||||||
self.p1.article_set = qs
|
self.p1.article_set.set(qs)
|
||||||
|
|
||||||
self.assertEqual(1, self.p1.article_set.count())
|
self.assertEqual(1, self.p1.article_set.count())
|
||||||
self.assertEqual(1, qs.count())
|
self.assertEqual(1, qs.count())
|
||||||
|
|
|
@ -5,6 +5,7 @@ from django.core.exceptions import FieldError, MultipleObjectsReturned
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
from django.utils.translation import ugettext_lazy
|
from django.utils.translation import ugettext_lazy
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
|
@ -123,6 +124,15 @@ class ManyToOneTests(TestCase):
|
||||||
])
|
])
|
||||||
self.assertQuerysetEqual(self.r2.article_set.all(), ["<Article: Paul's story>"])
|
self.assertQuerysetEqual(self.r2.article_set.all(), ["<Article: Paul's story>"])
|
||||||
|
|
||||||
|
def test_reverse_assignment_deprecation(self):
|
||||||
|
msg = (
|
||||||
|
"Direct assignment to the reverse side of a related set is "
|
||||||
|
"deprecated due to the implicit save() that happens. Use "
|
||||||
|
"article_set.set() instead."
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(RemovedInDjango20Warning, msg):
|
||||||
|
self.r2.article_set = []
|
||||||
|
|
||||||
def test_assign(self):
|
def test_assign(self):
|
||||||
new_article = self.r.article_set.create(headline="John's second story",
|
new_article = self.r.article_set.create(headline="John's second story",
|
||||||
pub_date=datetime.date(2005, 7, 29))
|
pub_date=datetime.date(2005, 7, 29))
|
||||||
|
@ -141,8 +151,8 @@ class ManyToOneTests(TestCase):
|
||||||
])
|
])
|
||||||
self.assertQuerysetEqual(self.r2.article_set.all(), [])
|
self.assertQuerysetEqual(self.r2.article_set.all(), [])
|
||||||
|
|
||||||
# Set the article back again using set descriptor.
|
# Set the article back again using set() method.
|
||||||
self.r2.article_set = [new_article, new_article2]
|
self.r2.article_set.set([new_article, new_article2])
|
||||||
self.assertQuerysetEqual(self.r.article_set.all(), ["<Article: This is a test>"])
|
self.assertQuerysetEqual(self.r.article_set.all(), ["<Article: This is a test>"])
|
||||||
self.assertQuerysetEqual(self.r2.article_set.all(),
|
self.assertQuerysetEqual(self.r2.article_set.all(),
|
||||||
[
|
[
|
||||||
|
@ -150,9 +160,9 @@ class ManyToOneTests(TestCase):
|
||||||
"<Article: Paul's story>",
|
"<Article: Paul's story>",
|
||||||
])
|
])
|
||||||
|
|
||||||
# Funny case - assignment notation can only go so far; because the
|
# Because the ForeignKey cannot be null, existing members of the set
|
||||||
# ForeignKey cannot be null, existing members of the set must remain.
|
# must remain.
|
||||||
self.r.article_set = [new_article]
|
self.r.article_set.set([new_article])
|
||||||
self.assertQuerysetEqual(self.r.article_set.all(),
|
self.assertQuerysetEqual(self.r.article_set.all(),
|
||||||
[
|
[
|
||||||
"<Article: John's second story>",
|
"<Article: John's second story>",
|
||||||
|
|
|
@ -94,7 +94,7 @@ class ManyToOneNullTests(TestCase):
|
||||||
# Use descriptor assignment to allocate ForeignKey. Null is legal, so
|
# Use descriptor assignment to allocate ForeignKey. Null is legal, so
|
||||||
# existing members of the set that are not in the assignment set are
|
# existing members of the set that are not in the assignment set are
|
||||||
# set to null.
|
# set to null.
|
||||||
self.r2.article_set = [self.a2, self.a3]
|
self.r2.article_set.set([self.a2, self.a3])
|
||||||
self.assertQuerysetEqual(self.r2.article_set.all(),
|
self.assertQuerysetEqual(self.r2.article_set.all(),
|
||||||
['<Article: Second>', '<Article: Third>'])
|
['<Article: Second>', '<Article: Third>'])
|
||||||
# Clear the rest of the set
|
# Clear the rest of the set
|
||||||
|
@ -106,11 +106,11 @@ class ManyToOneNullTests(TestCase):
|
||||||
def test_assign_with_queryset(self):
|
def test_assign_with_queryset(self):
|
||||||
# Ensure that querysets used in reverse FK assignments are pre-evaluated
|
# Ensure that querysets used in reverse FK assignments are pre-evaluated
|
||||||
# so their value isn't affected by the clearing operation in
|
# so their value isn't affected by the clearing operation in
|
||||||
# ReverseManyToOneDescriptor.__set__. Refs #19816.
|
# RelatedManager.set() (#19816).
|
||||||
self.r2.article_set = [self.a2, self.a3]
|
self.r2.article_set.set([self.a2, self.a3])
|
||||||
|
|
||||||
qs = self.r2.article_set.filter(headline="Second")
|
qs = self.r2.article_set.filter(headline="Second")
|
||||||
self.r2.article_set = qs
|
self.r2.article_set.set(qs)
|
||||||
|
|
||||||
self.assertEqual(1, self.r2.article_set.count())
|
self.assertEqual(1, self.r2.article_set.count())
|
||||||
self.assertEqual(1, qs.count())
|
self.assertEqual(1, qs.count())
|
||||||
|
|
|
@ -344,7 +344,7 @@ class ModelFormsetTest(TestCase):
|
||||||
author3 = Author.objects.create(name='Walt Whitman')
|
author3 = Author.objects.create(name='Walt Whitman')
|
||||||
|
|
||||||
meeting = AuthorMeeting.objects.create(created=date.today())
|
meeting = AuthorMeeting.objects.create(created=date.today())
|
||||||
meeting.authors = Author.objects.all()
|
meeting.authors.set(Author.objects.all())
|
||||||
|
|
||||||
# create an Author instance to add to the meeting.
|
# create an Author instance to add to the meeting.
|
||||||
|
|
||||||
|
|
|
@ -234,9 +234,9 @@ class ModelInheritanceDataTests(TestCase):
|
||||||
def test_related_objects_for_inherited_models(self):
|
def test_related_objects_for_inherited_models(self):
|
||||||
# Related objects work just as they normally do.
|
# Related objects work just as they normally do.
|
||||||
s1 = Supplier.objects.create(name="Joe's Chickens", address="123 Sesame St")
|
s1 = Supplier.objects.create(name="Joe's Chickens", address="123 Sesame St")
|
||||||
s1.customers = [self.restaurant, self.italian_restaurant]
|
s1.customers .set([self.restaurant, self.italian_restaurant])
|
||||||
s2 = Supplier.objects.create(name="Luigi's Pasta", address="456 Sesame St")
|
s2 = Supplier.objects.create(name="Luigi's Pasta", address="456 Sesame St")
|
||||||
s2.customers = [self.italian_restaurant]
|
s2.customers.set([self.italian_restaurant])
|
||||||
|
|
||||||
# This won't work because the Place we select is not a Restaurant (it's
|
# This won't work because the Place we select is not a Restaurant (it's
|
||||||
# a Supplier).
|
# a Supplier).
|
||||||
|
|
|
@ -350,10 +350,10 @@ class ModelInheritanceTest(TestCase):
|
||||||
|
|
||||||
birthday = BirthdayParty.objects.create(
|
birthday = BirthdayParty.objects.create(
|
||||||
name='Birthday party for Alice')
|
name='Birthday party for Alice')
|
||||||
birthday.attendees = [p1, p3]
|
birthday.attendees.set([p1, p3])
|
||||||
|
|
||||||
bachelor = BachelorParty.objects.create(name='Bachelor party for Bob')
|
bachelor = BachelorParty.objects.create(name='Bachelor party for Bob')
|
||||||
bachelor.attendees = [p2, p4]
|
bachelor.attendees.set([p2, p4])
|
||||||
|
|
||||||
parties = list(p1.birthdayparty_set.all())
|
parties = list(p1.birthdayparty_set.all())
|
||||||
self.assertEqual(parties, [birthday])
|
self.assertEqual(parties, [birthday])
|
||||||
|
@ -371,7 +371,7 @@ class ModelInheritanceTest(TestCase):
|
||||||
# ... but it does inherit the m2m from its parent
|
# ... but it does inherit the m2m from its parent
|
||||||
messy = MessyBachelorParty.objects.create(
|
messy = MessyBachelorParty.objects.create(
|
||||||
name='Bachelor party for Dave')
|
name='Bachelor party for Dave')
|
||||||
messy.attendees = [p4]
|
messy.attendees.set([p4])
|
||||||
messy_parent = messy.bachelorparty_ptr
|
messy_parent = messy.bachelorparty_ptr
|
||||||
|
|
||||||
parties = list(p4.bachelorparty_set.all())
|
parties = list(p4.bachelorparty_set.all())
|
||||||
|
|
|
@ -177,8 +177,8 @@ class QueryTestCase(TestCase):
|
||||||
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
||||||
|
|
||||||
# Save the author relations
|
# Save the author relations
|
||||||
pro.authors = [marty]
|
pro.authors.set([marty])
|
||||||
dive.authors = [mark]
|
dive.authors.set([mark])
|
||||||
|
|
||||||
# Inspect the m2m tables directly.
|
# Inspect the m2m tables directly.
|
||||||
# There should be 1 entry in each database
|
# There should be 1 entry in each database
|
||||||
|
@ -224,7 +224,7 @@ class QueryTestCase(TestCase):
|
||||||
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
||||||
|
|
||||||
# Save the author relations
|
# Save the author relations
|
||||||
dive.authors = [mark]
|
dive.authors.set([mark])
|
||||||
|
|
||||||
# Add a second author
|
# Add a second author
|
||||||
john = Person.objects.using('other').create(name="John Smith")
|
john = Person.objects.using('other').create(name="John Smith")
|
||||||
|
@ -285,7 +285,7 @@ class QueryTestCase(TestCase):
|
||||||
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
||||||
|
|
||||||
# Save the author relations
|
# Save the author relations
|
||||||
dive.authors = [mark]
|
dive.authors.set([mark])
|
||||||
|
|
||||||
# Create a second book on the other database
|
# Create a second book on the other database
|
||||||
grease = Book.objects.using('other').create(title="Greasemonkey Hacks",
|
grease = Book.objects.using('other').create(title="Greasemonkey Hacks",
|
||||||
|
@ -357,7 +357,7 @@ class QueryTestCase(TestCase):
|
||||||
# Set a foreign key set with an object from a different database
|
# Set a foreign key set with an object from a different database
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
with transaction.atomic(using='default'):
|
with transaction.atomic(using='default'):
|
||||||
marty.edited = [pro, dive]
|
marty.edited.set([pro, dive])
|
||||||
|
|
||||||
# Add to an m2m with an object from a different database
|
# Add to an m2m with an object from a different database
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
|
@ -367,7 +367,7 @@ class QueryTestCase(TestCase):
|
||||||
# Set a m2m with an object from a different database
|
# Set a m2m with an object from a different database
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
with transaction.atomic(using='default'):
|
with transaction.atomic(using='default'):
|
||||||
marty.book_set = [pro, dive]
|
marty.book_set.set([pro, dive])
|
||||||
|
|
||||||
# Add to a reverse m2m with an object from a different database
|
# Add to a reverse m2m with an object from a different database
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
|
@ -377,7 +377,7 @@ class QueryTestCase(TestCase):
|
||||||
# Set a reverse m2m with an object from a different database
|
# Set a reverse m2m with an object from a different database
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
with transaction.atomic(using='other'):
|
with transaction.atomic(using='other'):
|
||||||
dive.authors = [mark, marty]
|
dive.authors.set([mark, marty])
|
||||||
|
|
||||||
def test_m2m_deletion(self):
|
def test_m2m_deletion(self):
|
||||||
"Cascaded deletions of m2m relations issue queries on the right database"
|
"Cascaded deletions of m2m relations issue queries on the right database"
|
||||||
|
@ -386,7 +386,7 @@ class QueryTestCase(TestCase):
|
||||||
published=datetime.date(2009, 5, 4))
|
published=datetime.date(2009, 5, 4))
|
||||||
|
|
||||||
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
mark = Person.objects.using('other').create(name="Mark Pilgrim")
|
||||||
dive.authors = [mark]
|
dive.authors.set([mark])
|
||||||
|
|
||||||
# Check the initial state
|
# Check the initial state
|
||||||
self.assertEqual(Person.objects.using('default').count(), 0)
|
self.assertEqual(Person.objects.using('default').count(), 0)
|
||||||
|
@ -414,7 +414,7 @@ class QueryTestCase(TestCase):
|
||||||
# Now try deletion in the reverse direction. Set up the relation again
|
# Now try deletion in the reverse direction. Set up the relation again
|
||||||
dive = Book.objects.using('other').create(title="Dive into Python",
|
dive = Book.objects.using('other').create(title="Dive into Python",
|
||||||
published=datetime.date(2009, 5, 4))
|
published=datetime.date(2009, 5, 4))
|
||||||
dive.authors = [mark]
|
dive.authors.set([mark])
|
||||||
|
|
||||||
# Check the initial state
|
# Check the initial state
|
||||||
self.assertEqual(Person.objects.using('default').count(), 0)
|
self.assertEqual(Person.objects.using('default').count(), 0)
|
||||||
|
@ -589,7 +589,7 @@ class QueryTestCase(TestCase):
|
||||||
# Set a foreign key set with an object from a different database
|
# Set a foreign key set with an object from a different database
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
with transaction.atomic(using='default'):
|
with transaction.atomic(using='default'):
|
||||||
marty.edited = [pro, dive]
|
marty.edited.set([pro, dive])
|
||||||
|
|
||||||
# Add to a foreign key set with an object from a different database
|
# Add to a foreign key set with an object from a different database
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
|
@ -1095,7 +1095,7 @@ class RouterTestCase(TestCase):
|
||||||
pro = Book.objects.using('default').create(title="Pro Django",
|
pro = Book.objects.using('default').create(title="Pro Django",
|
||||||
published=datetime.date(2008, 12, 16),
|
published=datetime.date(2008, 12, 16),
|
||||||
editor=marty)
|
editor=marty)
|
||||||
pro.authors = [marty]
|
pro.authors.set([marty])
|
||||||
|
|
||||||
# Create a book and author on the other database
|
# Create a book and author on the other database
|
||||||
Book.objects.using('other').create(title="Dive into Python",
|
Book.objects.using('other').create(title="Dive into Python",
|
||||||
|
@ -1320,7 +1320,7 @@ class RouterTestCase(TestCase):
|
||||||
|
|
||||||
# Set a m2m set with an object from a different database
|
# Set a m2m set with an object from a different database
|
||||||
try:
|
try:
|
||||||
marty.book_set = [pro, dive]
|
marty.book_set.set([pro, dive])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across primary/replica databases with a common source should be ok")
|
self.fail("Assignment across primary/replica databases with a common source should be ok")
|
||||||
|
|
||||||
|
@ -1358,7 +1358,7 @@ class RouterTestCase(TestCase):
|
||||||
|
|
||||||
# Set a reverse m2m with an object from a different database
|
# Set a reverse m2m with an object from a different database
|
||||||
try:
|
try:
|
||||||
dive.authors = [mark, marty]
|
dive.authors.set([mark, marty])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.fail("Assignment across primary/replica databases with a common source should be ok")
|
self.fail("Assignment across primary/replica databases with a common source should be ok")
|
||||||
|
|
||||||
|
@ -1861,7 +1861,7 @@ class RouterAttributeErrorTestCase(TestCase):
|
||||||
b = Book.objects.create(title="Pro Django",
|
b = Book.objects.create(title="Pro Django",
|
||||||
published=datetime.date(2008, 12, 16))
|
published=datetime.date(2008, 12, 16))
|
||||||
p = Person.objects.create(name="Marty Alchin")
|
p = Person.objects.create(name="Marty Alchin")
|
||||||
b.authors = [p]
|
b.authors.set([p])
|
||||||
b.editor = p
|
b.editor = p
|
||||||
with self.override_router():
|
with self.override_router():
|
||||||
self.assertRaises(AttributeError, b.delete)
|
self.assertRaises(AttributeError, b.delete)
|
||||||
|
@ -1872,7 +1872,8 @@ class RouterAttributeErrorTestCase(TestCase):
|
||||||
published=datetime.date(2008, 12, 16))
|
published=datetime.date(2008, 12, 16))
|
||||||
p = Person.objects.create(name="Marty Alchin")
|
p = Person.objects.create(name="Marty Alchin")
|
||||||
with self.override_router():
|
with self.override_router():
|
||||||
self.assertRaises(AttributeError, setattr, b, 'authors', [p])
|
with self.assertRaises(AttributeError):
|
||||||
|
b.authors.set([p])
|
||||||
|
|
||||||
|
|
||||||
class ModelMetaRouter(object):
|
class ModelMetaRouter(object):
|
||||||
|
@ -1898,7 +1899,7 @@ class RouterModelArgumentTestCase(TestCase):
|
||||||
# test clear
|
# test clear
|
||||||
b.authors.clear()
|
b.authors.clear()
|
||||||
# test setattr
|
# test setattr
|
||||||
b.authors = [p]
|
b.authors.set([p])
|
||||||
# test M2M collection
|
# test M2M collection
|
||||||
b.delete()
|
b.delete()
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,7 @@ class OneToOneTests(TestCase):
|
||||||
"""
|
"""
|
||||||
f = Favorites(name='Fred')
|
f = Favorites(name='Fred')
|
||||||
f.save()
|
f.save()
|
||||||
f.restaurants = [self.r1]
|
f.restaurants.set([self.r1])
|
||||||
self.assertQuerysetEqual(
|
self.assertQuerysetEqual(
|
||||||
f.restaurants.all(),
|
f.restaurants.all(),
|
||||||
['<Restaurant: Demon Dogs the restaurant>']
|
['<Restaurant: Demon Dogs the restaurant>']
|
||||||
|
|
|
@ -73,12 +73,12 @@ class Queries1Tests(BaseQuerysetTest):
|
||||||
time3 = datetime.datetime(2007, 12, 20, 22, 25, 0)
|
time3 = datetime.datetime(2007, 12, 20, 22, 25, 0)
|
||||||
time4 = datetime.datetime(2007, 12, 20, 21, 0, 0)
|
time4 = datetime.datetime(2007, 12, 20, 21, 0, 0)
|
||||||
cls.i1 = Item.objects.create(name='one', created=cls.time1, modified=cls.time1, creator=cls.a1, note=cls.n3)
|
cls.i1 = Item.objects.create(name='one', created=cls.time1, modified=cls.time1, creator=cls.a1, note=cls.n3)
|
||||||
cls.i1.tags = [cls.t1, cls.t2]
|
cls.i1.tags.set([cls.t1, cls.t2])
|
||||||
cls.i2 = Item.objects.create(name='two', created=cls.time2, creator=cls.a2, note=n2)
|
cls.i2 = Item.objects.create(name='two', created=cls.time2, creator=cls.a2, note=n2)
|
||||||
cls.i2.tags = [cls.t1, cls.t3]
|
cls.i2.tags.set([cls.t1, cls.t3])
|
||||||
cls.i3 = Item.objects.create(name='three', created=time3, creator=cls.a2, note=cls.n3)
|
cls.i3 = Item.objects.create(name='three', created=time3, creator=cls.a2, note=cls.n3)
|
||||||
i4 = Item.objects.create(name='four', created=time4, creator=cls.a4, note=cls.n3)
|
i4 = Item.objects.create(name='four', created=time4, creator=cls.a4, note=cls.n3)
|
||||||
i4.tags = [t4]
|
i4.tags.set([t4])
|
||||||
|
|
||||||
cls.r1 = Report.objects.create(name='r1', creator=cls.a1)
|
cls.r1 = Report.objects.create(name='r1', creator=cls.a1)
|
||||||
Report.objects.create(name='r2', creator=a3)
|
Report.objects.create(name='r2', creator=a3)
|
||||||
|
@ -1373,8 +1373,8 @@ class Queries4Tests(BaseQuerysetTest):
|
||||||
math101 = tag.note_set.create(note='MATH', misc='101')
|
math101 = tag.note_set.create(note='MATH', misc='101')
|
||||||
s1 = tag.annotation_set.create(name='1')
|
s1 = tag.annotation_set.create(name='1')
|
||||||
s2 = tag.annotation_set.create(name='2')
|
s2 = tag.annotation_set.create(name='2')
|
||||||
s1.notes = [math101, anth100]
|
s1.notes.set([math101, anth100])
|
||||||
s2.notes = [math101]
|
s2.notes.set([math101])
|
||||||
result = math101.annotation_set.all() & tag.annotation_set.exclude(notes__in=[anth100])
|
result = math101.annotation_set.all() & tag.annotation_set.exclude(notes__in=[anth100])
|
||||||
self.assertEqual(list(result), [s2])
|
self.assertEqual(list(result), [s2])
|
||||||
|
|
||||||
|
@ -3348,9 +3348,9 @@ class ManyToManyExcludeTest(TestCase):
|
||||||
pg2 = Page.objects.create(text='pg2')
|
pg2 = Page.objects.create(text='pg2')
|
||||||
pg1 = Page.objects.create(text='pg1')
|
pg1 = Page.objects.create(text='pg1')
|
||||||
pa1 = Paragraph.objects.create(text='pa1')
|
pa1 = Paragraph.objects.create(text='pa1')
|
||||||
pa1.page = [pg1, pg2]
|
pa1.page.set([pg1, pg2])
|
||||||
pa2 = Paragraph.objects.create(text='pa2')
|
pa2 = Paragraph.objects.create(text='pa2')
|
||||||
pa2.page = [pg2, pg3]
|
pa2.page.set([pg2, pg3])
|
||||||
pa3 = Paragraph.objects.create(text='pa3')
|
pa3 = Paragraph.objects.create(text='pa3')
|
||||||
ch1 = Chapter.objects.create(title='ch1', paragraph=pa1)
|
ch1 = Chapter.objects.create(title='ch1', paragraph=pa1)
|
||||||
ch2 = Chapter.objects.create(title='ch2', paragraph=pa2)
|
ch2 = Chapter.objects.create(title='ch2', paragraph=pa2)
|
||||||
|
|
|
@ -66,7 +66,7 @@ def fk_create(pk, klass, data):
|
||||||
def m2m_create(pk, klass, data):
|
def m2m_create(pk, klass, data):
|
||||||
instance = klass(id=pk)
|
instance = klass(id=pk)
|
||||||
models.Model.save_base(instance, raw=True)
|
models.Model.save_base(instance, raw=True)
|
||||||
instance.data = data
|
instance.data.set(data)
|
||||||
return [instance]
|
return [instance]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ class SerializersTestBase(object):
|
||||||
pub_date=datetime(2006, 6, 16, 11, 00)
|
pub_date=datetime(2006, 6, 16, 11, 00)
|
||||||
)
|
)
|
||||||
self.a1.save()
|
self.a1.save()
|
||||||
self.a1.categories = [sports, op_ed]
|
self.a1.categories.set([sports, op_ed])
|
||||||
|
|
||||||
self.a2 = Article(
|
self.a2 = Article(
|
||||||
author=self.joe,
|
author=self.joe,
|
||||||
|
@ -120,7 +120,7 @@ class SerializersTestBase(object):
|
||||||
pub_date=datetime(2006, 6, 16, 13, 00, 11, 345)
|
pub_date=datetime(2006, 6, 16, 13, 00, 11, 345)
|
||||||
)
|
)
|
||||||
self.a2.save()
|
self.a2.save()
|
||||||
self.a2.categories = [music, op_ed]
|
self.a2.categories.set([music, op_ed])
|
||||||
|
|
||||||
def test_serialize(self):
|
def test_serialize(self):
|
||||||
"""Tests that basic serialization works."""
|
"""Tests that basic serialization works."""
|
||||||
|
|
|
@ -222,9 +222,9 @@ class SignalTests(BaseSignalTest):
|
||||||
data[:] = []
|
data[:] = []
|
||||||
|
|
||||||
# Assigning and removing to/from m2m shouldn't generate an m2m signal.
|
# Assigning and removing to/from m2m shouldn't generate an m2m signal.
|
||||||
b1.authors = [a1]
|
b1.authors.set([a1])
|
||||||
self.assertEqual(data, [])
|
self.assertEqual(data, [])
|
||||||
b1.authors = []
|
b1.authors.set([])
|
||||||
self.assertEqual(data, [])
|
self.assertEqual(data, [])
|
||||||
finally:
|
finally:
|
||||||
signals.pre_save.disconnect(pre_save_handler)
|
signals.pre_save.disconnect(pre_save_handler)
|
||||||
|
|
|
@ -19,7 +19,7 @@ class SimpleTests(TestCase):
|
||||||
a = A01.objects.create(f_a="foo", f_b=42)
|
a = A01.objects.create(f_a="foo", f_b=42)
|
||||||
B01.objects.create(fk_a=a, f_a="fred", f_b=1729)
|
B01.objects.create(fk_a=a, f_a="fred", f_b=1729)
|
||||||
c = C01.objects.create(f_a="barney", f_b=1)
|
c = C01.objects.create(f_a="barney", f_b=1)
|
||||||
c.mm_a = [a]
|
c.mm_a.set([a])
|
||||||
|
|
||||||
# ... and pull it out via the other set.
|
# ... and pull it out via the other set.
|
||||||
a2 = A02.objects.all()[0]
|
a2 = A02.objects.all()[0]
|
||||||
|
|
|
@ -124,11 +124,9 @@ class UpdateOnlyFieldsTests(TestCase):
|
||||||
profile_boss = Profile.objects.create(name='Boss', salary=3000)
|
profile_boss = Profile.objects.create(name='Boss', salary=3000)
|
||||||
e1 = Employee.objects.create(name='Sara', gender='F',
|
e1 = Employee.objects.create(name='Sara', gender='F',
|
||||||
employee_num=1, profile=profile_boss)
|
employee_num=1, profile=profile_boss)
|
||||||
|
|
||||||
a1 = Account.objects.create(num=1)
|
a1 = Account.objects.create(num=1)
|
||||||
a2 = Account.objects.create(num=2)
|
a2 = Account.objects.create(num=2)
|
||||||
|
e1.accounts.set([a1, a2])
|
||||||
e1.accounts = [a1, a2]
|
|
||||||
|
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
e1.save(update_fields=['accounts'])
|
e1.save(update_fields=['accounts'])
|
||||||
|
|
Loading…
Reference in New Issue