55from django .db import connection , DatabaseError , IntegrityError , OperationalError
66from django .db .models .fields import (BinaryField , BooleanField , CharField , IntegerField ,
77 PositiveIntegerField , SlugField , TextField )
8- from django .db .models .fields .related import ManyToManyField , ForeignKey
8+ from django .db .models .fields .related import ForeignKey , ManyToManyField , OneToOneField
99from django .db .transaction import atomic
1010from .models import (Author , AuthorWithDefaultHeight , AuthorWithM2M , Book , BookWithLongName ,
1111 BookWithSlug , BookWithM2M , Tag , TagIndexed , TagM2MTest , TagUniqueRename ,
1212 UniqueTest , Thing , TagThrough , BookWithM2MThrough , AuthorTag , AuthorWithM2MThrough ,
13- AuthorWithEvenLongerName , BookWeak , Note )
13+ AuthorWithEvenLongerName , BookWeak , Note , BookWithO2O )
1414
1515
1616class SchemaTests (TransactionTestCase ):
@@ -28,7 +28,7 @@ class SchemaTests(TransactionTestCase):
2828 Author , AuthorWithM2M , Book , BookWithLongName , BookWithSlug ,
2929 BookWithM2M , Tag , TagIndexed , TagM2MTest , TagUniqueRename , UniqueTest ,
3030 Thing , TagThrough , BookWithM2MThrough , AuthorWithEvenLongerName ,
31- BookWeak ,
31+ BookWeak , BookWithO2O ,
3232 ]
3333
3434 # Utility functions
@@ -528,6 +528,106 @@ def test_alter_fk(self):
528528 else :
529529 self .fail ("No FK constraint for author_id found" )
530530
531+ @unittest .skipUnless (connection .features .supports_foreign_keys , "No FK support" )
532+ def test_alter_o2o_to_fk (self ):
533+ """
534+ #24163 - Tests altering of OneToOne to FK
535+ """
536+ # Create the table
537+ with connection .schema_editor () as editor :
538+ editor .create_model (Author )
539+ editor .create_model (BookWithO2O )
540+ # Ensure the field is right to begin with
541+ columns = self .column_classes (BookWithO2O )
542+ self .assertEqual (columns ['author_id' ][0 ], "IntegerField" )
543+ # Make sure the FK and unique constraints are present
544+ constraints = self .get_constraints (BookWithO2O ._meta .db_table )
545+ author_is_fk = False
546+ author_is_unique = False
547+ for name , details in constraints .items ():
548+ if details ['columns' ] == ['author_id' ]:
549+ if details ['foreign_key' ] and details ['foreign_key' ] == ('schema_author' , 'id' ):
550+ author_is_fk = True
551+ if details ['unique' ]:
552+ author_is_unique = True
553+ self .assertTrue (author_is_fk , "No FK constraint for author_id found" )
554+ self .assertTrue (author_is_unique , "No unique constraint for author_id found" )
555+ # Alter the O2O to FK
556+ new_field = ForeignKey (Author )
557+ new_field .set_attributes_from_name ("author" )
558+ with connection .schema_editor () as editor :
559+ editor .alter_field (
560+ BookWithO2O ,
561+ BookWithO2O ._meta .get_field ("author" ),
562+ new_field ,
563+ strict = True ,
564+ )
565+ # Ensure the field is right afterwards
566+ columns = self .column_classes (Book )
567+ self .assertEqual (columns ['author_id' ][0 ], "IntegerField" )
568+ # Make sure the FK constraint is present and unique constraint is absent
569+ constraints = self .get_constraints (Book ._meta .db_table )
570+ author_is_fk = False
571+ author_is_unique = True
572+ for name , details in constraints .items ():
573+ if details ['columns' ] == ['author_id' ]:
574+ if details ['foreign_key' ] and details ['foreign_key' ] == ('schema_author' , 'id' ):
575+ author_is_fk = True
576+ if not details ['unique' ]:
577+ author_is_unique = False
578+ self .assertTrue (author_is_fk , "No FK constraint for author_id found" )
579+ self .assertFalse (author_is_unique , "Unique constraint for author_id found" )
580+
581+ @unittest .skipUnless (connection .features .supports_foreign_keys , "No FK support" )
582+ def test_alter_fk_to_o2o (self ):
583+ """
584+ #24163 - Tests altering of FK to OneToOne
585+ """
586+ # Create the table
587+ with connection .schema_editor () as editor :
588+ editor .create_model (Author )
589+ editor .create_model (Book )
590+ # Ensure the field is right to begin with
591+ columns = self .column_classes (Book )
592+ self .assertEqual (columns ['author_id' ][0 ], "IntegerField" )
593+ # Make sure the FK constraint is present and unique constraint is absent
594+ constraints = self .get_constraints (Book ._meta .db_table )
595+ author_is_fk = False
596+ author_is_unique = True
597+ for name , details in constraints .items ():
598+ if details ['columns' ] == ['author_id' ]:
599+ if details ['foreign_key' ] and details ['foreign_key' ] == ('schema_author' , 'id' ):
600+ author_is_fk = True
601+ if not details ['unique' ]:
602+ author_is_unique = False
603+ self .assertTrue (author_is_fk , "No FK constraint for author_id found" )
604+ self .assertFalse (author_is_unique , "Unique constraint for author_id found" )
605+ # Alter the O2O to FK
606+ new_field = OneToOneField (Author )
607+ new_field .set_attributes_from_name ("author" )
608+ with connection .schema_editor () as editor :
609+ editor .alter_field (
610+ Book ,
611+ Book ._meta .get_field ("author" ),
612+ new_field ,
613+ strict = True ,
614+ )
615+ # Ensure the field is right afterwards
616+ columns = self .column_classes (BookWithO2O )
617+ self .assertEqual (columns ['author_id' ][0 ], "IntegerField" )
618+ # Make sure the FK and unique constraints are present
619+ constraints = self .get_constraints (BookWithO2O ._meta .db_table )
620+ author_is_fk = False
621+ author_is_unique = False
622+ for name , details in constraints .items ():
623+ if details ['columns' ] == ['author_id' ]:
624+ if details ['foreign_key' ] and details ['foreign_key' ] == ('schema_author' , 'id' ):
625+ author_is_fk = True
626+ if details ['unique' ]:
627+ author_is_unique = True
628+ self .assertTrue (author_is_fk , "No FK constraint for author_id found" )
629+ self .assertTrue (author_is_unique , "No unique constraint for author_id found" )
630+
531631 def test_alter_implicit_id_to_explicit (self ):
532632 """
533633 Should be able to convert an implicit "id" field to an explicit "id"
0 commit comments