1. from django.test import TestCase
    
  2. 
    
  3. from .models import Article, Car, Driver, Reporter
    
  4. 
    
  5. 
    
  6. class ManyToOneNullTests(TestCase):
    
  7.     @classmethod
    
  8.     def setUpTestData(cls):
    
  9.         # Create a Reporter.
    
  10.         cls.r = Reporter(name="John Smith")
    
  11.         cls.r.save()
    
  12.         # Create an Article.
    
  13.         cls.a = Article(headline="First", reporter=cls.r)
    
  14.         cls.a.save()
    
  15.         # Create an Article via the Reporter object.
    
  16.         cls.a2 = cls.r.article_set.create(headline="Second")
    
  17.         # Create an Article with no Reporter by passing "reporter=None".
    
  18.         cls.a3 = Article(headline="Third", reporter=None)
    
  19.         cls.a3.save()
    
  20.         # Create another article and reporter
    
  21.         cls.r2 = Reporter(name="Paul Jones")
    
  22.         cls.r2.save()
    
  23.         cls.a4 = cls.r2.article_set.create(headline="Fourth")
    
  24. 
    
  25.     def test_get_related(self):
    
  26.         self.assertEqual(self.a.reporter.id, self.r.id)
    
  27.         # Article objects have access to their related Reporter objects.
    
  28.         r = self.a.reporter
    
  29.         self.assertEqual(r.id, self.r.id)
    
  30. 
    
  31.     def test_created_via_related_set(self):
    
  32.         self.assertEqual(self.a2.reporter.id, self.r.id)
    
  33. 
    
  34.     def test_related_set(self):
    
  35.         # Reporter objects have access to their related Article objects.
    
  36.         self.assertSequenceEqual(self.r.article_set.all(), [self.a, self.a2])
    
  37.         self.assertSequenceEqual(
    
  38.             self.r.article_set.filter(headline__startswith="Fir"), [self.a]
    
  39.         )
    
  40.         self.assertEqual(self.r.article_set.count(), 2)
    
  41. 
    
  42.     def test_created_without_related(self):
    
  43.         self.assertIsNone(self.a3.reporter)
    
  44.         # Need to reget a3 to refresh the cache
    
  45.         a3 = Article.objects.get(pk=self.a3.pk)
    
  46.         with self.assertRaises(AttributeError):
    
  47.             getattr(a3.reporter, "id")
    
  48.         # Accessing an article's 'reporter' attribute returns None
    
  49.         # if the reporter is set to None.
    
  50.         self.assertIsNone(a3.reporter)
    
  51.         # To retrieve the articles with no reporters set, use "reporter__isnull=True".
    
  52.         self.assertSequenceEqual(
    
  53.             Article.objects.filter(reporter__isnull=True), [self.a3]
    
  54.         )
    
  55.         # We can achieve the same thing by filtering for the case where the
    
  56.         # reporter is None.
    
  57.         self.assertSequenceEqual(Article.objects.filter(reporter=None), [self.a3])
    
  58.         # Set the reporter for the Third article
    
  59.         self.assertSequenceEqual(self.r.article_set.all(), [self.a, self.a2])
    
  60.         self.r.article_set.add(a3)
    
  61.         self.assertSequenceEqual(
    
  62.             self.r.article_set.all(),
    
  63.             [self.a, self.a2, self.a3],
    
  64.         )
    
  65.         # Remove an article from the set, and check that it was removed.
    
  66.         self.r.article_set.remove(a3)
    
  67.         self.assertSequenceEqual(self.r.article_set.all(), [self.a, self.a2])
    
  68.         self.assertSequenceEqual(
    
  69.             Article.objects.filter(reporter__isnull=True), [self.a3]
    
  70.         )
    
  71. 
    
  72.     def test_remove_from_wrong_set(self):
    
  73.         self.assertSequenceEqual(self.r2.article_set.all(), [self.a4])
    
  74.         # Try to remove a4 from a set it does not belong to
    
  75.         with self.assertRaises(Reporter.DoesNotExist):
    
  76.             self.r.article_set.remove(self.a4)
    
  77.         self.assertSequenceEqual(self.r2.article_set.all(), [self.a4])
    
  78. 
    
  79.     def test_set(self):
    
  80.         # Use manager.set() to allocate ForeignKey. Null is legal, so existing
    
  81.         # members of the set that are not in the assignment set are set to null.
    
  82.         self.r2.article_set.set([self.a2, self.a3])
    
  83.         self.assertSequenceEqual(self.r2.article_set.all(), [self.a2, self.a3])
    
  84.         # Use manager.set(clear=True)
    
  85.         self.r2.article_set.set([self.a3, self.a4], clear=True)
    
  86.         self.assertSequenceEqual(self.r2.article_set.all(), [self.a4, self.a3])
    
  87.         # Clear the rest of the set
    
  88.         self.r2.article_set.set([])
    
  89.         self.assertSequenceEqual(self.r2.article_set.all(), [])
    
  90.         self.assertSequenceEqual(
    
  91.             Article.objects.filter(reporter__isnull=True),
    
  92.             [self.a4, self.a2, self.a3],
    
  93.         )
    
  94. 
    
  95.     def test_set_clear_non_bulk(self):
    
  96.         # 2 queries for clear(), 1 for add(), and 1 to select objects.
    
  97.         with self.assertNumQueries(4):
    
  98.             self.r.article_set.set([self.a], bulk=False, clear=True)
    
  99. 
    
  100.     def test_assign_clear_related_set(self):
    
  101.         # Use descriptor assignment to allocate ForeignKey. Null is legal, so
    
  102.         # existing members of the set that are not in the assignment set are
    
  103.         # set to null.
    
  104.         self.r2.article_set.set([self.a2, self.a3])
    
  105.         self.assertSequenceEqual(self.r2.article_set.all(), [self.a2, self.a3])
    
  106.         # Clear the rest of the set
    
  107.         self.r.article_set.clear()
    
  108.         self.assertSequenceEqual(self.r.article_set.all(), [])
    
  109.         self.assertSequenceEqual(
    
  110.             Article.objects.filter(reporter__isnull=True),
    
  111.             [self.a, self.a4],
    
  112.         )
    
  113. 
    
  114.     def test_assign_with_queryset(self):
    
  115.         # Querysets used in reverse FK assignments are pre-evaluated
    
  116.         # so their value isn't affected by the clearing operation in
    
  117.         # RelatedManager.set() (#19816).
    
  118.         self.r2.article_set.set([self.a2, self.a3])
    
  119. 
    
  120.         qs = self.r2.article_set.filter(headline="Second")
    
  121.         self.r2.article_set.set(qs)
    
  122. 
    
  123.         self.assertEqual(1, self.r2.article_set.count())
    
  124.         self.assertEqual(1, qs.count())
    
  125. 
    
  126.     def test_add_efficiency(self):
    
  127.         r = Reporter.objects.create()
    
  128.         articles = []
    
  129.         for _ in range(3):
    
  130.             articles.append(Article.objects.create())
    
  131.         with self.assertNumQueries(1):
    
  132.             r.article_set.add(*articles)
    
  133.         self.assertEqual(r.article_set.count(), 3)
    
  134. 
    
  135.     def test_clear_efficiency(self):
    
  136.         r = Reporter.objects.create()
    
  137.         for _ in range(3):
    
  138.             r.article_set.create()
    
  139.         with self.assertNumQueries(1):
    
  140.             r.article_set.clear()
    
  141.         self.assertEqual(r.article_set.count(), 0)
    
  142. 
    
  143.     def test_related_null_to_field(self):
    
  144.         c1 = Car.objects.create()
    
  145.         d1 = Driver.objects.create()
    
  146.         self.assertIs(d1.car, None)
    
  147.         with self.assertNumQueries(0):
    
  148.             self.assertEqual(list(c1.drivers.all()), [])
    
  149. 
    
  150.     def test_unsaved(self):
    
  151.         msg = (
    
  152.             "'Car' instance needs to have a primary key value before this relationship "
    
  153.             "can be used."
    
  154.         )
    
  155.         with self.assertRaisesMessage(ValueError, msg):
    
  156.             Car(make="Ford").drivers.all()
    
  157. 
    
  158.     def test_related_null_to_field_related_managers(self):
    
  159.         car = Car.objects.create(make=None)
    
  160.         driver = Driver.objects.create()
    
  161.         msg = (
    
  162.             f'"{car!r}" needs to have a value for field "make" before this '
    
  163.             f"relationship can be used."
    
  164.         )
    
  165.         with self.assertRaisesMessage(ValueError, msg):
    
  166.             car.drivers.add(driver)
    
  167.         with self.assertRaisesMessage(ValueError, msg):
    
  168.             car.drivers.create()
    
  169.         with self.assertRaisesMessage(ValueError, msg):
    
  170.             car.drivers.get_or_create()
    
  171.         with self.assertRaisesMessage(ValueError, msg):
    
  172.             car.drivers.update_or_create()
    
  173.         with self.assertRaisesMessage(ValueError, msg):
    
  174.             car.drivers.remove(driver)
    
  175.         with self.assertRaisesMessage(ValueError, msg):
    
  176.             car.drivers.clear()
    
  177.         with self.assertRaisesMessage(ValueError, msg):
    
  178.             car.drivers.set([driver])
    
  179. 
    
  180.         with self.assertNumQueries(0):
    
  181.             self.assertEqual(car.drivers.count(), 0)