create a few reporters create an article article objects have access to their related reporter objects you can also instantiate an article by passing the reporter s id instead of a reporter object similarly the reporter id can be a string create an article via the reporter object create a new article and add it to the article set add the same article to a different article set check that it moves adding an object of the wrong type raises typeerror assign the article to the reporter set the article back again funny case because the foreignkey cannot be null existing members of the set must remain assign the article to the reporter directly using the descriptor set the article back again using set method because the foreignkey cannot be null existing members of the set must remain reporter cannot be null there should not be a clear or remove method reassigning the same value doesn t clear cached instance reporter objects have access to their related article objects get articles by id query on an article property the api automatically follows relationships as far as you need use double underscores to separate relationships this works as many levels deep as you want there s no limit find all articles for any reporter whose first name is john implied exact also works query twice over the related field the underlying query only makes one join when a related table is referenced twice the automatically joined table has a predictable name and should work fine with the string that comes out of forms form cleaneddata find all articles for a reporter use direct id check pk check and object comparison you can also use a queryset instead of a literal list of instances the queryset must be reduced to a list of values using values then converted into a query reporters can be queried reporters can query in opposite direction of foreignkey definition counting in the opposite direction works in conjunction with distinct queries can go round in circles implied exact also works it s possible to use values calls across manytoone relations note too that we clear the ordering here so as not to drag the headline field into the columns being used to determine uniqueness article objects selectrelated dates works properly when there are multiple articles with the same date but different foreignkey objects reporters if you delete a reporter their articles will be deleted you can delete using a join in the query create a new article with getorcreate using an explicit value for a foreignkey you can specify filters containing the explicit fk value create an article by paul for the same date get should respect explicit foreign keys as well regression for 12876 model methods that include queries that recursive don t cause recursion depth problems under deepcopy same twice same as each other tests of foreignkey assignment and the relatedobject cache see 6886 look up the object again so that we get a fresh object accessing the related object again returns the exactly same object but if we kill the cache we get a new object assigning a new object results in that object getting cached immediately assigning none succeeds if field is nulltrue bestchild should still be none after saving bestchild should still be none after fetching the object again assigning none will not fail child parent is nullfalse you also can t assign an object of the wrong type here you can assign none to child parent during object creation but when trying to save a child with parentnone the database will raise integrityerror creation using keyword argument should cache the related object creation using keyword argument and unsaved related instance 8070 creation using attname keyword argument and an id will cause the related object to be fetched test of multiple foreignkeys to the same model bug 7125 regression for 12190 should be able to instantiate a fk outside of a model and interrogate its related field the object isn t saved and the relation cannot be used the reverse foreign key manager can be created now the model is saved so we will need to execute a query only one school is available via all due to the custom default manager make sure the base manager is used so that a student can still access its related school even if the default manager doesn t normally allow it the exception raised on attribute access when a related object doesn t exist should be an instance of a subclass of attributeerror refs 21563 model save invalidates stale foreignkey relations after a primary key assignment create a few reporters create an article article objects have access to their related reporter objects you can also instantiate an article by passing the reporter s id instead of a reporter object similarly the reporter id can be a string create an article via the reporter object create a new article and add it to the article set add the same article to a different article set check that it moves adding an object of the wrong type raises typeerror assign the article to the reporter set the article back again funny case because the foreignkey cannot be null existing members of the set must remain assign the article to the reporter directly using the descriptor set the article back again using set method because the foreignkey cannot be null existing members of the set must remain reporter cannot be null there should not be a clear or remove method reassigning the same value doesn t clear cached instance reporter objects have access to their related article objects get articles by id query on an article property the api automatically follows relationships as far as you need use double underscores to separate relationships this works as many levels deep as you want there s no limit find all articles for any reporter whose first name is john implied __exact also works query twice over the related field the underlying query only makes one join when a related table is referenced twice the automatically joined table has a predictable name and should work fine with the string that comes out of forms form cleaned_data find all articles for a reporter use direct id check pk check and object comparison you can also use a queryset instead of a literal list of instances the queryset must be reduced to a list of values using values then converted into a query reporters can be queried reporters can query in opposite direction of foreignkey definition counting in the opposite direction works in conjunction with distinct queries can go round in circles implied __exact also works it s possible to use values calls across many to one relations note too that we clear the ordering here so as not to drag the headline field into the columns being used to determine uniqueness article objects select_related dates works properly when there are multiple articles with the same date but different foreign key objects reporters if you delete a reporter their articles will be deleted you can delete using a join in the query create a new article with get_or_create using an explicit value for a foreignkey you can specify filters containing the explicit fk value create an article by paul for the same date get should respect explicit foreign keys as well regression for 12876 model methods that include queries that recursive don t cause recursion depth problems under deepcopy same twice same as each other tests of foreignkey assignment and the related object cache see 6886 look up the object again so that we get a fresh object accessing the related object again returns the exactly same object but if we kill the cache we get a new object assigning a new object results in that object getting cached immediately assigning none succeeds if field is null true bestchild should still be none after saving bestchild should still be none after fetching the object again assigning none will not fail child parent is null false you also can t assign an object of the wrong type here you can assign none to child parent during object creation but when trying to save a child with parent none the database will raise integrityerror creation using keyword argument should cache the related object creation using keyword argument and unsaved related instance 8070 creation using attname keyword argument and an id will cause the related object to be fetched test of multiple foreignkeys to the same model bug 7125 regression for 12190 should be able to instantiate a fk outside of a model and interrogate its related field the object isn t saved and the relation cannot be used the reverse foreign key manager can be created now the model is saved so we will need to execute a query only one school is available via all due to the custom default manager make sure the base manager is used so that a student can still access its related school even if the default manager doesn t normally allow it the exception raised on attribute access when a related object doesn t exist should be an instance of a subclass of attributeerror refs 21563 model save invalidates stale foreignkey relations after a primary key assignment caches a reporter
import datetime
from copy import deepcopy

