|
1 | 1 | import itertools |
2 | 2 |
|
3 | 3 | from django.db import connection, models |
| 4 | +from django.db.models.expressions import F |
4 | 5 | from django.test import TransactionTestCase, skipUnlessDBFeature |
5 | 6 | from django.test.utils import isolate_apps |
6 | 7 |
|
@@ -519,6 +520,167 @@ class Meta: |
519 | 520 | self.assertTableNotExists(Author) |
520 | 521 |
|
521 | 522 |
|
| 523 | +class EmbeddedModelsTopLevelIndexTest(TestMixin, TransactionTestCase): |
| 524 | + @isolate_apps("schema_") |
| 525 | + def test_unique_together(self): |
| 526 | + """Meta.unique_together defined at the top-level for embedded fields.""" |
| 527 | + |
| 528 | + class Address(EmbeddedModel): |
| 529 | + unique_together_one = models.CharField(max_length=10) |
| 530 | + unique_together_two = models.CharField(max_length=10) |
| 531 | + |
| 532 | + class Meta: |
| 533 | + app_label = "schema_" |
| 534 | + |
| 535 | + class Author(EmbeddedModel): |
| 536 | + address = EmbeddedModelField(Address) |
| 537 | + unique_together_three = models.CharField(max_length=10) |
| 538 | + unique_together_four = models.CharField(max_length=10) |
| 539 | + |
| 540 | + class Meta: |
| 541 | + app_label = "schema_" |
| 542 | + |
| 543 | + class Book(models.Model): |
| 544 | + author = EmbeddedModelField(Author) |
| 545 | + |
| 546 | + class Meta: |
| 547 | + app_label = "schema_" |
| 548 | + constraints = [ |
| 549 | + models.UniqueConstraint( |
| 550 | + F("author__unique_together_three").asc(), |
| 551 | + F("author__unique_together_four").desc(), |
| 552 | + name="unique_together_34", |
| 553 | + ), |
| 554 | + ( |
| 555 | + models.UniqueConstraint( |
| 556 | + F("author__address__unique_together_one"), |
| 557 | + F("author__address__unique_together_two").asc(), |
| 558 | + name="unique_together_12", |
| 559 | + ) |
| 560 | + ), |
| 561 | + ] |
| 562 | + |
| 563 | + with connection.schema_editor() as editor: |
| 564 | + editor.create_model(Book) |
| 565 | + self.assertTableExists(Book) |
| 566 | + # Embedded uniques are created from top-level definition. |
| 567 | + self.assertEqual( |
| 568 | + self.get_constraints_for_columns( |
| 569 | + Book, ["author.unique_together_three", "author.unique_together_four"] |
| 570 | + ), |
| 571 | + ["unique_together_34"], |
| 572 | + ) |
| 573 | + self.assertEqual( |
| 574 | + self.get_constraints_for_columns( |
| 575 | + Book, |
| 576 | + ["author.address.unique_together_one", "author.address.unique_together_two"], |
| 577 | + ), |
| 578 | + ["unique_together_12"], |
| 579 | + ) |
| 580 | + editor.delete_model(Book) |
| 581 | + self.assertTableNotExists(Book) |
| 582 | + |
| 583 | + @isolate_apps("schema_") |
| 584 | + def test_add_remove_field_indexes(self): |
| 585 | + """AddField/RemoveField + EmbeddedModelField + Meta.indexes at top-level.""" |
| 586 | + |
| 587 | + class Address(EmbeddedModel): |
| 588 | + indexed_one = models.CharField(max_length=10) |
| 589 | + |
| 590 | + class Meta: |
| 591 | + app_label = "schema_" |
| 592 | + |
| 593 | + class Author(EmbeddedModel): |
| 594 | + address = EmbeddedModelField(Address) |
| 595 | + indexed_two = models.CharField(max_length=10) |
| 596 | + |
| 597 | + class Meta: |
| 598 | + app_label = "schema_" |
| 599 | + |
| 600 | + class Book(models.Model): |
| 601 | + author = EmbeddedModelField(Author) |
| 602 | + |
| 603 | + class Meta: |
| 604 | + app_label = "schema_" |
| 605 | + indexes = [ |
| 606 | + models.Index(F("author__indexed_two").asc(), name="indexed_two"), |
| 607 | + models.Index(F("author__address__indexed_one").asc(), name="indexed_one"), |
| 608 | + ] |
| 609 | + |
| 610 | + new_field = EmbeddedModelField(Author) |
| 611 | + new_field.set_attributes_from_name("author") |
| 612 | + |
| 613 | + with connection.schema_editor() as editor: |
| 614 | + # Create the table and add the field. |
| 615 | + editor.create_model(Book) |
| 616 | + editor.add_field(Book, new_field) |
| 617 | + # Embedded indexes are created. |
| 618 | + self.assertEqual( |
| 619 | + self.get_constraints_for_columns(Book, ["author.indexed_two"]), |
| 620 | + ["indexed_two"], |
| 621 | + ) |
| 622 | + self.assertEqual( |
| 623 | + self.get_constraints_for_columns( |
| 624 | + Book, |
| 625 | + ["author.address.indexed_one"], |
| 626 | + ), |
| 627 | + ["indexed_one"], |
| 628 | + ) |
| 629 | + editor.delete_model(Book) |
| 630 | + self.assertTableNotExists(Book) |
| 631 | + |
| 632 | + @isolate_apps("schema_") |
| 633 | + def test_add_remove_field_constraints(self): |
| 634 | + """AddField/RemoveField + EmbeddedModelField + Meta.constraints at top-level.""" |
| 635 | + |
| 636 | + class Address(EmbeddedModel): |
| 637 | + unique_constraint_one = models.CharField(max_length=10) |
| 638 | + |
| 639 | + class Meta: |
| 640 | + app_label = "schema_" |
| 641 | + |
| 642 | + class Author(EmbeddedModel): |
| 643 | + address = EmbeddedModelField(Address) |
| 644 | + unique_constraint_two = models.CharField(max_length=10) |
| 645 | + |
| 646 | + class Meta: |
| 647 | + app_label = "schema_" |
| 648 | + |
| 649 | + class Book(models.Model): |
| 650 | + author = EmbeddedModelField(Author) |
| 651 | + |
| 652 | + class Meta: |
| 653 | + app_label = "schema_" |
| 654 | + constraints = [ |
| 655 | + models.UniqueConstraint(F("author__unique_constraint_two"), name="unique_two"), |
| 656 | + models.UniqueConstraint( |
| 657 | + F("author__address__unique_constraint_one"), name="unique_one" |
| 658 | + ), |
| 659 | + ] |
| 660 | + |
| 661 | + new_field = EmbeddedModelField(Author) |
| 662 | + new_field.set_attributes_from_name("author") |
| 663 | + |
| 664 | + with connection.schema_editor() as editor: |
| 665 | + # Create the table and add the field. |
| 666 | + editor.create_model(Book) |
| 667 | + editor.add_field(Book, new_field) |
| 668 | + # Embedded constraints are created. |
| 669 | + self.assertEqual( |
| 670 | + self.get_constraints_for_columns(Book, ["author.unique_constraint_two"]), |
| 671 | + ["unique_two"], |
| 672 | + ) |
| 673 | + self.assertEqual( |
| 674 | + self.get_constraints_for_columns( |
| 675 | + Book, |
| 676 | + ["author.address.unique_constraint_one"], |
| 677 | + ), |
| 678 | + ["unique_one"], |
| 679 | + ) |
| 680 | + editor.delete_model(Book) |
| 681 | + self.assertTableNotExists(Book) |
| 682 | + |
| 683 | + |
522 | 684 | class EmbeddedModelsIgnoredTests(TestMixin, TransactionTestCase): |
523 | 685 | def test_embedded_not_created(self): |
524 | 686 | """create_model() and delete_model() ignore EmbeddedModel.""" |
|
0 commit comments