2016-03-22 09:06:54 +08:00
|
|
|
from django.apps import apps
|
|
|
|
from django.db import models
|
2017-08-31 21:34:44 +08:00
|
|
|
from django.test import SimpleTestCase, TestCase
|
2016-03-22 09:06:54 +08:00
|
|
|
from django.test.utils import isolate_apps
|
|
|
|
|
2017-08-31 21:34:44 +08:00
|
|
|
from .models import ManyToMany
|
2016-03-22 09:06:54 +08:00
|
|
|
|
2016-05-29 03:16:44 +08:00
|
|
|
|
2017-08-31 21:34:44 +08:00
|
|
|
class ManyToManyFieldTests(SimpleTestCase):
|
2016-03-22 09:06:54 +08:00
|
|
|
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):
|
2017-01-19 18:00:41 +08:00
|
|
|
reference = models.ManyToManyField("Referred", through="Through")
|
2016-03-22 09:06:54 +08:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
app_label = "model_fields"
|
|
|
|
abstract = True
|
|
|
|
|
|
|
|
def assert_app_model_resolved(label):
|
2017-01-19 18:00:41 +08:00
|
|
|
class Referred(models.Model):
|
2016-03-22 09:06:54 +08:00
|
|
|
class Meta:
|
|
|
|
app_label = label
|
|
|
|
|
|
|
|
class Through(models.Model):
|
2017-01-19 18:00:41 +08:00
|
|
|
referred = models.ForeignKey("Referred", on_delete=models.CASCADE)
|
2016-03-22 09:06:54 +08:00
|
|
|
referent = models.ForeignKey(
|
|
|
|
"ConcreteReferent", on_delete=models.CASCADE
|
|
|
|
)
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
app_label = label
|
|
|
|
|
|
|
|
class ConcreteReferent(AbstractReferent):
|
|
|
|
class Meta:
|
|
|
|
app_label = label
|
|
|
|
|
2017-01-19 18:00:41 +08:00
|
|
|
self.assertEqual(
|
|
|
|
ConcreteReferent._meta.get_field("reference").related_model, Referred
|
|
|
|
)
|
2016-03-22 09:06:54 +08:00
|
|
|
self.assertEqual(ConcreteReferent.reference.through, Through)
|
|
|
|
|
|
|
|
assert_app_model_resolved("model_fields")
|
|
|
|
assert_app_model_resolved("tests")
|
2017-08-31 21:34:44 +08:00
|
|
|
|
2021-03-24 13:45:08 +08:00
|
|
|
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):
|
2022-02-04 03:24:19 +08:00
|
|
|
|
2021-03-24 13:45:08 +08:00
|
|
|
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):
|
2022-02-04 03:24:19 +08:00
|
|
|
|
2021-03-24 13:45:08 +08:00
|
|
|
class MyModel(models.Model):
|
|
|
|
m2m = models.ManyToManyField(
|
|
|
|
Child,
|
|
|
|
through="Through",
|
|
|
|
db_table="custom_name",
|
|
|
|
)
|
|
|
|
|
2017-08-31 21:34:44 +08:00
|
|
|
|
|
|
|
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]
|
|
|
|
)
|