1. from decimal import Decimal
    
  2. 
    
  3. from django.apps import apps
    
  4. from django.core import checks
    
  5. from django.core.exceptions import FieldError
    
  6. from django.db import models
    
  7. from django.test import TestCase, skipIfDBFeature
    
  8. from django.test.utils import isolate_apps
    
  9. 
    
  10. from .models import Bar, FkToChar, Foo, PrimaryKeyCharModel
    
  11. 
    
  12. 
    
  13. class ForeignKeyTests(TestCase):
    
  14.     def test_callable_default(self):
    
  15.         """A lazy callable may be used for ForeignKey.default."""
    
  16.         a = Foo.objects.create(id=1, a="abc", d=Decimal("12.34"))
    
  17.         b = Bar.objects.create(b="bcd")
    
  18.         self.assertEqual(b.a, a)
    
  19. 
    
  20.     @skipIfDBFeature("interprets_empty_strings_as_nulls")
    
  21.     def test_empty_string_fk(self):
    
  22.         """
    
  23.         Empty strings foreign key values don't get converted to None (#19299).
    
  24.         """
    
  25.         char_model_empty = PrimaryKeyCharModel.objects.create(string="")
    
  26.         fk_model_empty = FkToChar.objects.create(out=char_model_empty)
    
  27.         fk_model_empty = FkToChar.objects.select_related("out").get(
    
  28.             id=fk_model_empty.pk
    
  29.         )
    
  30.         self.assertEqual(fk_model_empty.out, char_model_empty)
    
  31. 
    
  32.     @isolate_apps("model_fields")
    
  33.     def test_warning_when_unique_true_on_fk(self):
    
  34.         class Foo(models.Model):
    
  35.             pass
    
  36. 
    
  37.         class FKUniqueTrue(models.Model):
    
  38.             fk_field = models.ForeignKey(Foo, models.CASCADE, unique=True)
    
  39. 
    
  40.         model = FKUniqueTrue()
    
  41.         expected_warnings = [
    
  42.             checks.Warning(
    
  43.                 "Setting unique=True on a ForeignKey has the same effect as using a "
    
  44.                 "OneToOneField.",
    
  45.                 hint=(
    
  46.                     "ForeignKey(unique=True) is usually better served by a "
    
  47.                     "OneToOneField."
    
  48.                 ),
    
  49.                 obj=FKUniqueTrue.fk_field.field,
    
  50.                 id="fields.W342",
    
  51.             )
    
  52.         ]
    
  53.         warnings = model.check()
    
  54.         self.assertEqual(warnings, expected_warnings)
    
  55. 
    
  56.     def test_related_name_converted_to_text(self):
    
  57.         rel_name = Bar._meta.get_field("a").remote_field.related_name
    
  58.         self.assertIsInstance(rel_name, str)
    
  59. 
    
  60.     def test_abstract_model_pending_operations(self):
    
  61.         """
    
  62.         Foreign key fields declared on abstract models should not add lazy
    
  63.         relations to resolve relationship declared as string (#24215).
    
  64.         """
    
  65.         pending_ops_before = list(apps._pending_operations.items())
    
  66. 
    
  67.         class AbstractForeignKeyModel(models.Model):
    
  68.             fk = models.ForeignKey("missing.FK", models.CASCADE)
    
  69. 
    
  70.             class Meta:
    
  71.                 abstract = True
    
  72. 
    
  73.         self.assertIs(AbstractForeignKeyModel._meta.apps, apps)
    
  74.         self.assertEqual(
    
  75.             pending_ops_before,
    
  76.             list(apps._pending_operations.items()),
    
  77.             "Pending lookup added for a foreign key on an abstract model",
    
  78.         )
    
  79. 
    
  80.     @isolate_apps("model_fields", "model_fields.tests")
    
  81.     def test_abstract_model_app_relative_foreign_key(self):
    
  82.         class AbstractReferent(models.Model):
    
  83.             reference = models.ForeignKey("Referred", on_delete=models.CASCADE)
    
  84. 
    
  85.             class Meta:
    
  86.                 app_label = "model_fields"
    
  87.                 abstract = True
    
  88. 
    
  89.         def assert_app_model_resolved(label):
    
  90.             class Referred(models.Model):
    
  91.                 class Meta:
    
  92.                     app_label = label
    
  93. 
    
  94.             class ConcreteReferent(AbstractReferent):
    
  95.                 class Meta:
    
  96.                     app_label = label
    
  97. 
    
  98.             self.assertEqual(
    
  99.                 ConcreteReferent._meta.get_field("reference").related_model, Referred
    
  100.             )
    
  101. 
    
  102.         assert_app_model_resolved("model_fields")
    
  103.         assert_app_model_resolved("tests")
    
  104. 
    
  105.     @isolate_apps("model_fields")
    
  106.     def test_to_python(self):
    
  107.         class Foo(models.Model):
    
  108.             pass
    
  109. 
    
  110.         class Bar(models.Model):
    
  111.             fk = models.ForeignKey(Foo, models.CASCADE)
    
  112. 
    
  113.         self.assertEqual(Bar._meta.get_field("fk").to_python("1"), 1)
    
  114. 
    
  115.     @isolate_apps("model_fields")
    
  116.     def test_fk_to_fk_get_col_output_field(self):
    
  117.         class Foo(models.Model):
    
  118.             pass
    
  119. 
    
  120.         class Bar(models.Model):
    
  121.             foo = models.ForeignKey(Foo, models.CASCADE, primary_key=True)
    
  122. 
    
  123.         class Baz(models.Model):
    
  124.             bar = models.ForeignKey(Bar, models.CASCADE, primary_key=True)
    
  125. 
    
  126.         col = Baz._meta.get_field("bar").get_col("alias")
    
  127.         self.assertIs(col.output_field, Foo._meta.pk)
    
  128. 
    
  129.     @isolate_apps("model_fields")
    
  130.     def test_recursive_fks_get_col(self):
    
  131.         class Foo(models.Model):
    
  132.             bar = models.ForeignKey("Bar", models.CASCADE, primary_key=True)
    
  133. 
    
  134.         class Bar(models.Model):
    
  135.             foo = models.ForeignKey(Foo, models.CASCADE, primary_key=True)
    
  136. 
    
  137.         with self.assertRaisesMessage(ValueError, "Cannot resolve output_field"):
    
  138.             Foo._meta.get_field("bar").get_col("alias")
    
  139. 
    
  140.     @isolate_apps("model_fields")
    
  141.     def test_non_local_to_field(self):
    
  142.         class Parent(models.Model):
    
  143.             key = models.IntegerField(unique=True)
    
  144. 
    
  145.         class Child(Parent):
    
  146.             pass
    
  147. 
    
  148.         class Related(models.Model):
    
  149.             child = models.ForeignKey(Child, on_delete=models.CASCADE, to_field="key")
    
  150. 
    
  151.         msg = (
    
  152.             "'model_fields.Related.child' refers to field 'key' which is not "
    
  153.             "local to model 'model_fields.Child'."
    
  154.         )
    
  155.         with self.assertRaisesMessage(FieldError, msg):
    
  156.             Related._meta.get_field("child").related_fields
    
  157. 
    
  158.     def test_invalid_to_parameter(self):
    
  159.         msg = (
    
  160.             "ForeignKey(1) is invalid. First parameter to ForeignKey must be "
    
  161.             "either a model, a model name, or the string 'self'"
    
  162.         )
    
  163.         with self.assertRaisesMessage(TypeError, msg):
    
  164. 
    
  165.             class MyModel(models.Model):
    
  166.                 child = models.ForeignKey(1, models.CASCADE)
    
  167. 
    
  168.     def test_manager_class_getitem(self):
    
  169.         self.assertIs(models.ForeignKey["Foo"], models.ForeignKey)