from django.core.exceptions import FieldError, MultipleObjectsReturned
from django.db import IntegrityError, models, transaction
from django.test import TestCase
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.translation import gettext_lazy

from .models import (
    Article,
    Category,
    Child,
    ChildNullableParent,
    ChildStringPrimaryKeyParent,
    City,
    Country,
    District,
    First,
    Parent,
    ParentStringPrimaryKey,
    Record,
    Relation,
    Reporter,
    School,
    Student,
    Third,
    ToFieldChild,
)


class ManyToOneTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        
        cls.r = Reporter(first_name="John", last_name="Smith", email="john@example.com")
        cls.r.save()
        cls.r2 = Reporter(
            first_name="Paul", last_name="Jones", email="paul@example.com"
        )
        cls.r2.save()
        
        cls.a = Article(
            headline="This is a test",
            pub_date=datetime.date(2005, 7, 27),
            reporter=cls.r,
        )
        cls.a.save()

    def test_get(self):
        
        r = self.a.reporter
        self.assertEqual(r.id, self.r.id)
        self.assertEqual((r.first_name, self.r.last_name), ("John", "Smith"))

    def test_create(self):
        
        
        a3 = Article(
            headline="Third article",
            pub_date=datetime.date(2005, 7, 27),
            reporter_id=self.r.id,
        )
        a3.save()
        self.assertEqual(a3.reporter.id, self.r.id)

        
        a4 = Article(
            headline="Fourth article",
            pub_date=datetime.date(2005, 7, 27),
            reporter_id=str(self.r.id),
        )
        a4.save()
        self.assertEqual(repr(a4.reporter), "<Reporter: John Smith>")

    def test_add(self):
        
        new_article = self.r.article_set.create(
            headline="John's second story", pub_date=datetime.date(2005, 7, 29)
        )
        self.assertEqual(repr(new_article), "<Article: John's second story>")
        self.assertEqual(new_article.reporter.id, self.r.id)

        
        new_article2 = Article(
            headline="Paul's story", pub_date=datetime.date(2006, 1, 17)
        )
        msg = (
            "<Article: Paul's story> instance isn't saved. Use bulk=False or save the "
            "object first."
        )
        with self.assertRaisesMessage(ValueError, msg):
            self.r.article_set.add(new_article2)

        self.r.article_set.add(new_article2, bulk=False)
        self.assertEqual(new_article2.reporter.id, self.r.id)
        self.assertSequenceEqual(
            self.r.article_set.all(),
            [new_article, new_article2, self.a],
        )

        
        self.r2.article_set.add(new_article2)
        self.assertEqual(new_article2.reporter.id, self.r2.id)
        self.assertSequenceEqual(self.r2.article_set.all(), [new_article2])

        
        with transaction.atomic():
            with self.assertRaisesMessage(
                TypeError, "'Article' instance expected, got <Reporter:"
            ):
                self.r.article_set.add(self.r2)
        self.assertSequenceEqual(
            self.r.article_set.all(),
            [new_article, self.a],
        )

    def test_set(self):
        new_article = self.r.article_set.create(
            headline="John's second story", pub_date=datetime.date(2005, 7, 29)
        )
        new_article2 = self.r2.article_set.create(
            headline="Paul's story", pub_date=datetime.date(2006, 1, 17)
        )

        
        new_article2.reporter = self.r
        new_article2.save()
        self.assertEqual(repr(new_article2.reporter), "<Reporter: John Smith>")
        self.assertEqual(new_article2.reporter.id, self.r.id)
        self.assertSequenceEqual(
            self.r.article_set.all(),
            [new_article, new_article2, self.a],
        )
        self.assertSequenceEqual(self.r2.article_set.all(), [])

        
        self.r2.article_set.set([new_article, new_article2])
        self.assertSequenceEqual(self.r.article_set.all(), [self.a])
        self.assertSequenceEqual(
            self.r2.article_set.all(),
            [new_article, new_article2],
        )

        
        
        self.r.article_set.set([new_article])
        self.assertSequenceEqual(
            self.r.article_set.all(),
            [new_article, self.a],
        )
        self.assertSequenceEqual(self.r2.article_set.all(), [new_article2])

    def test_reverse_assignment_deprecation(self):
        msg = (
            "Direct assignment to the reverse side of a related set is "
            "prohibited. Use article_set.set() instead."
        )
        with self.assertRaisesMessage(TypeError, msg):
            self.r2.article_set = []

    def test_assign(self):
        new_article = self.r.article_set.create(
            headline="John's second story", pub_date=datetime.date(2005, 7, 29)
        )
        new_article2 = self.r2.article_set.create(
            headline="Paul's story", pub_date=datetime.date(2006, 1, 17)
        )

        
        new_article2.reporter = self.r
        new_article2.save()
        self.assertEqual(repr(new_article2.reporter), "<Reporter: John Smith>")
        self.assertEqual(new_article2.reporter.id, self.r.id)
        self.assertSequenceEqual(
            self.r.article_set.all(),
            [new_article, new_article2, self.a],
        )
        self.assertSequenceEqual(self.r2.article_set.all(), [])

        
        self.r2.article_set.set([new_article, new_article2])
        self.assertSequenceEqual(self.r.article_set.all(), [self.a])
        self.assertSequenceEqual(
            self.r2.article_set.all(),
            [new_article, new_article2],
        )

        
        
        self.r.article_set.set([new_article])
        self.assertSequenceEqual(
            self.r.article_set.all(),
            [new_article, self.a],
        )
        self.assertSequenceEqual(self.r2.article_set.all(), [new_article2])
        
        self.assertFalse(hasattr(self.r2.article_set, "remove"))
        self.assertFalse(hasattr(self.r2.article_set, "clear"))

    def test_assign_fk_id_value(self):
        parent = Parent.objects.create(name="jeff")
        child1 = Child.objects.create(name="frank", parent=parent)
        child2 = Child.objects.create(name="randy", parent=parent)
        parent.bestchild = child1
        parent.save()
        parent.bestchild_id = child2.pk
        parent.save()
        self.assertEqual(parent.bestchild_id, child2.pk)
        self.assertFalse(Parent.bestchild.is_cached(parent))
        self.assertEqual(parent.bestchild, child2)
        self.assertTrue(Parent.bestchild.is_cached(parent))
        
        parent.bestchild_id = child2.pk
        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):
        new_article1 = self.r.article_set.create(
            headline="John's second story",
            pub_date=datetime.date(2005, 7, 29),
        )
        new_article2 = self.r2.article_set.create(
            headline="Paul's story",
            pub_date=datetime.date(2006, 1, 17),
        )
        
        self.assertSequenceEqual(
            self.r.article_set.all(),
            [new_article1, self.a],
        )
        self.assertSequenceEqual(
            self.r.article_set.filter(headline__startswith="This"), [self.a]
        )
        self.assertEqual(self.r.article_set.count(), 2)
        self.assertEqual(self.r2.article_set.count(), 1)
        
        self.assertSequenceEqual(Article.objects.filter(id__exact=self.a.id), [self.a])
        self.assertSequenceEqual(Article.objects.filter(pk=self.a.id), [self.a])
        
        self.assertSequenceEqual(
            Article.objects.filter(headline__startswith="This"), [self.a]
        )
        
        
        
        
        self.assertSequenceEqual(
            Article.objects.filter(reporter__first_name__exact="John"),
            [new_article1, self.a],
        )
        
        self.assertSequenceEqual(
            Article.objects.filter(reporter__first_name="John"),
            [new_article1, self.a],
        )
        
        self.assertSequenceEqual(
            Article.objects.filter(
                reporter__first_name__exact="John", reporter__last_name__exact="Smith"
            ),
            [new_article1, self.a],
        )
        
        
        queryset = Article.objects.filter(
            reporter__first_name__exact="John", reporter__last_name__exact="Smith"
        )
        self.assertNumQueries(1, list, queryset)
        self.assertEqual(
            queryset.query.get_compiler(queryset.db).as_sql()[0].count("INNER JOIN"), 1
        )

        
        self.assertSequenceEqual(
            Article.objects.filter(reporter__first_name__exact="John").extra(
                where=["many_to_one_reporter.last_name='Smith'"]
            ),
            [new_article1, self.a],
        )
        
        
        self.assertQuerySetEqual(
            (
                Article.objects.filter(reporter__first_name__exact="John").extra(
                    where=["many_to_one_reporter.last_name='%s'" % "Smith"]
                )
            ),
            [new_article1, self.a],
        )
        
        
        self.assertSequenceEqual(
            Article.objects.filter(reporter__id__exact=self.r.id),
            [new_article1, self.a],
        )
        self.assertSequenceEqual(
            Article.objects.filter(reporter__pk=self.r.id),
            [new_article1, self.a],
        )
        self.assertSequenceEqual(
            Article.objects.filter(reporter=self.r.id),
            [new_article1, self.a],
        )
        self.assertSequenceEqual(
            Article.objects.filter(reporter=self.r),
            [new_article1, self.a],
        )
        self.assertSequenceEqual(
            Article.objects.filter(reporter__in=[self.r.id, self.r2.id]).distinct(),
            [new_article1, new_article2, self.a],
        )
        self.assertSequenceEqual(
            Article.objects.filter(reporter__in=[self.r, self.r2]).distinct(),
            [new_article1, new_article2, self.a],
        )
        
        
        
        self.assertSequenceEqual(
            Article.objects.filter(
                reporter__in=Reporter.objects.filter(first_name="John")
                .values("pk")
                .query
            ).distinct(),
            [new_article1, self.a],
        )

    def test_reverse_selects(self):
        a3 = Article.objects.create(
            headline="Third article",
            pub_date=datetime.date(2005, 7, 27),
            reporter_id=self.r.id,
        )
        Article.objects.create(
            headline="Fourth article",
            pub_date=datetime.date(2005, 7, 27),
            reporter_id=self.r.id,
        )
        john_smith = [self.r]
        
        self.assertSequenceEqual(
            Reporter.objects.filter(id__exact=self.r.id), john_smith
        )
        self.assertSequenceEqual(Reporter.objects.filter(pk=self.r.id), john_smith)
        self.assertSequenceEqual(
            Reporter.objects.filter(first_name__startswith="John"), john_smith
        )
        
        self.assertSequenceEqual(
            Reporter.objects.filter(article__id__exact=self.a.id), john_smith
        )
        self.assertSequenceEqual(
            Reporter.objects.filter(article__pk=self.a.id), john_smith
        )
        self.assertSequenceEqual(Reporter.objects.filter(article=self.a.id), john_smith)
        self.assertSequenceEqual(Reporter.objects.filter(article=self.a), john_smith)
        self.assertSequenceEqual(
            Reporter.objects.filter(article__in=[self.a.id, a3.id]).distinct(),
            john_smith,
        )
        self.assertSequenceEqual(
            Reporter.objects.filter(article__in=[self.a.id, a3]).distinct(), john_smith
        )
        self.assertSequenceEqual(
            Reporter.objects.filter(article__in=[self.a, a3]).distinct(), john_smith
        )
        self.assertCountEqual(
            Reporter.objects.filter(article__headline__startswith="T"),
            [self.r, self.r],
        )
        self.assertSequenceEqual(
            Reporter.objects.filter(article__headline__startswith="T").distinct(),
            john_smith,
        )

        
        self.assertEqual(
            Reporter.objects.filter(article__headline__startswith="T").count(), 2
        )
        self.assertEqual(
            Reporter.objects.filter(article__headline__startswith="T")
            .distinct()
            .count(),
            1,
        )

        
        self.assertCountEqual(
            Reporter.objects.filter(article__reporter__first_name__startswith="John"),
            [self.r, self.r, self.r],
        )
        self.assertSequenceEqual(
            Reporter.objects.filter(
                article__reporter__first_name__startswith="John"
            ).distinct(),
            john_smith,
        )
        self.assertSequenceEqual(
            Reporter.objects.filter(article__reporter__exact=self.r).distinct(),
            john_smith,
        )

        
        self.assertSequenceEqual(
            Reporter.objects.filter(article__reporter=self.r).distinct(), john_smith
        )

        
        
        
        d = {"reporter__first_name": "John", "reporter__last_name": "Smith"}
        qs = (
            Article.objects.filter(
                reporter=self.r,
            )
            .distinct()
            .order_by()
            .values("reporter__first_name", "reporter__last_name")
        )
        self.assertEqual([d], list(qs))

    def test_select_related(self):
        
        
        
        r1 = Reporter.objects.create(
            first_name="Mike", last_name="Royko", email="royko@suntimes.com"
        )
        r2 = Reporter.objects.create(
            first_name="John", last_name="Kass", email="jkass@tribune.com"
        )
        Article.objects.create(
            headline="First", pub_date=datetime.date(1980, 4, 23), reporter=r1
        )
        Article.objects.create(
            headline="Second", pub_date=datetime.date(1980, 4, 23), reporter=r2
        )
        self.assertEqual(
            list(Article.objects.select_related().dates("pub_date", "day")),
            [datetime.date(1980, 4, 23), datetime.date(2005, 7, 27)],
        )
        self.assertEqual(
            list(Article.objects.select_related().dates("pub_date", "month")),
            [datetime.date(1980, 4, 1), datetime.date(2005, 7, 1)],
        )
        self.assertEqual(
            list(Article.objects.select_related().dates("pub_date", "year")),
            [datetime.date(1980, 1, 1), datetime.date(2005, 1, 1)],
        )

    def test_delete(self):
        new_article1 = self.r.article_set.create(
            headline="John's second story",
            pub_date=datetime.date(2005, 7, 29),
        )
        new_article2 = self.r2.article_set.create(
            headline="Paul's story",
            pub_date=datetime.date(2006, 1, 17),
        )
        new_article3 = Article.objects.create(
            headline="Third article",
            pub_date=datetime.date(2005, 7, 27),
            reporter_id=self.r.id,
        )
        new_article4 = Article.objects.create(
            headline="Fourth article",
            pub_date=datetime.date(2005, 7, 27),
            reporter_id=str(self.r.id),
        )
        
        self.assertSequenceEqual(
            Article.objects.all(),
            [new_article4, new_article1, new_article2, new_article3, self.a],
        )
        self.assertSequenceEqual(
            Reporter.objects.order_by("first_name"),
            [self.r, self.r2],
        )
        self.r2.delete()
        self.assertSequenceEqual(
            Article.objects.all(),
            [new_article4, new_article1, new_article3, self.a],
        )
        self.assertSequenceEqual(Reporter.objects.order_by("first_name"), [self.r])
        
        Reporter.objects.filter(article__headline__startswith="This").delete()
        self.assertSequenceEqual(Reporter.objects.all(), [])
        self.assertSequenceEqual(Article.objects.all(), [])

    def test_explicit_fk(self):
        
        
        a2, created = Article.objects.get_or_create(
            headline="John's second test",
            pub_date=datetime.date(2011, 5, 7),
            reporter_id=self.r.id,
        )
        self.assertTrue(created)
        self.assertEqual(a2.reporter.id, self.r.id)

        
        self.assertSequenceEqual(
            Article.objects.filter(reporter_id__exact=self.r.id),
            [a2, self.a],
        )

        
        a3 = Article.objects.create(
            headline="Paul's commentary",
            pub_date=datetime.date(2011, 5, 7),
            reporter_id=self.r2.id,
        )
        self.assertEqual(a3.reporter.id, self.r2.id)

        
        msg = "get() returned more than one Article -- it returned 2!"
        with self.assertRaisesMessage(MultipleObjectsReturned, msg):
            Article.objects.get(reporter_id=self.r.id)
        self.assertEqual(
            repr(a3),
            repr(
                Article.objects.get(
                    reporter_id=self.r2.id, pub_date=datetime.date(2011, 5, 7)
                )
            ),
        )

    def test_deepcopy_and_circular_references(self):
        
        
        self.r.cached_query = Article.objects.filter(reporter=self.r)
        self.assertEqual(repr(deepcopy(self.r)), "<Reporter: John Smith>")

    def test_manager_class_caching(self):
        r1 = Reporter.objects.create(first_name="Mike")
        r2 = Reporter.objects.create(first_name="John")

        
        self.assertIs(r1.article_set.__class__, r1.article_set.__class__)

        
        self.assertIs(r1.article_set.__class__, r2.article_set.__class__)

    def test_create_relation_with_gettext_lazy(self):
        reporter = Reporter.objects.create(
            first_name="John", last_name="Smith", email="john.smith@example.com"
        )
        lazy = gettext_lazy("test")
        reporter.article_set.create(headline=lazy, pub_date=datetime.date(2011, 6, 10))
        notlazy = str(lazy)
        article = reporter.article_set.get()
        self.assertEqual(article.headline, notlazy)

    def test_values_list_exception(self):
        expected_message = (
            "Cannot resolve keyword 'notafield' into field. Choices are: %s"
        )
        reporter_fields = ", ".join(sorted(f.name for f in Reporter._meta.get_fields()))
        with self.assertRaisesMessage(FieldError, expected_message % reporter_fields):
            Article.objects.values_list("reporter__notafield")
        article_fields = ", ".join(
            ["EXTRA"] + sorted(f.name for f in Article._meta.get_fields())
        )
        with self.assertRaisesMessage(FieldError, expected_message % article_fields):
            Article.objects.extra(select={"EXTRA": "EXTRA_SELECT"}).values_list(
                "notafield"
            )

    def test_fk_assignment_and_related_object_cache(self):
        

        p = Parent.objects.create(name="Parent")
        c = Child.objects.create(name="Child", parent=p)

        
        c = Child.objects.get(name="Child")
        p = c.parent

        
        self.assertIs(c.parent, p)

        
        del c._state.fields_cache["parent"]
        self.assertIsNot(c.parent, p)

        
        p2 = Parent.objects.create(name="Parent 2")
        c.parent = p2
        self.assertIs(c.parent, p2)

        
        p.bestchild = None
        self.assertIsNone(p.bestchild)

        
        p.save()
        self.assertIsNone(p.bestchild)

        
        p = Parent.objects.get(name="Parent")
        self.assertIsNone(p.bestchild)

        
        setattr(c, "parent", None)

        
        msg = (
            'Cannot assign "<First: First object (1)>": "Child.parent" must '
            'be a "Parent" instance.'
        )
        with self.assertRaisesMessage(ValueError, msg):
            setattr(c, "parent", First(id=1, second=1))

        
        Child(name="xyzzy", parent=None)

        
        
        with self.assertRaises(IntegrityError), transaction.atomic():
            Child.objects.create(name="xyzzy", parent=None)

        
        p = Parent.objects.get(name="Parent")
        c = Child(parent=p)
        self.assertIs(c.parent, p)

        
        p = Parent()
        msg = (
            "save() prohibited to prevent data loss due to unsaved related object "
            "'parent'."
        )
        with self.assertRaisesMessage(ValueError, msg):
            Child.objects.create(parent=p)

        with self.assertRaisesMessage(ValueError, msg):
            ToFieldChild.objects.create(parent=p)

        
        
        p = Parent.objects.get(name="Parent")
        c = Child(parent_id=p.id)
        self.assertIsNot(c.parent, p)
        self.assertEqual(c.parent, p)

    def test_save_parent_after_assign(self):
        category = Category(name="cats")
        record = Record(category=category)
        category.save()
        record.save()
        category.name = "dogs"
        with self.assertNumQueries(0):
            self.assertEqual(category.id, record.category_id)
            self.assertEqual(category.name, record.category.name)

    def test_save_nullable_fk_after_parent(self):
        parent = Parent()
        child = ChildNullableParent(parent=parent)
        parent.save()
        child.save()
        child.refresh_from_db()
        self.assertEqual(child.parent, parent)

    def test_save_nullable_fk_after_parent_with_to_field(self):
        parent = Parent(name="jeff")
        child = ToFieldChild(parent=parent)
        parent.save()
        child.save()
        child.refresh_from_db()
        self.assertEqual(child.parent, parent)
        self.assertEqual(child.parent_id, parent.name)

    def test_save_fk_after_parent_with_non_numeric_pk_set_on_child(self):
        parent = ParentStringPrimaryKey()
        child = ChildStringPrimaryKeyParent(parent=parent)
        child.parent.name = "jeff"
        parent.save()
        child.save()
        child.refresh_from_db()
        self.assertEqual(child.parent, parent)
        self.assertEqual(child.parent_id, parent.name)

    def test_fk_to_bigautofield(self):
        ch = City.objects.create(name="Chicago")
        District.objects.create(city=ch, name="Far South")
        District.objects.create(city=ch, name="North")

        ny = City.objects.create(name="New York", id=2**33)
        District.objects.create(city=ny, name="Brooklyn")
        District.objects.create(city=ny, name="Manhattan")

    def test_fk_to_smallautofield(self):
        us = Country.objects.create(name="United States")
        City.objects.create(country=us, name="Chicago")
        City.objects.create(country=us, name="New York")

        uk = Country.objects.create(name="United Kingdom", id=2**11)
        City.objects.create(country=uk, name="London")
        City.objects.create(country=uk, name="Edinburgh")

    def test_multiple_foreignkeys(self):
        
        c1 = Category.objects.create(name="First")
        c2 = Category.objects.create(name="Second")
        c3 = Category.objects.create(name="Third")
        r1 = Record.objects.create(category=c1)
        r2 = Record.objects.create(category=c1)
        r3 = Record.objects.create(category=c2)
        r4 = Record.objects.create(category=c2)
        r5 = Record.objects.create(category=c3)
        Relation.objects.create(left=r1, right=r2)
        Relation.objects.create(left=r3, right=r4)
        rel = Relation.objects.create(left=r1, right=r3)
        Relation.objects.create(left=r5, right=r2)
        Relation.objects.create(left=r3, right=r2)

        q1 = Relation.objects.filter(
            left__category__name__in=["First"], right__category__name__in=["Second"]
        )
        self.assertSequenceEqual(q1, [rel])

        q2 = Category.objects.filter(
            record__left_set__right__category__name="Second"
        ).order_by("name")
        self.assertSequenceEqual(q2, [c1, c2])

        p = Parent.objects.create(name="Parent")
        c = Child.objects.create(name="Child", parent=p)
        msg = 'Cannot assign "%r": "Child.parent" must be a "Parent" instance.' % c
        with self.assertRaisesMessage(ValueError, msg):
            Child.objects.create(name="Grandchild", parent=c)

    def test_fk_instantiation_outside_model(self):
        
        
        cat = models.ForeignKey(Category, models.CASCADE)
        self.assertEqual("id", cat.remote_field.get_related_field().name)

    def test_relation_unsaved(self):
        Third.objects.create(name="Third 1")
        Third.objects.create(name="Third 2")
        th = Third(name="testing")
        
        msg = (
            "'Third' instance needs to have a primary key value before this "
            "relationship can be used."
        )
        with self.assertRaisesMessage(ValueError, msg):
            th.child_set.count()
        
        self.assertEqual(th.child_set.model, Third)

        th.save()
        
        with self.assertNumQueries(1):
            self.assertEqual(th.child_set.count(), 0)

    def test_related_object(self):
        public_school = School.objects.create(is_public=True)
        public_student = Student.objects.create(school=public_school)

        private_school = School.objects.create(is_public=False)
        private_student = Student.objects.create(school=private_school)

        
        self.assertSequenceEqual(School.objects.all(), [public_school])

        self.assertEqual(public_student.school, public_school)

        
        
        
        self.assertEqual(private_student.school, private_school)

        School._meta.base_manager_name = "objects"
        School._meta._expire_cache()
        try:
            private_student = Student.objects.get(pk=private_student.pk)
            with self.assertRaises(School.DoesNotExist):
                private_student.school
        finally:
            School._meta.base_manager_name = None
            School._meta._expire_cache()

    def test_hasattr_related_object(self):
        
        
        
        self.assertFalse(hasattr(Article(), "reporter"))

    def test_create_after_prefetch(self):
        c = City.objects.create(name="Musical City")
        d1 = District.objects.create(name="Ladida", city=c)
        city = City.objects.prefetch_related("districts").get(id=c.id)
        self.assertSequenceEqual(city.districts.all(), [d1])
        d2 = city.districts.create(name="Goa")
        self.assertSequenceEqual(city.districts.all(), [d1, d2])

    def test_clear_after_prefetch(self):
        c = City.objects.create(name="Musical City")
        d = District.objects.create(name="Ladida", city=c)
        city = City.objects.prefetch_related("districts").get(id=c.id)
        self.assertSequenceEqual(city.districts.all(), [d])
        city.districts.clear()
        self.assertSequenceEqual(city.districts.all(), [])

    def test_remove_after_prefetch(self):
        c = City.objects.create(name="Musical City")
        d = District.objects.create(name="Ladida", city=c)
        city = City.objects.prefetch_related("districts").get(id=c.id)
        self.assertSequenceEqual(city.districts.all(), [d])
        city.districts.remove(d)
        self.assertSequenceEqual(city.districts.all(), [])

    def test_add_after_prefetch(self):
        c = City.objects.create(name="Musical City")
        District.objects.create(name="Ladida", city=c)
        d2 = District.objects.create(name="Ladidu")
        city = City.objects.prefetch_related("districts").get(id=c.id)
        self.assertEqual(city.districts.count(), 1)
        city.districts.add(d2)
        self.assertEqual(city.districts.count(), 2)

    def test_set_after_prefetch(self):
        c = City.objects.create(name="Musical City")
        District.objects.create(name="Ladida", city=c)
        d2 = District.objects.create(name="Ladidu")
        city = City.objects.prefetch_related("districts").get(id=c.id)
        self.assertEqual(city.districts.count(), 1)
        city.districts.set([d2])
        self.assertSequenceEqual(city.districts.all(), [d2])

    def test_add_then_remove_after_prefetch(self):
        c = City.objects.create(name="Musical City")
        District.objects.create(name="Ladida", city=c)
        d2 = District.objects.create(name="Ladidu")
        city = City.objects.prefetch_related("districts").get(id=c.id)
        self.assertEqual(city.districts.count(), 1)
        city.districts.add(d2)
        self.assertEqual(city.districts.count(), 2)
        city.districts.remove(d2)
        self.assertEqual(city.districts.count(), 1)

    def test_cached_relation_invalidated_on_save(self):
        
        self.assertEqual(self.a.reporter, self.r)  
        self.a.reporter_id = self.r2.pk
        self.a.save()
        self.assertEqual(self.a.reporter, self.r2)

    def test_cached_foreign_key_with_to_field_not_cleared_by_save(self):
        parent = Parent.objects.create(name="a")
        child = ToFieldChild.objects.create(parent=parent)
        with self.assertNumQueries(0):
            self.assertIs(child.parent, parent)

    def test_reverse_foreign_key_instance_to_field_caching(self):
        parent = Parent.objects.create(name="a")
        ToFieldChild.objects.create(parent=parent)
        child = parent.to_field_children.get()
        with self.assertNumQueries(0):
            self.assertIs(child.parent, parent)

    def test_add_remove_set_by_pk_raises(self):
        usa = Country.objects.create(name="United States")
        chicago = City.objects.create(name="Chicago")
        msg = "'City' instance expected, got %s" % chicago.pk
        with self.assertRaisesMessage(TypeError, msg):
            usa.cities.add(chicago.pk)
        with self.assertRaisesMessage(TypeError, msg):
            usa.cities.remove(chicago.pk)
        with self.assertRaisesMessage(TypeError, msg):
            usa.cities.set([chicago.pk])

    def test_get_prefetch_queryset_warning(self):
        City.objects.create(name="Chicago")
        cities = City.objects.all()
        msg = (
            "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() "
            "instead."
        )
        with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
            City.country.get_prefetch_queryset(cities)

    def test_get_prefetch_queryset_reverse_warning(self):
        usa = Country.objects.create(name="United States")
        City.objects.create(name="Chicago")
        countries = Country.objects.all()
        msg = (
            "get_prefetch_queryset() is deprecated. Use get_prefetch_querysets() "
            "instead."
        )
        with self.assertWarnsMessage(RemovedInDjango60Warning, msg):
            usa.cities.get_prefetch_queryset(countries)

    def test_get_prefetch_querysets_invalid_querysets_length(self):
        City.objects.create(name="Chicago")
        cities = City.objects.all()
        msg = (
            "querysets argument of get_prefetch_querysets() should have a length of 1."
        )
        with self.assertRaisesMessage(ValueError, msg):
            City.country.get_prefetch_querysets(
                instances=cities,
                querysets=[Country.objects.all(), Country.objects.all()],
            )

    def test_get_prefetch_querysets_reverse_invalid_querysets_length(self):
        usa = Country.objects.create(name="United States")
        City.objects.create(name="Chicago")
        countries = Country.objects.all()
        msg = (
            "querysets argument of get_prefetch_querysets() should have a length of 1."
        )
        with self.assertRaisesMessage(ValueError, msg):
            usa.cities.get_prefetch_querysets(
                instances=countries,
                querysets=[City.objects.all(), City.objects.all()],
            )