123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- from django.core import checks
- from django.db import connection, models
- from django.db.models import F
- from django.test import TestCase
- from django.test.utils import isolate_apps
- @isolate_apps("composite_pk")
- class CompositePKChecksTests(TestCase):
- maxDiff = None
- def test_composite_pk_must_be_unique_strings(self):
- test_cases = (
- (),
- (0,),
- (1,),
- ("id", False),
- ("id", "id"),
- (("id",),),
- )
- for i, args in enumerate(test_cases):
- with (
- self.subTest(args=args),
- self.assertRaisesMessage(
- ValueError, "CompositePrimaryKey args must be unique strings."
- ),
- ):
- models.CompositePrimaryKey(*args)
- def test_composite_pk_must_include_at_least_2_fields(self):
- expected_message = "CompositePrimaryKey must include at least two fields."
- with self.assertRaisesMessage(ValueError, expected_message):
- models.CompositePrimaryKey("id")
- def test_composite_pk_cannot_have_a_default(self):
- expected_message = "CompositePrimaryKey cannot have a default."
- with self.assertRaisesMessage(ValueError, expected_message):
- models.CompositePrimaryKey("tenant_id", "id", default=(1, 1))
- def test_composite_pk_cannot_have_a_database_default(self):
- expected_message = "CompositePrimaryKey cannot have a database default."
- with self.assertRaisesMessage(ValueError, expected_message):
- models.CompositePrimaryKey("tenant_id", "id", db_default=models.F("id"))
- def test_composite_pk_cannot_be_editable(self):
- expected_message = "CompositePrimaryKey cannot be editable."
- with self.assertRaisesMessage(ValueError, expected_message):
- models.CompositePrimaryKey("tenant_id", "id", editable=True)
- def test_composite_pk_must_be_a_primary_key(self):
- expected_message = "CompositePrimaryKey must be a primary key."
- with self.assertRaisesMessage(ValueError, expected_message):
- models.CompositePrimaryKey("tenant_id", "id", primary_key=False)
- def test_composite_pk_must_be_blank(self):
- expected_message = "CompositePrimaryKey must be blank."
- with self.assertRaisesMessage(ValueError, expected_message):
- models.CompositePrimaryKey("tenant_id", "id", blank=False)
- def test_composite_pk_must_not_have_other_pk_field(self):
- class Foo(models.Model):
- pk = models.CompositePrimaryKey("foo_id", "id")
- foo_id = models.IntegerField()
- id = models.IntegerField(primary_key=True)
- self.assertEqual(
- Foo.check(databases=self.databases),
- [
- checks.Error(
- "The model cannot have more than one field with "
- "'primary_key=True'.",
- obj=Foo,
- id="models.E026",
- ),
- ],
- )
- def test_composite_pk_cannot_include_nullable_field(self):
- class Foo(models.Model):
- pk = models.CompositePrimaryKey("foo_id", "id")
- foo_id = models.IntegerField()
- id = models.IntegerField(null=True)
- self.assertEqual(
- Foo.check(databases=self.databases),
- [
- checks.Error(
- "'id' cannot be included in the composite primary key.",
- hint="'id' field may not set 'null=True'.",
- obj=Foo,
- id="models.E042",
- ),
- ],
- )
- def test_composite_pk_can_include_fk_name(self):
- class Foo(models.Model):
- pass
- class Bar(models.Model):
- pk = models.CompositePrimaryKey("foo", "id")
- foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
- id = models.SmallIntegerField()
- self.assertEqual(Foo.check(databases=self.databases), [])
- self.assertEqual(Bar.check(databases=self.databases), [])
- def test_composite_pk_cannot_include_same_field(self):
- class Foo(models.Model):
- pass
- class Bar(models.Model):
- pk = models.CompositePrimaryKey("foo", "foo_id")
- foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
- id = models.SmallIntegerField()
- self.assertEqual(Foo.check(databases=self.databases), [])
- self.assertEqual(
- Bar.check(databases=self.databases),
- [
- checks.Error(
- "'foo_id' cannot be included in the composite primary key.",
- hint="'foo_id' and 'foo' are the same fields.",
- obj=Bar,
- id="models.E042",
- ),
- ],
- )
- def test_composite_pk_cannot_include_composite_pk_field(self):
- class Foo(models.Model):
- pk = models.CompositePrimaryKey("id", "pk")
- id = models.SmallIntegerField()
- self.assertEqual(
- Foo.check(databases=self.databases),
- [
- checks.Error(
- "'pk' cannot be included in the composite primary key.",
- hint="'pk' field has no column.",
- obj=Foo,
- id="models.E042",
- ),
- ],
- )
- def test_composite_pk_cannot_include_db_column(self):
- class Foo(models.Model):
- pk = models.CompositePrimaryKey("foo", "bar")
- foo = models.SmallIntegerField(db_column="foo_id")
- bar = models.SmallIntegerField(db_column="bar_id")
- class Bar(models.Model):
- pk = models.CompositePrimaryKey("foo_id", "bar_id")
- foo = models.SmallIntegerField(db_column="foo_id")
- bar = models.SmallIntegerField(db_column="bar_id")
- self.assertEqual(Foo.check(databases=self.databases), [])
- self.assertEqual(
- Bar.check(databases=self.databases),
- [
- checks.Error(
- "'foo_id' cannot be included in the composite primary key.",
- hint="'foo_id' is not a valid field.",
- obj=Bar,
- id="models.E042",
- ),
- checks.Error(
- "'bar_id' cannot be included in the composite primary key.",
- hint="'bar_id' is not a valid field.",
- obj=Bar,
- id="models.E042",
- ),
- ],
- )
- def test_foreign_object_can_refer_composite_pk(self):
- class Foo(models.Model):
- pass
- class Bar(models.Model):
- pk = models.CompositePrimaryKey("foo_id", "id")
- foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
- id = models.IntegerField()
- class Baz(models.Model):
- pk = models.CompositePrimaryKey("foo_id", "id")
- foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
- id = models.IntegerField()
- bar_id = models.IntegerField()
- bar = models.ForeignObject(
- Bar,
- on_delete=models.CASCADE,
- from_fields=("foo_id", "bar_id"),
- to_fields=("foo_id", "id"),
- )
- self.assertEqual(Foo.check(databases=self.databases), [])
- self.assertEqual(Bar.check(databases=self.databases), [])
- self.assertEqual(Baz.check(databases=self.databases), [])
- def test_composite_pk_must_be_named_pk(self):
- class Foo(models.Model):
- primary_key = models.CompositePrimaryKey("foo_id", "id")
- foo_id = models.IntegerField()
- id = models.IntegerField()
- self.assertEqual(
- Foo.check(databases=self.databases),
- [
- checks.Error(
- "'CompositePrimaryKey' must be named 'pk'.",
- obj=Foo._meta.get_field("primary_key"),
- id="fields.E013",
- ),
- ],
- )
- def test_composite_pk_cannot_include_generated_field(self):
- is_oracle = connection.vendor == "oracle"
- class Foo(models.Model):
- pk = models.CompositePrimaryKey("id", "foo")
- id = models.IntegerField()
- foo = models.GeneratedField(
- expression=F("id"),
- output_field=models.IntegerField(),
- db_persist=not is_oracle,
- )
- self.assertEqual(
- Foo.check(databases=self.databases),
- [
- checks.Error(
- "'foo' cannot be included in the composite primary key.",
- hint="'foo' field is a generated field.",
- obj=Foo,
- id="models.E042",
- ),
- ],
- )
|