1. from django.apps import apps
    
  2. from django.db import models
    
  3. from django.test import SimpleTestCase, TestCase
    
  4. from django.test.utils import isolate_apps
    
  5. 
    
  6. from .models import ManyToMany
    
  7. 
    
  8. 
    
  9. class ManyToManyFieldTests(SimpleTestCase):
    
  10.     def test_abstract_model_pending_operations(self):
    
  11.         """
    
  12.         Many-to-many fields declared on abstract models should not add lazy
    
  13.         relations to resolve relationship declared as string (#24215).
    
  14.         """
    
  15.         pending_ops_before = list(apps._pending_operations.items())
    
  16. 
    
  17.         class AbstractManyToManyModel(models.Model):
    
  18.             fk = models.ForeignKey("missing.FK", models.CASCADE)
    
  19. 
    
  20.             class Meta:
    
  21.                 abstract = True
    
  22. 
    
  23.         self.assertIs(AbstractManyToManyModel._meta.apps, apps)
    
  24.         self.assertEqual(
    
  25.             pending_ops_before,
    
  26.             list(apps._pending_operations.items()),
    
  27.             "Pending lookup added for a many-to-many field on an abstract model",
    
  28.         )
    
  29. 
    
  30.     @isolate_apps("model_fields", "model_fields.tests")
    
  31.     def test_abstract_model_app_relative_foreign_key(self):
    
  32.         class AbstractReferent(models.Model):
    
  33.             reference = models.ManyToManyField("Referred", through="Through")
    
  34. 
    
  35.             class Meta:
    
  36.                 app_label = "model_fields"
    
  37.                 abstract = True
    
  38. 
    
  39.         def assert_app_model_resolved(label):
    
  40.             class Referred(models.Model):
    
  41.                 class Meta:
    
  42.                     app_label = label
    
  43. 
    
  44.             class Through(models.Model):
    
  45.                 referred = models.ForeignKey("Referred", on_delete=models.CASCADE)
    
  46.                 referent = models.ForeignKey(
    
  47.                     "ConcreteReferent", on_delete=models.CASCADE
    
  48.                 )
    
  49. 
    
  50.                 class Meta:
    
  51.                     app_label = label
    
  52. 
    
  53.             class ConcreteReferent(AbstractReferent):
    
  54.                 class Meta:
    
  55.                     app_label = label
    
  56. 
    
  57.             self.assertEqual(
    
  58.                 ConcreteReferent._meta.get_field("reference").related_model, Referred
    
  59.             )
    
  60.             self.assertEqual(ConcreteReferent.reference.through, Through)
    
  61. 
    
  62.         assert_app_model_resolved("model_fields")
    
  63.         assert_app_model_resolved("tests")
    
  64. 
    
  65.     def test_invalid_to_parameter(self):
    
  66.         msg = (
    
  67.             "ManyToManyField(1) is invalid. First parameter to "
    
  68.             "ManyToManyField must be either a model, a model name, or the "
    
  69.             "string 'self'"
    
  70.         )
    
  71.         with self.assertRaisesMessage(TypeError, msg):
    
  72. 
    
  73.             class MyModel(models.Model):
    
  74.                 m2m = models.ManyToManyField(1)
    
  75. 
    
  76.     @isolate_apps("model_fields")
    
  77.     def test_through_db_table_mutually_exclusive(self):
    
  78.         class Child(models.Model):
    
  79.             pass
    
  80. 
    
  81.         class Through(models.Model):
    
  82.             referred = models.ForeignKey(Child, on_delete=models.CASCADE)
    
  83.             referent = models.ForeignKey(Child, on_delete=models.CASCADE)
    
  84. 
    
  85.         msg = "Cannot specify a db_table if an intermediary model is used."
    
  86.         with self.assertRaisesMessage(ValueError, msg):
    
  87. 
    
  88.             class MyModel(models.Model):
    
  89.                 m2m = models.ManyToManyField(
    
  90.                     Child,
    
  91.                     through="Through",
    
  92.                     db_table="custom_name",
    
  93.                 )
    
  94. 
    
  95. 
    
  96. class ManyToManyFieldDBTests(TestCase):
    
  97.     def test_value_from_object_instance_without_pk(self):
    
  98.         obj = ManyToMany()
    
  99.         self.assertEqual(obj._meta.get_field("m2m").value_from_object(obj), [])
    
  100. 
    
  101.     def test_value_from_object_instance_with_pk(self):
    
  102.         obj = ManyToMany.objects.create()
    
  103.         related_obj = ManyToMany.objects.create()
    
  104.         obj.m2m.add(related_obj)
    
  105.         self.assertEqual(
    
  106.             obj._meta.get_field("m2m").value_from_object(obj), [related_obj]
    
  107.         )