text: from decimal import Decimal
  
  from django.apps import apps
  from django.core import checks
  from django.core.exceptions import FieldError
  from django.db import models
  from django.test import TestCase, skipIfDBFeature
  from django.test.utils import isolate_apps
  
  from .models import Bar, FkToChar, Foo, PrimaryKeyCharModel
  
  
  class ForeignKeyTests(TestCase):
      def test_callable_default(self):
          """A lazy callable may be used for ForeignKey.default."""
          a = Foo.objects.create(id=1, a="abc", d=Decimal("12.34"))
          b = Bar.objects.create(b="bcd")
          self.assertEqual(b.a, a)
  
      @skipIfDBFeature("interprets_empty_strings_as_nulls")
      def test_empty_string_fk(self):
          """
          Empty strings foreign key values don't get converted to None (#19299).
          """
          char_model_empty = PrimaryKeyCharModel.objects.create(string="")
          fk_model_empty = FkToChar.objects.create(out=char_model_empty)
          fk_model_empty = FkToChar.objects.select_related("out").get(
              id=fk_model_empty.pk
          )
          self.assertEqual(fk_model_empty.out, char_model_empty)
  
      @isolate_apps("model_fields")
      def test_warning_when_unique_true_on_fk(self):
          class Foo(models.Model):
              pass
  
          class FKUniqueTrue(models.Model):
              fk_field = models.ForeignKey(Foo, models.CASCADE, unique=True)
  
          model = FKUniqueTrue()
          expected_warnings = [
              checks.Warning(
                  "Setting unique=True on a ForeignKey has the same effect as using a OneToOneField.",
                  hint="ForeignKey(unique=True) is usually better served by a OneToOneField.",
                  obj=FKUniqueTrue.fk_field.field,
                  id="fields.W342",
              )
          ]
          warnings = model.check()
          self.assertEqual(warnings, expected_warnings)
  
      def test_related_name_converted_to_text(self):
          rel_name = Bar._meta.get_field("a").remote_field.related_name
          self.assertIsInstance(rel_name, str)
  
      def test_abstract_model_pending_operations(self):
          """
          Foreign key 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 AbstractForeignKeyModel(models.Model):
              fk = models.ForeignKey("missing.FK", models.CASCADE)
  
              class Meta:
                  abstract = True
  
          self.assertIs(AbstractForeignKeyModel._meta.apps, apps)
          self.assertEqual(
              pending_ops_before,
              list(apps._pending_operations.items()),
              "Pending lookup added for a foreign key 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.ForeignKey("Referred", on_delete=models.CASCADE)
  
              class Meta:
                  app_label = "model_fields"
                  abstract = True
  
          def assert_app_model_resolved(label):
              class Referred(models.Model):
                  class Meta:
                      app_label = label
  
              class ConcreteReferent(AbstractReferent):
                  class Meta:
                      app_label = label
  
              self.assertEqual(
                  ConcreteReferent._meta.get_field("reference").related_model, Referred
              )
  
          assert_app_model_resolved("model_fields")
          assert_app_model_resolved("tests")
  
      @isolate_apps("model_fields")
      def test_to_python(self):
          class Foo(models.Model):
              pass
  
          class Bar(models.Model):
              fk = models.ForeignKey(Foo, models.CASCADE)
  
          self.assertEqual(Bar._meta.get_field("fk").to_python("1"), 1)
  
      @isolate_apps("model_fields")
      def test_fk_to_fk_get_col_output_field(self):
          class Foo(models.Model):
              pass
  
          class Bar(models.Model):
              foo = models.ForeignKey(Foo, models.CASCADE, primary_key=True)
  
          class Baz(models.Model):
              bar = models.ForeignKey(Bar, models.CASCADE, primary_key=True)
  
          col = Baz._meta.get_field("bar").get_col("alias")
          self.assertIs(col.output_field, Foo._meta.pk)
  
      @isolate_apps("model_fields")
      def test_recursive_fks_get_col(self):
          class Foo(models.Model):
              bar = models.ForeignKey("Bar", models.CASCADE, primary_key=True)
  
          class Bar(models.Model):
              foo = models.ForeignKey(Foo, models.CASCADE, primary_key=True)
  
          with self.assertRaisesMessage(ValueError, "Cannot resolve output_field"):
              Foo._meta.get_field("bar").get_col("alias")
  
      @isolate_apps("model_fields")
      def test_non_local_to_field(self):
          class Parent(models.Model):
              key = models.IntegerField(unique=True)
  
          class Child(Parent):
              pass
  
          class Related(models.Model):
              child = models.ForeignKey(Child, on_delete=models.CASCADE, to_field="key")
  
          msg = (
              "'model_fields.Related.child' refers to field 'key' which is not "
              "local to model 'model_fields.Child'."
          )
          with self.assertRaisesMessage(FieldError, msg):
              Related._meta.get_field("child").related_fields
  
      def test_invalid_to_parameter(self):
          msg = (
              "ForeignKey(1) is invalid. First parameter to ForeignKey must be "
              "either a model, a model name, or the string 'self'"
          )
          with self.assertRaisesMessage(TypeError, msg):
  
              class MyModel(models.Model):
                  child = models.ForeignKey(1, models.CASCADE)
  