from django.apps import apps from django.db import models from django.test import SimpleTestCase, TestCase from django.test.utils import isolate_apps from .models import ManyToMany class ManyToManyFieldTests(SimpleTestCase): def test_abstract_model_pending_operations(self): """ Many-to-many fields declared on abstract models should not add lazy relations to resolve relationship declared as string (#24215). """ pending_ops_before = list(apps._pending_operations.items()) class AbstractManyToManyModel(models.Model): fk = models.ForeignKey("missing.FK", models.CASCADE) class Meta: abstract = True self.assertIs(AbstractManyToManyModel._meta.apps, apps) self.assertEqual( pending_ops_before, list(apps._pending_operations.items()), "Pending lookup added for a many-to-many field on an abstract model", ) @isolate_apps("model_fields", "model_fields.tests") def test_abstract_model_app_relative_foreign_key(self): class AbstractReferent(models.Model): reference = models.ManyToManyField("Referred", through="Through") class Meta: app_label = "model_fields" abstract = True def assert_app_model_resolved(label): class Referred(models.Model): class Meta: app_label = label class Through(models.Model): referred = models.ForeignKey("Referred", on_delete=models.CASCADE) referent = models.ForeignKey( "ConcreteReferent", on_delete=models.CASCADE ) class Meta: app_label = label class ConcreteReferent(AbstractReferent): class Meta: app_label = label self.assertEqual( ConcreteReferent._meta.get_field("reference").related_model, Referred ) self.assertEqual(ConcreteReferent.reference.through, Through) assert_app_model_resolved("model_fields") assert_app_model_resolved("tests") def test_invalid_to_parameter(self): msg = ( "ManyToManyField(1) is invalid. First parameter to " "ManyToManyField must be either a model, a model name, or the " "string 'self'" ) with self.assertRaisesMessage(TypeError, msg): class MyModel(models.Model): m2m = models.ManyToManyField(1) @isolate_apps("model_fields") def test_through_db_table_mutually_exclusive(self): class Child(models.Model): pass class Through(models.Model): referred = models.ForeignKey(Child, on_delete=models.CASCADE) referent = models.ForeignKey(Child, on_delete=models.CASCADE) msg = "Cannot specify a db_table if an intermediary model is used." with self.assertRaisesMessage(ValueError, msg): class MyModel(models.Model): m2m = models.ManyToManyField( Child, through="Through", db_table="custom_name", ) class ManyToManyFieldDBTests(TestCase): def test_value_from_object_instance_without_pk(self): obj = ManyToMany() self.assertEqual(obj._meta.get_field("m2m").value_from_object(obj), []) def test_value_from_object_instance_with_pk(self): obj = ManyToMany.objects.create() related_obj = ManyToMany.objects.create() obj.m2m.add(related_obj) self.assertEqual( obj._meta.get_field("m2m").value_from_object(obj), [related_obj] )