mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			159 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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)
 |