How to use changes method in localstack

Best Python code snippet using localstack_python

test_autodetector.py

Source:test_autodetector.py Github

copy

Full Screen

...451 ], {452 "unique_together": {("parent", "knight")},453 "indexes": [models.Index(fields=["parent", "knight"], name='rabbit_circular_fk_index')],454 })455 def repr_changes(self, changes, include_dependencies=False):456 output = ""457 for app_label, migrations_ in sorted(changes.items()):458 output += " %s:\n" % app_label459 for migration in migrations_:460 output += " %s\n" % migration.name461 for operation in migration.operations:462 output += " %s\n" % operation463 if include_dependencies:464 output += " Dependencies:\n"465 if migration.dependencies:466 for dep in migration.dependencies:467 output += " %s\n" % (dep,)468 else:469 output += " None\n"470 return output471 def assertNumberMigrations(self, changes, app_label, number):472 if len(changes.get(app_label, [])) != number:473 self.fail("Incorrect number of migrations (%s) for %s (expected %s)\n%s" % (474 len(changes.get(app_label, [])),475 app_label,476 number,477 self.repr_changes(changes),478 ))479 def assertMigrationDependencies(self, changes, app_label, position, dependencies):480 if not changes.get(app_label):481 self.fail("No migrations found for %s\n%s" % (app_label, self.repr_changes(changes)))482 if len(changes[app_label]) < position + 1:483 self.fail("No migration at index %s for %s\n%s" % (position, app_label, self.repr_changes(changes)))484 migration = changes[app_label][position]485 if set(migration.dependencies) != set(dependencies):486 self.fail("Migration dependencies mismatch for %s.%s (expected %s):\n%s" % (487 app_label,488 migration.name,489 dependencies,490 self.repr_changes(changes, include_dependencies=True),491 ))492 def assertOperationTypes(self, changes, app_label, position, types):493 if not changes.get(app_label):494 self.fail("No migrations found for %s\n%s" % (app_label, self.repr_changes(changes)))495 if len(changes[app_label]) < position + 1:496 self.fail("No migration at index %s for %s\n%s" % (position, app_label, self.repr_changes(changes)))497 migration = changes[app_label][position]498 real_types = [operation.__class__.__name__ for operation in migration.operations]499 if types != real_types:500 self.fail("Operation type mismatch for %s.%s (expected %s):\n%s" % (501 app_label,502 migration.name,503 types,504 self.repr_changes(changes),505 ))506 def assertOperationAttributes(self, changes, app_label, position, operation_position, **attrs):507 if not changes.get(app_label):508 self.fail("No migrations found for %s\n%s" % (app_label, self.repr_changes(changes)))509 if len(changes[app_label]) < position + 1:510 self.fail("No migration at index %s for %s\n%s" % (position, app_label, self.repr_changes(changes)))511 migration = changes[app_label][position]512 if len(changes[app_label]) < position + 1:513 self.fail("No operation at index %s for %s.%s\n%s" % (514 operation_position,515 app_label,516 migration.name,517 self.repr_changes(changes),518 ))519 operation = migration.operations[operation_position]520 for attr, value in attrs.items():521 if getattr(operation, attr, None) != value:522 self.fail("Attribute mismatch for %s.%s op #%s, %s (expected %r, got %r):\n%s" % (523 app_label,524 migration.name,525 operation_position,526 attr,527 value,528 getattr(operation, attr, None),529 self.repr_changes(changes),530 ))531 def assertOperationFieldAttributes(self, changes, app_label, position, operation_position, **attrs):532 if not changes.get(app_label):533 self.fail("No migrations found for %s\n%s" % (app_label, self.repr_changes(changes)))534 if len(changes[app_label]) < position + 1:535 self.fail("No migration at index %s for %s\n%s" % (position, app_label, self.repr_changes(changes)))536 migration = changes[app_label][position]537 if len(changes[app_label]) < position + 1:538 self.fail("No operation at index %s for %s.%s\n%s" % (539 operation_position,540 app_label,541 migration.name,542 self.repr_changes(changes),543 ))544 operation = migration.operations[operation_position]545 if not hasattr(operation, 'field'):546 self.fail("No field attribute for %s.%s op #%s." % (547 app_label,548 migration.name,549 operation_position,550 ))551 field = operation.field552 for attr, value in attrs.items():553 if getattr(field, attr, None) != value:554 self.fail("Field attribute mismatch for %s.%s op #%s, field.%s (expected %r, got %r):\n%s" % (555 app_label,556 migration.name,557 operation_position,558 attr,559 value,560 getattr(field, attr, None),561 self.repr_changes(changes),562 ))563 def make_project_state(self, model_states):564 "Shortcut to make ProjectStates from lists of predefined models"565 project_state = ProjectState()566 for model_state in model_states:567 project_state.add_model(model_state.clone())568 return project_state569 def get_changes(self, before_states, after_states, questioner=None):570 return MigrationAutodetector(571 self.make_project_state(before_states),572 self.make_project_state(after_states),573 questioner,574 )._detect_changes()575 def test_arrange_for_graph(self):576 """Tests auto-naming of migrations for graph matching."""577 # Make a fake graph578 graph = MigrationGraph()579 graph.add_node(("testapp", "0001_initial"), None)580 graph.add_node(("testapp", "0002_foobar"), None)581 graph.add_node(("otherapp", "0001_initial"), None)582 graph.add_dependency("testapp.0002_foobar", ("testapp", "0002_foobar"), ("testapp", "0001_initial"))583 graph.add_dependency("testapp.0002_foobar", ("testapp", "0002_foobar"), ("otherapp", "0001_initial"))584 # Use project state to make a new migration change set585 before = self.make_project_state([])586 after = self.make_project_state([self.author_empty, self.other_pony, self.other_stable])587 autodetector = MigrationAutodetector(before, after)588 changes = autodetector._detect_changes()589 # Run through arrange_for_graph590 changes = autodetector.arrange_for_graph(changes, graph)591 # Make sure there's a new name, deps match, etc.592 self.assertEqual(changes["testapp"][0].name, "0003_author")593 self.assertEqual(changes["testapp"][0].dependencies, [("testapp", "0002_foobar")])594 self.assertEqual(changes["otherapp"][0].name, "0002_pony_stable")595 self.assertEqual(changes["otherapp"][0].dependencies, [("otherapp", "0001_initial")])596 def test_arrange_for_graph_with_multiple_initial(self):597 # Make a fake graph.598 graph = MigrationGraph()599 # Use project state to make a new migration change set.600 before = self.make_project_state([])601 after = self.make_project_state([self.author_with_book, self.book, self.attribution])602 autodetector = MigrationAutodetector(before, after, MigrationQuestioner({'ask_initial': True}))603 changes = autodetector._detect_changes()604 changes = autodetector.arrange_for_graph(changes, graph)605 self.assertEqual(changes['otherapp'][0].name, '0001_initial')606 self.assertEqual(changes['otherapp'][0].dependencies, [])607 self.assertEqual(changes['otherapp'][1].name, '0002_initial')608 self.assertCountEqual(609 changes['otherapp'][1].dependencies,610 [('testapp', '0001_initial'), ('otherapp', '0001_initial')],611 )612 self.assertEqual(changes['testapp'][0].name, '0001_initial')613 self.assertEqual(changes['testapp'][0].dependencies, [('otherapp', '0001_initial')])614 def test_trim_apps(self):615 """616 Trim does not remove dependencies but does remove unwanted apps.617 """618 # Use project state to make a new migration change set619 before = self.make_project_state([])620 after = self.make_project_state([self.author_empty, self.other_pony, self.other_stable, self.third_thing])621 autodetector = MigrationAutodetector(before, after, MigrationQuestioner({"ask_initial": True}))622 changes = autodetector._detect_changes()623 # Run through arrange_for_graph624 graph = MigrationGraph()625 changes = autodetector.arrange_for_graph(changes, graph)626 changes["testapp"][0].dependencies.append(("otherapp", "0001_initial"))627 changes = autodetector._trim_to_apps(changes, {"testapp"})628 # Make sure there's the right set of migrations629 self.assertEqual(changes["testapp"][0].name, "0001_initial")630 self.assertEqual(changes["otherapp"][0].name, "0001_initial")631 self.assertNotIn("thirdapp", changes)632 def test_custom_migration_name(self):633 """Tests custom naming of migrations for graph matching."""634 # Make a fake graph635 graph = MigrationGraph()636 graph.add_node(("testapp", "0001_initial"), None)637 graph.add_node(("testapp", "0002_foobar"), None)638 graph.add_node(("otherapp", "0001_initial"), None)639 graph.add_dependency("testapp.0002_foobar", ("testapp", "0002_foobar"), ("testapp", "0001_initial"))640 # Use project state to make a new migration change set641 before = self.make_project_state([])642 after = self.make_project_state([self.author_empty, self.other_pony, self.other_stable])643 autodetector = MigrationAutodetector(before, after)644 changes = autodetector._detect_changes()645 # Run through arrange_for_graph646 migration_name = 'custom_name'647 changes = autodetector.arrange_for_graph(changes, graph, migration_name)648 # Make sure there's a new name, deps match, etc.649 self.assertEqual(changes["testapp"][0].name, "0003_%s" % migration_name)650 self.assertEqual(changes["testapp"][0].dependencies, [("testapp", "0002_foobar")])651 self.assertEqual(changes["otherapp"][0].name, "0002_%s" % migration_name)652 self.assertEqual(changes["otherapp"][0].dependencies, [("otherapp", "0001_initial")])653 def test_new_model(self):654 """Tests autodetection of new models."""655 changes = self.get_changes([], [self.other_pony_food])656 # Right number/type of migrations?657 self.assertNumberMigrations(changes, 'otherapp', 1)658 self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"])659 self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Pony")660 self.assertEqual([name for name, mgr in changes['otherapp'][0].operations[0].managers],661 ['food_qs', 'food_mgr', 'food_mgr_kwargs'])662 def test_old_model(self):663 """Tests deletion of old models."""664 changes = self.get_changes([self.author_empty], [])665 # Right number/type of migrations?666 self.assertNumberMigrations(changes, 'testapp', 1)667 self.assertOperationTypes(changes, 'testapp', 0, ["DeleteModel"])668 self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")669 def test_add_field(self):670 """Tests autodetection of new fields."""671 changes = self.get_changes([self.author_empty], [self.author_name])672 # Right number/type of migrations?673 self.assertNumberMigrations(changes, 'testapp', 1)674 self.assertOperationTypes(changes, 'testapp', 0, ["AddField"])675 self.assertOperationAttributes(changes, "testapp", 0, 0, name="name")676 @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition',677 side_effect=AssertionError("Should not have prompted for not null addition"))678 def test_add_date_fields_with_auto_now_not_asking_for_default(self, mocked_ask_method):679 changes = self.get_changes([self.author_empty], [self.author_dates_of_birth_auto_now])680 # Right number/type of migrations?681 self.assertNumberMigrations(changes, 'testapp', 1)682 self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AddField", "AddField"])683 self.assertOperationFieldAttributes(changes, "testapp", 0, 0, auto_now=True)684 self.assertOperationFieldAttributes(changes, "testapp", 0, 1, auto_now=True)685 self.assertOperationFieldAttributes(changes, "testapp", 0, 2, auto_now=True)686 @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition',687 side_effect=AssertionError("Should not have prompted for not null addition"))688 def test_add_date_fields_with_auto_now_add_not_asking_for_null_addition(self, mocked_ask_method):689 changes = self.get_changes([self.author_empty], [self.author_dates_of_birth_auto_now_add])690 # Right number/type of migrations?691 self.assertNumberMigrations(changes, 'testapp', 1)692 self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AddField", "AddField"])693 self.assertOperationFieldAttributes(changes, "testapp", 0, 0, auto_now_add=True)694 self.assertOperationFieldAttributes(changes, "testapp", 0, 1, auto_now_add=True)695 self.assertOperationFieldAttributes(changes, "testapp", 0, 2, auto_now_add=True)696 @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_auto_now_add_addition')697 def test_add_date_fields_with_auto_now_add_asking_for_default(self, mocked_ask_method):698 changes = self.get_changes([self.author_empty], [self.author_dates_of_birth_auto_now_add])699 # Right number/type of migrations?700 self.assertNumberMigrations(changes, 'testapp', 1)701 self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AddField", "AddField"])702 self.assertOperationFieldAttributes(changes, "testapp", 0, 0, auto_now_add=True)703 self.assertOperationFieldAttributes(changes, "testapp", 0, 1, auto_now_add=True)704 self.assertOperationFieldAttributes(changes, "testapp", 0, 2, auto_now_add=True)705 self.assertEqual(mocked_ask_method.call_count, 3)706 def test_remove_field(self):707 """Tests autodetection of removed fields."""708 changes = self.get_changes([self.author_name], [self.author_empty])709 # Right number/type of migrations?710 self.assertNumberMigrations(changes, 'testapp', 1)711 self.assertOperationTypes(changes, 'testapp', 0, ["RemoveField"])712 self.assertOperationAttributes(changes, "testapp", 0, 0, name="name")713 def test_alter_field(self):714 """Tests autodetection of new fields."""715 changes = self.get_changes([self.author_name], [self.author_name_longer])716 # Right number/type of migrations?717 self.assertNumberMigrations(changes, 'testapp', 1)718 self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])719 self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=True)720 def test_supports_functools_partial(self):721 def _content_file_name(instance, filename, key, **kwargs):722 return '{}/{}'.format(instance, filename)723 def content_file_name(key, **kwargs):724 return functools.partial(_content_file_name, key, **kwargs)725 # An unchanged partial reference.726 before = [ModelState("testapp", "Author", [727 ("id", models.AutoField(primary_key=True)),728 ("file", models.FileField(max_length=200, upload_to=content_file_name('file'))),729 ])]730 after = [ModelState("testapp", "Author", [731 ("id", models.AutoField(primary_key=True)),732 ("file", models.FileField(max_length=200, upload_to=content_file_name('file'))),733 ])]734 changes = self.get_changes(before, after)735 self.assertNumberMigrations(changes, 'testapp', 0)736 # A changed partial reference.737 args_changed = [ModelState("testapp", "Author", [738 ("id", models.AutoField(primary_key=True)),739 ("file", models.FileField(max_length=200, upload_to=content_file_name('other-file'))),740 ])]741 changes = self.get_changes(before, args_changed)742 self.assertNumberMigrations(changes, 'testapp', 1)743 self.assertOperationTypes(changes, 'testapp', 0, ['AlterField'])744 # Can't use assertOperationFieldAttributes because we need the745 # deconstructed version, i.e., the exploded func/args/keywords rather746 # than the partial: we don't care if it's not the same instance of the747 # partial, only if it's the same source function, args, and keywords.748 value = changes['testapp'][0].operations[0].field.upload_to749 self.assertEqual(750 (_content_file_name, ('other-file',), {}),751 (value.func, value.args, value.keywords)752 )753 kwargs_changed = [ModelState("testapp", "Author", [754 ("id", models.AutoField(primary_key=True)),755 ("file", models.FileField(max_length=200, upload_to=content_file_name('file', spam='eggs'))),756 ])]757 changes = self.get_changes(before, kwargs_changed)758 self.assertNumberMigrations(changes, 'testapp', 1)759 self.assertOperationTypes(changes, 'testapp', 0, ['AlterField'])760 value = changes['testapp'][0].operations[0].field.upload_to761 self.assertEqual(762 (_content_file_name, ('file',), {'spam': 'eggs'}),763 (value.func, value.args, value.keywords)764 )765 @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_alteration',766 side_effect=AssertionError("Should not have prompted for not null addition"))767 def test_alter_field_to_not_null_with_default(self, mocked_ask_method):768 """769 #23609 - Tests autodetection of nullable to non-nullable alterations.770 """771 changes = self.get_changes([self.author_name_null], [self.author_name_default])772 # Right number/type of migrations?773 self.assertNumberMigrations(changes, 'testapp', 1)774 self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])775 self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=True)776 self.assertOperationFieldAttributes(changes, "testapp", 0, 0, default='Ada Lovelace')777 @mock.patch(778 'django.db.migrations.questioner.MigrationQuestioner.ask_not_null_alteration',779 return_value=models.NOT_PROVIDED,780 )781 def test_alter_field_to_not_null_without_default(self, mocked_ask_method):782 """783 #23609 - Tests autodetection of nullable to non-nullable alterations.784 """785 changes = self.get_changes([self.author_name_null], [self.author_name])786 self.assertEqual(mocked_ask_method.call_count, 1)787 # Right number/type of migrations?788 self.assertNumberMigrations(changes, 'testapp', 1)789 self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])790 self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=True)791 self.assertOperationFieldAttributes(changes, "testapp", 0, 0, default=models.NOT_PROVIDED)792 @mock.patch(793 'django.db.migrations.questioner.MigrationQuestioner.ask_not_null_alteration',794 return_value='Some Name',795 )796 def test_alter_field_to_not_null_oneoff_default(self, mocked_ask_method):797 """798 #23609 - Tests autodetection of nullable to non-nullable alterations.799 """800 changes = self.get_changes([self.author_name_null], [self.author_name])801 self.assertEqual(mocked_ask_method.call_count, 1)802 # Right number/type of migrations?803 self.assertNumberMigrations(changes, 'testapp', 1)804 self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])805 self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=False)806 self.assertOperationFieldAttributes(changes, "testapp", 0, 0, default="Some Name")807 def test_rename_field(self):808 """Tests autodetection of renamed fields."""809 changes = self.get_changes(810 [self.author_name], [self.author_name_renamed], MigrationQuestioner({"ask_rename": True})811 )812 # Right number/type of migrations?813 self.assertNumberMigrations(changes, 'testapp', 1)814 self.assertOperationTypes(changes, 'testapp', 0, ["RenameField"])815 self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name="name", new_name="names")816 def test_rename_field_foreign_key_to_field(self):817 before = [818 ModelState('app', 'Foo', [819 ('id', models.AutoField(primary_key=True)),820 ('field', models.IntegerField(unique=True)),821 ]),822 ModelState('app', 'Bar', [823 ('id', models.AutoField(primary_key=True)),824 ('foo', models.ForeignKey('app.Foo', models.CASCADE, to_field='field')),825 ]),826 ]827 after = [828 ModelState('app', 'Foo', [829 ('id', models.AutoField(primary_key=True)),830 ('renamed_field', models.IntegerField(unique=True)),831 ]),832 ModelState('app', 'Bar', [833 ('id', models.AutoField(primary_key=True)),834 ('foo', models.ForeignKey('app.Foo', models.CASCADE, to_field='renamed_field')),835 ]),836 ]837 changes = self.get_changes(before, after, MigrationQuestioner({'ask_rename': True}))838 # Right number/type of migrations?839 self.assertNumberMigrations(changes, 'app', 1)840 self.assertOperationTypes(changes, 'app', 0, ['RenameField'])841 self.assertOperationAttributes(changes, 'app', 0, 0, old_name='field', new_name='renamed_field')842 def test_rename_foreign_object_fields(self):843 fields = ('first', 'second')844 renamed_fields = ('first_renamed', 'second_renamed')845 before = [846 ModelState('app', 'Foo', [847 ('id', models.AutoField(primary_key=True)),848 ('first', models.IntegerField()),849 ('second', models.IntegerField()),850 ], options={'unique_together': {fields}}),851 ModelState('app', 'Bar', [852 ('id', models.AutoField(primary_key=True)),853 ('first', models.IntegerField()),854 ('second', models.IntegerField()),855 ('foo', models.ForeignObject(856 'app.Foo', models.CASCADE, from_fields=fields, to_fields=fields,857 )),858 ]),859 ]860 # Case 1: to_fields renames.861 after = [862 ModelState('app', 'Foo', [863 ('id', models.AutoField(primary_key=True)),864 ('first_renamed', models.IntegerField()),865 ('second_renamed', models.IntegerField()),866 ], options={'unique_together': {renamed_fields}}),867 ModelState('app', 'Bar', [868 ('id', models.AutoField(primary_key=True)),869 ('first', models.IntegerField()),870 ('second', models.IntegerField()),871 ('foo', models.ForeignObject(872 'app.Foo', models.CASCADE, from_fields=fields, to_fields=renamed_fields,873 )),874 ]),875 ]876 changes = self.get_changes(before, after, MigrationQuestioner({'ask_rename': True}))877 self.assertNumberMigrations(changes, 'app', 1)878 self.assertOperationTypes(changes, 'app', 0, ['RenameField', 'RenameField', 'AlterUniqueTogether'])879 self.assertOperationAttributes(880 changes, 'app', 0, 0, model_name='foo', old_name='first', new_name='first_renamed',881 )882 self.assertOperationAttributes(883 changes, 'app', 0, 1, model_name='foo', old_name='second', new_name='second_renamed',884 )885 # Case 2: from_fields renames.886 after = [887 ModelState('app', 'Foo', [888 ('id', models.AutoField(primary_key=True)),889 ('first', models.IntegerField()),890 ('second', models.IntegerField()),891 ], options={'unique_together': {fields}}),892 ModelState('app', 'Bar', [893 ('id', models.AutoField(primary_key=True)),894 ('first_renamed', models.IntegerField()),895 ('second_renamed', models.IntegerField()),896 ('foo', models.ForeignObject(897 'app.Foo', models.CASCADE, from_fields=renamed_fields, to_fields=fields,898 )),899 ]),900 ]901 changes = self.get_changes(before, after, MigrationQuestioner({'ask_rename': True}))902 self.assertNumberMigrations(changes, 'app', 1)903 self.assertOperationTypes(changes, 'app', 0, ['RenameField', 'RenameField'])904 self.assertOperationAttributes(905 changes, 'app', 0, 0, model_name='bar', old_name='first', new_name='first_renamed',906 )907 self.assertOperationAttributes(908 changes, 'app', 0, 1, model_name='bar', old_name='second', new_name='second_renamed',909 )910 def test_rename_referenced_primary_key(self):911 before = [912 ModelState('app', 'Foo', [913 ('id', models.CharField(primary_key=True, serialize=False)),914 ]),915 ModelState('app', 'Bar', [916 ('id', models.AutoField(primary_key=True)),917 ('foo', models.ForeignKey('app.Foo', models.CASCADE)),918 ]),919 ]920 after = [921 ModelState('app', 'Foo', [922 ('renamed_id', models.CharField(primary_key=True, serialize=False))923 ]),924 ModelState('app', 'Bar', [925 ('id', models.AutoField(primary_key=True)),926 ('foo', models.ForeignKey('app.Foo', models.CASCADE)),927 ]),928 ]929 changes = self.get_changes(before, after, MigrationQuestioner({'ask_rename': True}))930 self.assertNumberMigrations(changes, 'app', 1)931 self.assertOperationTypes(changes, 'app', 0, ['RenameField'])932 self.assertOperationAttributes(changes, 'app', 0, 0, old_name='id', new_name='renamed_id')933 def test_rename_field_preserved_db_column(self):934 """935 RenameField is used if a field is renamed and db_column equal to the936 old field's column is added.937 """938 before = [939 ModelState('app', 'Foo', [940 ('id', models.AutoField(primary_key=True)),941 ('field', models.IntegerField()),942 ]),943 ]944 after = [945 ModelState('app', 'Foo', [946 ('id', models.AutoField(primary_key=True)),947 ('renamed_field', models.IntegerField(db_column='field')),948 ]),949 ]950 changes = self.get_changes(before, after, MigrationQuestioner({'ask_rename': True}))951 self.assertNumberMigrations(changes, 'app', 1)952 self.assertOperationTypes(changes, 'app', 0, ['RenameField', 'AlterField'])953 self.assertOperationAttributes(954 changes, 'app', 0, 0, model_name='foo', old_name='field', new_name='renamed_field',955 )956 self.assertOperationAttributes(changes, 'app', 0, 1, model_name='foo', name='renamed_field')957 self.assertEqual(changes['app'][0].operations[-1].field.deconstruct(), (958 'renamed_field', 'django.db.models.IntegerField', [], {'db_column': 'field'},959 ))960 def test_rename_related_field_preserved_db_column(self):961 before = [962 ModelState('app', 'Foo', [963 ('id', models.AutoField(primary_key=True)),964 ]),965 ModelState('app', 'Bar', [966 ('id', models.AutoField(primary_key=True)),967 ('foo', models.ForeignKey('app.Foo', models.CASCADE)),968 ]),969 ]970 after = [971 ModelState('app', 'Foo', [972 ('id', models.AutoField(primary_key=True)),973 ]),974 ModelState('app', 'Bar', [975 ('id', models.AutoField(primary_key=True)),976 ('renamed_foo', models.ForeignKey('app.Foo', models.CASCADE, db_column='foo_id')),977 ]),978 ]979 changes = self.get_changes(before, after, MigrationQuestioner({'ask_rename': True}))980 self.assertNumberMigrations(changes, 'app', 1)981 self.assertOperationTypes(changes, 'app', 0, ['RenameField', 'AlterField'])982 self.assertOperationAttributes(983 changes, 'app', 0, 0, model_name='bar', old_name='foo', new_name='renamed_foo',984 )985 self.assertOperationAttributes(changes, 'app', 0, 1, model_name='bar', name='renamed_foo')986 self.assertEqual(changes['app'][0].operations[-1].field.deconstruct(), (987 'renamed_foo',988 'django.db.models.ForeignKey',989 [],990 {'to': 'app.foo', 'on_delete': models.CASCADE, 'db_column': 'foo_id'},991 ))992 def test_rename_model(self):993 """Tests autodetection of renamed models."""994 changes = self.get_changes(995 [self.author_with_book, self.book],996 [self.author_renamed_with_book, self.book_with_author_renamed],997 MigrationQuestioner({"ask_rename_model": True}),998 )999 # Right number/type of migrations?1000 self.assertNumberMigrations(changes, 'testapp', 1)1001 self.assertOperationTypes(changes, 'testapp', 0, ["RenameModel"])1002 self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name="Author", new_name="Writer")1003 # Now that RenameModel handles related fields too, there should be1004 # no AlterField for the related field.1005 self.assertNumberMigrations(changes, 'otherapp', 0)1006 def test_rename_model_case(self):1007 """1008 Model name is case-insensitive. Changing case doesn't lead to any1009 autodetected operations.1010 """1011 author_renamed = ModelState('testapp', 'author', [1012 ('id', models.AutoField(primary_key=True)),1013 ])1014 changes = self.get_changes(1015 [self.author_empty, self.book],1016 [author_renamed, self.book],1017 questioner=MigrationQuestioner({'ask_rename_model': True}),1018 )1019 self.assertNumberMigrations(changes, 'testapp', 0)1020 self.assertNumberMigrations(changes, 'otherapp', 0)1021 def test_rename_m2m_through_model(self):1022 """1023 Tests autodetection of renamed models that are used in M2M relations as1024 through models.1025 """1026 changes = self.get_changes(1027 [self.author_with_m2m_through, self.publisher, self.contract],1028 [self.author_with_renamed_m2m_through, self.publisher, self.contract_renamed],1029 MigrationQuestioner({'ask_rename_model': True})1030 )1031 # Right number/type of migrations?1032 self.assertNumberMigrations(changes, 'testapp', 1)1033 self.assertOperationTypes(changes, 'testapp', 0, ['RenameModel'])1034 self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name='Contract', new_name='Deal')1035 def test_rename_model_with_renamed_rel_field(self):1036 """1037 Tests autodetection of renamed models while simultaneously renaming one1038 of the fields that relate to the renamed model.1039 """1040 changes = self.get_changes(1041 [self.author_with_book, self.book],1042 [self.author_renamed_with_book, self.book_with_field_and_author_renamed],1043 MigrationQuestioner({"ask_rename": True, "ask_rename_model": True}),1044 )1045 # Right number/type of migrations?1046 self.assertNumberMigrations(changes, 'testapp', 1)1047 self.assertOperationTypes(changes, 'testapp', 0, ["RenameModel"])1048 self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name="Author", new_name="Writer")1049 # Right number/type of migrations for related field rename?1050 # Alter is already taken care of.1051 self.assertNumberMigrations(changes, 'otherapp', 1)1052 self.assertOperationTypes(changes, 'otherapp', 0, ["RenameField"])1053 self.assertOperationAttributes(changes, 'otherapp', 0, 0, old_name="author", new_name="writer")1054 def test_rename_model_with_fks_in_different_position(self):1055 """1056 #24537 - The order of fields in a model does not influence1057 the RenameModel detection.1058 """1059 before = [1060 ModelState("testapp", "EntityA", [1061 ("id", models.AutoField(primary_key=True)),1062 ]),1063 ModelState("testapp", "EntityB", [1064 ("id", models.AutoField(primary_key=True)),1065 ("some_label", models.CharField(max_length=255)),1066 ("entity_a", models.ForeignKey("testapp.EntityA", models.CASCADE)),1067 ]),1068 ]1069 after = [1070 ModelState("testapp", "EntityA", [1071 ("id", models.AutoField(primary_key=True)),1072 ]),1073 ModelState("testapp", "RenamedEntityB", [1074 ("id", models.AutoField(primary_key=True)),1075 ("entity_a", models.ForeignKey("testapp.EntityA", models.CASCADE)),1076 ("some_label", models.CharField(max_length=255)),1077 ]),1078 ]1079 changes = self.get_changes(before, after, MigrationQuestioner({"ask_rename_model": True}))1080 self.assertNumberMigrations(changes, "testapp", 1)1081 self.assertOperationTypes(changes, "testapp", 0, ["RenameModel"])1082 self.assertOperationAttributes(changes, "testapp", 0, 0, old_name="EntityB", new_name="RenamedEntityB")1083 def test_rename_model_reverse_relation_dependencies(self):1084 """1085 The migration to rename a model pointed to by a foreign key in another1086 app must run after the other app's migration that adds the foreign key1087 with model's original name. Therefore, the renaming migration has a1088 dependency on that other migration.1089 """1090 before = [1091 ModelState('testapp', 'EntityA', [1092 ('id', models.AutoField(primary_key=True)),1093 ]),1094 ModelState('otherapp', 'EntityB', [1095 ('id', models.AutoField(primary_key=True)),1096 ('entity_a', models.ForeignKey('testapp.EntityA', models.CASCADE)),1097 ]),1098 ]1099 after = [1100 ModelState('testapp', 'RenamedEntityA', [1101 ('id', models.AutoField(primary_key=True)),1102 ]),1103 ModelState('otherapp', 'EntityB', [1104 ('id', models.AutoField(primary_key=True)),1105 ('entity_a', models.ForeignKey('testapp.RenamedEntityA', models.CASCADE)),1106 ]),1107 ]1108 changes = self.get_changes(before, after, MigrationQuestioner({'ask_rename_model': True}))1109 self.assertNumberMigrations(changes, 'testapp', 1)1110 self.assertMigrationDependencies(changes, 'testapp', 0, [('otherapp', '__first__')])1111 self.assertOperationTypes(changes, 'testapp', 0, ['RenameModel'])1112 self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name='EntityA', new_name='RenamedEntityA')1113 def test_fk_dependency(self):1114 """Having a ForeignKey automatically adds a dependency."""1115 # Note that testapp (author) has no dependencies,1116 # otherapp (book) depends on testapp (author),1117 # thirdapp (edition) depends on otherapp (book)1118 changes = self.get_changes([], [self.author_name, self.book, self.edition])1119 # Right number/type of migrations?1120 self.assertNumberMigrations(changes, 'testapp', 1)1121 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"])1122 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author")1123 self.assertMigrationDependencies(changes, 'testapp', 0, [])1124 # Right number/type of migrations?1125 self.assertNumberMigrations(changes, 'otherapp', 1)1126 self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"])1127 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="Book")1128 self.assertMigrationDependencies(changes, 'otherapp', 0, [("testapp", "auto_1")])1129 # Right number/type of migrations?1130 self.assertNumberMigrations(changes, 'thirdapp', 1)1131 self.assertOperationTypes(changes, 'thirdapp', 0, ["CreateModel"])1132 self.assertOperationAttributes(changes, 'thirdapp', 0, 0, name="Edition")1133 self.assertMigrationDependencies(changes, 'thirdapp', 0, [("otherapp", "auto_1")])1134 def test_proxy_fk_dependency(self):1135 """FK dependencies still work on proxy models."""1136 # Note that testapp (author) has no dependencies,1137 # otherapp (book) depends on testapp (authorproxy)1138 changes = self.get_changes([], [self.author_empty, self.author_proxy_third, self.book_proxy_fk])1139 # Right number/type of migrations?1140 self.assertNumberMigrations(changes, 'testapp', 1)1141 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"])1142 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author")1143 self.assertMigrationDependencies(changes, 'testapp', 0, [])1144 # Right number/type of migrations?1145 self.assertNumberMigrations(changes, 'otherapp', 1)1146 self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"])1147 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="Book")1148 self.assertMigrationDependencies(changes, 'otherapp', 0, [("thirdapp", "auto_1")])1149 # Right number/type of migrations?1150 self.assertNumberMigrations(changes, 'thirdapp', 1)1151 self.assertOperationTypes(changes, 'thirdapp', 0, ["CreateModel"])1152 self.assertOperationAttributes(changes, 'thirdapp', 0, 0, name="AuthorProxy")1153 self.assertMigrationDependencies(changes, 'thirdapp', 0, [("testapp", "auto_1")])1154 def test_same_app_no_fk_dependency(self):1155 """1156 A migration with a FK between two models of the same app1157 does not have a dependency to itself.1158 """1159 changes = self.get_changes([], [self.author_with_publisher, self.publisher])1160 # Right number/type of migrations?1161 self.assertNumberMigrations(changes, 'testapp', 1)1162 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel"])1163 self.assertOperationAttributes(changes, "testapp", 0, 0, name="Publisher")1164 self.assertOperationAttributes(changes, "testapp", 0, 1, name="Author")1165 self.assertMigrationDependencies(changes, 'testapp', 0, [])1166 def test_circular_fk_dependency(self):1167 """1168 Having a circular ForeignKey dependency automatically1169 resolves the situation into 2 migrations on one side and 1 on the other.1170 """1171 changes = self.get_changes([], [self.author_with_book, self.book, self.publisher_with_book])1172 # Right number/type of migrations?1173 self.assertNumberMigrations(changes, 'testapp', 1)1174 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel"])1175 self.assertOperationAttributes(changes, "testapp", 0, 0, name="Publisher")1176 self.assertOperationAttributes(changes, "testapp", 0, 1, name="Author")1177 self.assertMigrationDependencies(changes, 'testapp', 0, [("otherapp", "auto_1")])1178 # Right number/type of migrations?1179 self.assertNumberMigrations(changes, 'otherapp', 2)1180 self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"])1181 self.assertOperationTypes(changes, 'otherapp', 1, ["AddField"])1182 self.assertMigrationDependencies(changes, 'otherapp', 0, [])1183 self.assertMigrationDependencies(changes, 'otherapp', 1, [("otherapp", "auto_1"), ("testapp", "auto_1")])1184 # both split migrations should be `initial`1185 self.assertTrue(changes['otherapp'][0].initial)1186 self.assertTrue(changes['otherapp'][1].initial)1187 def test_same_app_circular_fk_dependency(self):1188 """1189 A migration with a FK between two models of the same app does1190 not have a dependency to itself.1191 """1192 changes = self.get_changes([], [self.author_with_publisher, self.publisher_with_author])1193 # Right number/type of migrations?1194 self.assertNumberMigrations(changes, 'testapp', 1)1195 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel", "AddField"])1196 self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")1197 self.assertOperationAttributes(changes, "testapp", 0, 1, name="Publisher")1198 self.assertOperationAttributes(changes, "testapp", 0, 2, name="publisher")1199 self.assertMigrationDependencies(changes, 'testapp', 0, [])1200 def test_same_app_circular_fk_dependency_with_unique_together_and_indexes(self):1201 """1202 #22275 - A migration with circular FK dependency does not try1203 to create unique together constraint and indexes before creating all1204 required fields first.1205 """1206 changes = self.get_changes([], [self.knight, self.rabbit])1207 # Right number/type of migrations?1208 self.assertNumberMigrations(changes, 'eggs', 1)1209 self.assertOperationTypes(1210 changes, 'eggs', 0, ["CreateModel", "CreateModel", "AddIndex", "AlterUniqueTogether"]1211 )1212 self.assertNotIn("unique_together", changes['eggs'][0].operations[0].options)1213 self.assertNotIn("unique_together", changes['eggs'][0].operations[1].options)1214 self.assertMigrationDependencies(changes, 'eggs', 0, [])1215 def test_alter_db_table_add(self):1216 """Tests detection for adding db_table in model's options."""1217 changes = self.get_changes([self.author_empty], [self.author_with_db_table_options])1218 # Right number/type of migrations?1219 self.assertNumberMigrations(changes, 'testapp', 1)1220 self.assertOperationTypes(changes, 'testapp', 0, ["AlterModelTable"])1221 self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", table="author_one")1222 def test_alter_db_table_change(self):1223 """Tests detection for changing db_table in model's options'."""1224 changes = self.get_changes([self.author_with_db_table_options], [self.author_with_new_db_table_options])1225 # Right number/type of migrations?1226 self.assertNumberMigrations(changes, 'testapp', 1)1227 self.assertOperationTypes(changes, 'testapp', 0, ["AlterModelTable"])1228 self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", table="author_two")1229 def test_alter_db_table_remove(self):1230 """Tests detection for removing db_table in model's options."""1231 changes = self.get_changes([self.author_with_db_table_options], [self.author_empty])1232 # Right number/type of migrations?1233 self.assertNumberMigrations(changes, 'testapp', 1)1234 self.assertOperationTypes(changes, 'testapp', 0, ["AlterModelTable"])1235 self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", table=None)1236 def test_alter_db_table_no_changes(self):1237 """1238 Alter_db_table doesn't generate a migration if no changes have been made.1239 """1240 changes = self.get_changes([self.author_with_db_table_options], [self.author_with_db_table_options])1241 # Right number of migrations?1242 self.assertEqual(len(changes), 0)1243 def test_keep_db_table_with_model_change(self):1244 """1245 Tests when model changes but db_table stays as-is, autodetector must not1246 create more than one operation.1247 """1248 changes = self.get_changes(1249 [self.author_with_db_table_options],1250 [self.author_renamed_with_db_table_options],1251 MigrationQuestioner({"ask_rename_model": True}),1252 )1253 # Right number/type of migrations?1254 self.assertNumberMigrations(changes, 'testapp', 1)1255 self.assertOperationTypes(changes, 'testapp', 0, ["RenameModel"])1256 self.assertOperationAttributes(changes, "testapp", 0, 0, old_name="Author", new_name="NewAuthor")1257 def test_alter_db_table_with_model_change(self):1258 """1259 Tests when model and db_table changes, autodetector must create two1260 operations.1261 """1262 changes = self.get_changes(1263 [self.author_with_db_table_options],1264 [self.author_renamed_with_new_db_table_options],1265 MigrationQuestioner({"ask_rename_model": True}),1266 )1267 # Right number/type of migrations?1268 self.assertNumberMigrations(changes, 'testapp', 1)1269 self.assertOperationTypes(changes, 'testapp', 0, ["RenameModel", "AlterModelTable"])1270 self.assertOperationAttributes(changes, "testapp", 0, 0, old_name="Author", new_name="NewAuthor")1271 self.assertOperationAttributes(changes, "testapp", 0, 1, name="newauthor", table="author_three")1272 def test_identical_regex_doesnt_alter(self):1273 from_state = ModelState(1274 "testapp", "model", [("id", models.AutoField(primary_key=True, validators=[1275 RegexValidator(1276 re.compile('^[-a-zA-Z0-9_]+\\Z'),1277 'Enter a valid “slug” consisting of letters, numbers, underscores or hyphens.',1278 'invalid'1279 )1280 ]))]1281 )1282 to_state = ModelState(1283 "testapp", "model", [("id", models.AutoField(primary_key=True, validators=[validate_slug]))]1284 )1285 changes = self.get_changes([from_state], [to_state])1286 # Right number/type of migrations?1287 self.assertNumberMigrations(changes, "testapp", 0)1288 def test_different_regex_does_alter(self):1289 from_state = ModelState(1290 "testapp", "model", [("id", models.AutoField(primary_key=True, validators=[1291 RegexValidator(1292 re.compile('^[a-z]+\\Z', 32),1293 'Enter a valid “slug” consisting of letters, numbers, underscores or hyphens.',1294 'invalid'1295 )1296 ]))]1297 )1298 to_state = ModelState(1299 "testapp", "model", [("id", models.AutoField(primary_key=True, validators=[validate_slug]))]1300 )1301 changes = self.get_changes([from_state], [to_state])1302 self.assertNumberMigrations(changes, "testapp", 1)1303 self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])1304 def test_empty_foo_together(self):1305 """1306 #23452 - Empty unique/index_together shouldn't generate a migration.1307 """1308 # Explicitly testing for not specified, since this is the case after1309 # a CreateModel operation w/o any definition on the original model1310 model_state_not_specified = ModelState("a", "model", [("id", models.AutoField(primary_key=True))])1311 # Explicitly testing for None, since this was the issue in #23452 after1312 # an AlterFooTogether operation with e.g. () as value1313 model_state_none = ModelState("a", "model", [1314 ("id", models.AutoField(primary_key=True))1315 ], {1316 "index_together": None,1317 "unique_together": None,1318 })1319 # Explicitly testing for the empty set, since we now always have sets.1320 # During removal (('col1', 'col2'),) --> () this becomes set([])1321 model_state_empty = ModelState("a", "model", [1322 ("id", models.AutoField(primary_key=True))1323 ], {1324 "index_together": set(),1325 "unique_together": set(),1326 })1327 def test(from_state, to_state, msg):1328 changes = self.get_changes([from_state], [to_state])1329 if changes:1330 ops = ', '.join(o.__class__.__name__ for o in changes['a'][0].operations)1331 self.fail('Created operation(s) %s from %s' % (ops, msg))1332 tests = (1333 (model_state_not_specified, model_state_not_specified, '"not specified" to "not specified"'),1334 (model_state_not_specified, model_state_none, '"not specified" to "None"'),1335 (model_state_not_specified, model_state_empty, '"not specified" to "empty"'),1336 (model_state_none, model_state_not_specified, '"None" to "not specified"'),1337 (model_state_none, model_state_none, '"None" to "None"'),1338 (model_state_none, model_state_empty, '"None" to "empty"'),1339 (model_state_empty, model_state_not_specified, '"empty" to "not specified"'),1340 (model_state_empty, model_state_none, '"empty" to "None"'),1341 (model_state_empty, model_state_empty, '"empty" to "empty"'),1342 )1343 for t in tests:1344 test(*t)1345 def test_create_model_with_indexes(self):1346 """Test creation of new model with indexes already defined."""1347 author = ModelState('otherapp', 'Author', [1348 ('id', models.AutoField(primary_key=True)),1349 ('name', models.CharField(max_length=200)),1350 ], {'indexes': [models.Index(fields=['name'], name='create_model_with_indexes_idx')]})1351 changes = self.get_changes([], [author])1352 added_index = models.Index(fields=['name'], name='create_model_with_indexes_idx')1353 # Right number of migrations?1354 self.assertEqual(len(changes['otherapp']), 1)1355 # Right number of actions?1356 migration = changes['otherapp'][0]1357 self.assertEqual(len(migration.operations), 2)1358 # Right actions order?1359 self.assertOperationTypes(changes, 'otherapp', 0, ['CreateModel', 'AddIndex'])1360 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name='Author')1361 self.assertOperationAttributes(changes, 'otherapp', 0, 1, model_name='author', index=added_index)1362 def test_add_indexes(self):1363 """Test change detection of new indexes."""1364 changes = self.get_changes([self.author_empty, self.book], [self.author_empty, self.book_indexes])1365 self.assertNumberMigrations(changes, 'otherapp', 1)1366 self.assertOperationTypes(changes, 'otherapp', 0, ['AddIndex'])1367 added_index = models.Index(fields=['author', 'title'], name='book_title_author_idx')1368 self.assertOperationAttributes(changes, 'otherapp', 0, 0, model_name='book', index=added_index)1369 def test_remove_indexes(self):1370 """Test change detection of removed indexes."""1371 changes = self.get_changes([self.author_empty, self.book_indexes], [self.author_empty, self.book])1372 # Right number/type of migrations?1373 self.assertNumberMigrations(changes, 'otherapp', 1)1374 self.assertOperationTypes(changes, 'otherapp', 0, ['RemoveIndex'])1375 self.assertOperationAttributes(changes, 'otherapp', 0, 0, model_name='book', name='book_title_author_idx')1376 def test_order_fields_indexes(self):1377 """Test change detection of reordering of fields in indexes."""1378 changes = self.get_changes(1379 [self.author_empty, self.book_indexes], [self.author_empty, self.book_unordered_indexes]1380 )1381 self.assertNumberMigrations(changes, 'otherapp', 1)1382 self.assertOperationTypes(changes, 'otherapp', 0, ['RemoveIndex', 'AddIndex'])1383 self.assertOperationAttributes(changes, 'otherapp', 0, 0, model_name='book', name='book_title_author_idx')1384 added_index = models.Index(fields=['title', 'author'], name='book_author_title_idx')1385 self.assertOperationAttributes(changes, 'otherapp', 0, 1, model_name='book', index=added_index)1386 def test_create_model_with_check_constraint(self):1387 """Test creation of new model with constraints already defined."""1388 author = ModelState('otherapp', 'Author', [1389 ('id', models.AutoField(primary_key=True)),1390 ('name', models.CharField(max_length=200)),1391 ], {'constraints': [models.CheckConstraint(check=models.Q(name__contains='Bob'), name='name_contains_bob')]})1392 changes = self.get_changes([], [author])1393 added_constraint = models.CheckConstraint(check=models.Q(name__contains='Bob'), name='name_contains_bob')1394 # Right number of migrations?1395 self.assertEqual(len(changes['otherapp']), 1)1396 # Right number of actions?1397 migration = changes['otherapp'][0]1398 self.assertEqual(len(migration.operations), 2)1399 # Right actions order?1400 self.assertOperationTypes(changes, 'otherapp', 0, ['CreateModel', 'AddConstraint'])1401 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name='Author')1402 self.assertOperationAttributes(changes, 'otherapp', 0, 1, model_name='author', constraint=added_constraint)1403 def test_add_constraints(self):1404 """Test change detection of new constraints."""1405 changes = self.get_changes([self.author_name], [self.author_name_check_constraint])1406 self.assertNumberMigrations(changes, 'testapp', 1)1407 self.assertOperationTypes(changes, 'testapp', 0, ['AddConstraint'])1408 added_constraint = models.CheckConstraint(check=models.Q(name__contains='Bob'), name='name_contains_bob')1409 self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name='author', constraint=added_constraint)1410 def test_remove_constraints(self):1411 """Test change detection of removed constraints."""1412 changes = self.get_changes([self.author_name_check_constraint], [self.author_name])1413 # Right number/type of migrations?1414 self.assertNumberMigrations(changes, 'testapp', 1)1415 self.assertOperationTypes(changes, 'testapp', 0, ['RemoveConstraint'])1416 self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name='author', name='name_contains_bob')1417 def test_add_foo_together(self):1418 """Tests index/unique_together detection."""1419 changes = self.get_changes([self.author_empty, self.book], [self.author_empty, self.book_foo_together])1420 # Right number/type of migrations?1421 self.assertNumberMigrations(changes, "otherapp", 1)1422 self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether", "AlterIndexTogether"])1423 self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together={("author", "title")})1424 self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together={("author", "title")})1425 def test_remove_foo_together(self):1426 """Tests index/unique_together detection."""1427 changes = self.get_changes([self.author_empty, self.book_foo_together], [self.author_empty, self.book])1428 # Right number/type of migrations?1429 self.assertNumberMigrations(changes, "otherapp", 1)1430 self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether", "AlterIndexTogether"])1431 self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together=set())1432 self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together=set())1433 def test_foo_together_remove_fk(self):1434 """Tests unique_together and field removal detection & ordering"""1435 changes = self.get_changes(1436 [self.author_empty, self.book_foo_together], [self.author_empty, self.book_with_no_author]1437 )1438 # Right number/type of migrations?1439 self.assertNumberMigrations(changes, "otherapp", 1)1440 self.assertOperationTypes(changes, "otherapp", 0, [1441 "AlterUniqueTogether", "AlterIndexTogether", "RemoveField"1442 ])1443 self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together=set())1444 self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together=set())1445 self.assertOperationAttributes(changes, "otherapp", 0, 2, model_name="book", name="author")1446 def test_foo_together_no_changes(self):1447 """1448 index/unique_together doesn't generate a migration if no1449 changes have been made.1450 """1451 changes = self.get_changes(1452 [self.author_empty, self.book_foo_together], [self.author_empty, self.book_foo_together]1453 )1454 # Right number of migrations?1455 self.assertEqual(len(changes), 0)1456 def test_foo_together_ordering(self):1457 """1458 index/unique_together also triggers on ordering changes.1459 """1460 changes = self.get_changes(1461 [self.author_empty, self.book_foo_together], [self.author_empty, self.book_foo_together_2]1462 )1463 # Right number/type of migrations?1464 self.assertNumberMigrations(changes, "otherapp", 1)1465 self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether", "AlterIndexTogether"])1466 self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together={("title", "author")})1467 self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together={("title", "author")})1468 def test_add_field_and_foo_together(self):1469 """1470 Added fields will be created before using them in index/unique_together.1471 """1472 changes = self.get_changes([self.author_empty, self.book], [self.author_empty, self.book_foo_together_3])1473 # Right number/type of migrations?1474 self.assertNumberMigrations(changes, "otherapp", 1)1475 self.assertOperationTypes(changes, "otherapp", 0, ["AddField", "AlterUniqueTogether", "AlterIndexTogether"])1476 self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", unique_together={("title", "newfield")})1477 self.assertOperationAttributes(changes, "otherapp", 0, 2, name="book", index_together={("title", "newfield")})1478 def test_create_model_and_unique_together(self):1479 author = ModelState("otherapp", "Author", [1480 ("id", models.AutoField(primary_key=True)),1481 ("name", models.CharField(max_length=200)),1482 ])1483 book_with_author = ModelState("otherapp", "Book", [1484 ("id", models.AutoField(primary_key=True)),1485 ("author", models.ForeignKey("otherapp.Author", models.CASCADE)),1486 ("title", models.CharField(max_length=200)),1487 ], {1488 "index_together": {("title", "author")},1489 "unique_together": {("title", "author")},1490 })1491 changes = self.get_changes([self.book_with_no_author], [author, book_with_author])1492 # Right number of migrations?1493 self.assertEqual(len(changes['otherapp']), 1)1494 # Right number of actions?1495 migration = changes['otherapp'][0]1496 self.assertEqual(len(migration.operations), 4)1497 # Right actions order?1498 self.assertOperationTypes(1499 changes, 'otherapp', 0,1500 ['CreateModel', 'AddField', 'AlterUniqueTogether', 'AlterIndexTogether']1501 )1502 def test_remove_field_and_foo_together(self):1503 """1504 Removed fields will be removed after updating index/unique_together.1505 """1506 changes = self.get_changes(1507 [self.author_empty, self.book_foo_together_3], [self.author_empty, self.book_foo_together]1508 )1509 # Right number/type of migrations?1510 self.assertNumberMigrations(changes, "otherapp", 1)1511 self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether", "AlterIndexTogether", "RemoveField"])1512 self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together={("author", "title")})1513 self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together={("author", "title")})1514 self.assertOperationAttributes(changes, "otherapp", 0, 2, model_name="book", name="newfield")1515 def test_rename_field_and_foo_together(self):1516 """1517 Removed fields will be removed after updating index/unique_together.1518 """1519 changes = self.get_changes(1520 [self.author_empty, self.book_foo_together_3],1521 [self.author_empty, self.book_foo_together_4],1522 MigrationQuestioner({"ask_rename": True}),1523 )1524 # Right number/type of migrations?1525 self.assertNumberMigrations(changes, "otherapp", 1)1526 self.assertOperationTypes(changes, "otherapp", 0, ["RenameField", "AlterUniqueTogether", "AlterIndexTogether"])1527 self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", unique_together={1528 ("title", "newfield2")1529 })1530 self.assertOperationAttributes(changes, "otherapp", 0, 2, name="book", index_together={("title", "newfield2")})1531 def test_proxy(self):1532 """The autodetector correctly deals with proxy models."""1533 # First, we test adding a proxy model1534 changes = self.get_changes([self.author_empty], [self.author_empty, self.author_proxy])1535 # Right number/type of migrations?1536 self.assertNumberMigrations(changes, "testapp", 1)1537 self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])1538 self.assertOperationAttributes(1539 changes, "testapp", 0, 0, name="AuthorProxy", options={"proxy": True, "indexes": [], "constraints": []}1540 )1541 # Now, we test turning a proxy model into a non-proxy model1542 # It should delete the proxy then make the real one1543 changes = self.get_changes(1544 [self.author_empty, self.author_proxy], [self.author_empty, self.author_proxy_notproxy]1545 )1546 # Right number/type of migrations?1547 self.assertNumberMigrations(changes, "testapp", 1)1548 self.assertOperationTypes(changes, "testapp", 0, ["DeleteModel", "CreateModel"])1549 self.assertOperationAttributes(changes, "testapp", 0, 0, name="AuthorProxy")1550 self.assertOperationAttributes(changes, "testapp", 0, 1, name="AuthorProxy", options={})1551 def test_proxy_custom_pk(self):1552 """1553 #23415 - The autodetector must correctly deal with custom FK on proxy1554 models.1555 """1556 # First, we test the default pk field name1557 changes = self.get_changes([], [self.author_empty, self.author_proxy_third, self.book_proxy_fk])1558 # The field name the FK on the book model points to1559 self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'id')1560 # Now, we test the custom pk field name1561 changes = self.get_changes([], [self.author_custom_pk, self.author_proxy_third, self.book_proxy_fk])1562 # The field name the FK on the book model points to1563 self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'pk_field')1564 def test_proxy_to_mti_with_fk_to_proxy(self):1565 # First, test the pk table and field name.1566 changes = self.get_changes(1567 [],1568 [self.author_empty, self.author_proxy_third, self.book_proxy_fk],1569 )1570 self.assertEqual(1571 changes['otherapp'][0].operations[0].fields[2][1].remote_field.model._meta.db_table,1572 'testapp_author',1573 )1574 self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'id')1575 # Change AuthorProxy to use MTI.1576 changes = self.get_changes(1577 [self.author_empty, self.author_proxy_third, self.book_proxy_fk],1578 [self.author_empty, self.author_proxy_third_notproxy, self.book_proxy_fk],1579 )1580 # Right number/type of migrations for the AuthorProxy model?1581 self.assertNumberMigrations(changes, 'thirdapp', 1)1582 self.assertOperationTypes(changes, 'thirdapp', 0, ['DeleteModel', 'CreateModel'])1583 # Right number/type of migrations for the Book model with a FK to1584 # AuthorProxy?1585 self.assertNumberMigrations(changes, 'otherapp', 1)1586 self.assertOperationTypes(changes, 'otherapp', 0, ['AlterField'])1587 # otherapp should depend on thirdapp.1588 self.assertMigrationDependencies(changes, 'otherapp', 0, [('thirdapp', 'auto_1')])1589 # Now, test the pk table and field name.1590 self.assertEqual(1591 changes['otherapp'][0].operations[0].field.remote_field.model._meta.db_table,1592 'thirdapp_authorproxy',1593 )1594 self.assertEqual(changes['otherapp'][0].operations[0].field.remote_field.field_name, 'author_ptr')1595 def test_proxy_to_mti_with_fk_to_proxy_proxy(self):1596 # First, test the pk table and field name.1597 changes = self.get_changes(1598 [],1599 [self.author_empty, self.author_proxy, self.author_proxy_proxy, self.book_proxy_proxy_fk],1600 )1601 self.assertEqual(1602 changes['otherapp'][0].operations[0].fields[1][1].remote_field.model._meta.db_table,1603 'testapp_author',1604 )1605 self.assertEqual(changes['otherapp'][0].operations[0].fields[1][1].remote_field.field_name, 'id')1606 # Change AuthorProxy to use MTI. FK still points to AAuthorProxyProxy,1607 # a proxy of AuthorProxy.1608 changes = self.get_changes(1609 [self.author_empty, self.author_proxy, self.author_proxy_proxy, self.book_proxy_proxy_fk],1610 [self.author_empty, self.author_proxy_notproxy, self.author_proxy_proxy, self.book_proxy_proxy_fk],1611 )1612 # Right number/type of migrations for the AuthorProxy model?1613 self.assertNumberMigrations(changes, 'testapp', 1)1614 self.assertOperationTypes(changes, 'testapp', 0, ['DeleteModel', 'CreateModel'])1615 # Right number/type of migrations for the Book model with a FK to1616 # AAuthorProxyProxy?1617 self.assertNumberMigrations(changes, 'otherapp', 1)1618 self.assertOperationTypes(changes, 'otherapp', 0, ['AlterField'])1619 # otherapp should depend on testapp.1620 self.assertMigrationDependencies(changes, 'otherapp', 0, [('testapp', 'auto_1')])1621 # Now, test the pk table and field name.1622 self.assertEqual(1623 changes['otherapp'][0].operations[0].field.remote_field.model._meta.db_table,1624 'testapp_authorproxy',1625 )1626 self.assertEqual(changes['otherapp'][0].operations[0].field.remote_field.field_name, 'author_ptr')1627 def test_unmanaged_create(self):1628 """The autodetector correctly deals with managed models."""1629 # First, we test adding an unmanaged model1630 changes = self.get_changes([self.author_empty], [self.author_empty, self.author_unmanaged])1631 # Right number/type of migrations?1632 self.assertNumberMigrations(changes, 'testapp', 1)1633 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"])1634 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="AuthorUnmanaged", options={"managed": False})1635 def test_unmanaged_delete(self):1636 changes = self.get_changes([self.author_empty, self.author_unmanaged], [self.author_empty])1637 self.assertNumberMigrations(changes, 'testapp', 1)1638 self.assertOperationTypes(changes, 'testapp', 0, ['DeleteModel'])1639 def test_unmanaged_to_managed(self):1640 # Now, we test turning an unmanaged model into a managed model1641 changes = self.get_changes(1642 [self.author_empty, self.author_unmanaged], [self.author_empty, self.author_unmanaged_managed]1643 )1644 # Right number/type of migrations?1645 self.assertNumberMigrations(changes, 'testapp', 1)1646 self.assertOperationTypes(changes, 'testapp', 0, ["AlterModelOptions"])1647 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="authorunmanaged", options={})1648 def test_managed_to_unmanaged(self):1649 # Now, we turn managed to unmanaged.1650 changes = self.get_changes(1651 [self.author_empty, self.author_unmanaged_managed], [self.author_empty, self.author_unmanaged]1652 )1653 # Right number/type of migrations?1654 self.assertNumberMigrations(changes, 'testapp', 1)1655 self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])1656 self.assertOperationAttributes(changes, "testapp", 0, 0, name="authorunmanaged", options={"managed": False})1657 def test_unmanaged_custom_pk(self):1658 """1659 #23415 - The autodetector must correctly deal with custom FK on1660 unmanaged models.1661 """1662 # First, we test the default pk field name1663 changes = self.get_changes([], [self.author_unmanaged_default_pk, self.book])1664 # The field name the FK on the book model points to1665 self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'id')1666 # Now, we test the custom pk field name1667 changes = self.get_changes([], [self.author_unmanaged_custom_pk, self.book])1668 # The field name the FK on the book model points to1669 self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].remote_field.field_name, 'pk_field')1670 @override_settings(AUTH_USER_MODEL="thirdapp.CustomUser")1671 def test_swappable(self):1672 with isolate_lru_cache(apps.get_swappable_settings_name):1673 changes = self.get_changes([self.custom_user], [self.custom_user, self.author_with_custom_user])1674 # Right number/type of migrations?1675 self.assertNumberMigrations(changes, 'testapp', 1)1676 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"])1677 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author")1678 self.assertMigrationDependencies(changes, 'testapp', 0, [("__setting__", "AUTH_USER_MODEL")])1679 def test_swappable_changed(self):1680 with isolate_lru_cache(apps.get_swappable_settings_name):1681 before = self.make_project_state([self.custom_user, self.author_with_user])1682 with override_settings(AUTH_USER_MODEL="thirdapp.CustomUser"):1683 after = self.make_project_state([self.custom_user, self.author_with_custom_user])1684 autodetector = MigrationAutodetector(before, after)1685 changes = autodetector._detect_changes()1686 # Right number/type of migrations?1687 self.assertNumberMigrations(changes, 'testapp', 1)1688 self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])1689 self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name="author", name='user')1690 fk_field = changes['testapp'][0].operations[0].field1691 to_model = '%s.%s' % (1692 fk_field.remote_field.model._meta.app_label,1693 fk_field.remote_field.model._meta.object_name,1694 )1695 self.assertEqual(to_model, 'thirdapp.CustomUser')1696 def test_add_field_with_default(self):1697 """#22030 - Adding a field with a default should work."""1698 changes = self.get_changes([self.author_empty], [self.author_name_default])1699 # Right number/type of migrations?1700 self.assertNumberMigrations(changes, 'testapp', 1)1701 self.assertOperationTypes(changes, 'testapp', 0, ["AddField"])1702 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="name")1703 def test_custom_deconstructible(self):1704 """1705 Two instances which deconstruct to the same value aren't considered a1706 change.1707 """1708 changes = self.get_changes([self.author_name_deconstructible_1], [self.author_name_deconstructible_2])1709 # Right number of migrations?1710 self.assertEqual(len(changes), 0)1711 def test_deconstruct_field_kwarg(self):1712 """Field instances are handled correctly by nested deconstruction."""1713 changes = self.get_changes([self.author_name_deconstructible_3], [self.author_name_deconstructible_4])1714 self.assertEqual(changes, {})1715 def test_deconstructible_list(self):1716 """Nested deconstruction descends into lists."""1717 # When lists contain items that deconstruct to identical values, those lists1718 # should be considered equal for the purpose of detecting state changes1719 # (even if the original items are unequal).1720 changes = self.get_changes(1721 [self.author_name_deconstructible_list_1], [self.author_name_deconstructible_list_2]1722 )1723 self.assertEqual(changes, {})1724 # Legitimate differences within the deconstructed lists should be reported1725 # as a change1726 changes = self.get_changes(1727 [self.author_name_deconstructible_list_1], [self.author_name_deconstructible_list_3]1728 )1729 self.assertEqual(len(changes), 1)1730 def test_deconstructible_tuple(self):1731 """Nested deconstruction descends into tuples."""1732 # When tuples contain items that deconstruct to identical values, those tuples1733 # should be considered equal for the purpose of detecting state changes1734 # (even if the original items are unequal).1735 changes = self.get_changes(1736 [self.author_name_deconstructible_tuple_1], [self.author_name_deconstructible_tuple_2]1737 )1738 self.assertEqual(changes, {})1739 # Legitimate differences within the deconstructed tuples should be reported1740 # as a change1741 changes = self.get_changes(1742 [self.author_name_deconstructible_tuple_1], [self.author_name_deconstructible_tuple_3]1743 )1744 self.assertEqual(len(changes), 1)1745 def test_deconstructible_dict(self):1746 """Nested deconstruction descends into dict values."""1747 # When dicts contain items whose values deconstruct to identical values,1748 # those dicts should be considered equal for the purpose of detecting1749 # state changes (even if the original values are unequal).1750 changes = self.get_changes(1751 [self.author_name_deconstructible_dict_1], [self.author_name_deconstructible_dict_2]1752 )1753 self.assertEqual(changes, {})1754 # Legitimate differences within the deconstructed dicts should be reported1755 # as a change1756 changes = self.get_changes(1757 [self.author_name_deconstructible_dict_1], [self.author_name_deconstructible_dict_3]1758 )1759 self.assertEqual(len(changes), 1)1760 def test_nested_deconstructible_objects(self):1761 """1762 Nested deconstruction is applied recursively to the args/kwargs of1763 deconstructed objects.1764 """1765 # If the items within a deconstructed object's args/kwargs have the same1766 # deconstructed values - whether or not the items themselves are different1767 # instances - then the object as a whole is regarded as unchanged.1768 changes = self.get_changes(1769 [self.author_name_nested_deconstructible_1], [self.author_name_nested_deconstructible_2]1770 )1771 self.assertEqual(changes, {})1772 # Differences that exist solely within the args list of a deconstructed object1773 # should be reported as changes1774 changes = self.get_changes(1775 [self.author_name_nested_deconstructible_1], [self.author_name_nested_deconstructible_changed_arg]1776 )1777 self.assertEqual(len(changes), 1)1778 # Additional args should also be reported as a change1779 changes = self.get_changes(1780 [self.author_name_nested_deconstructible_1], [self.author_name_nested_deconstructible_extra_arg]1781 )1782 self.assertEqual(len(changes), 1)1783 # Differences that exist solely within the kwargs dict of a deconstructed object1784 # should be reported as changes1785 changes = self.get_changes(1786 [self.author_name_nested_deconstructible_1], [self.author_name_nested_deconstructible_changed_kwarg]1787 )1788 self.assertEqual(len(changes), 1)1789 # Additional kwargs should also be reported as a change1790 changes = self.get_changes(1791 [self.author_name_nested_deconstructible_1], [self.author_name_nested_deconstructible_extra_kwarg]1792 )1793 self.assertEqual(len(changes), 1)1794 def test_deconstruct_type(self):1795 """1796 #22951 -- Uninstantiated classes with deconstruct are correctly returned1797 by deep_deconstruct during serialization.1798 """1799 author = ModelState(1800 "testapp",1801 "Author",1802 [1803 ("id", models.AutoField(primary_key=True)),1804 ("name", models.CharField(1805 max_length=200,1806 # IntegerField intentionally not instantiated.1807 default=models.IntegerField,1808 ))1809 ],1810 )1811 changes = self.get_changes([], [author])1812 # Right number/type of migrations?1813 self.assertNumberMigrations(changes, 'testapp', 1)1814 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"])1815 def test_replace_string_with_foreignkey(self):1816 """1817 #22300 - Adding an FK in the same "spot" as a deleted CharField should1818 work.1819 """1820 changes = self.get_changes([self.author_with_publisher_string], [self.author_with_publisher, self.publisher])1821 # Right number/type of migrations?1822 self.assertNumberMigrations(changes, 'testapp', 1)1823 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "RemoveField", "AddField"])1824 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Publisher")1825 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="publisher_name")1826 self.assertOperationAttributes(changes, 'testapp', 0, 2, name="publisher")1827 def test_foreign_key_removed_before_target_model(self):1828 """1829 Removing an FK and the model it targets in the same change must remove1830 the FK field before the model to maintain consistency.1831 """1832 changes = self.get_changes(1833 [self.author_with_publisher, self.publisher], [self.author_name]1834 ) # removes both the model and FK1835 # Right number/type of migrations?1836 self.assertNumberMigrations(changes, 'testapp', 1)1837 self.assertOperationTypes(changes, 'testapp', 0, ["RemoveField", "DeleteModel"])1838 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publisher")1839 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="Publisher")1840 @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition',1841 side_effect=AssertionError("Should not have prompted for not null addition"))1842 def test_add_many_to_many(self, mocked_ask_method):1843 """#22435 - Adding a ManyToManyField should not prompt for a default."""1844 changes = self.get_changes([self.author_empty, self.publisher], [self.author_with_m2m, self.publisher])1845 # Right number/type of migrations?1846 self.assertNumberMigrations(changes, 'testapp', 1)1847 self.assertOperationTypes(changes, 'testapp', 0, ["AddField"])1848 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publishers")1849 def test_alter_many_to_many(self):1850 changes = self.get_changes(1851 [self.author_with_m2m, self.publisher], [self.author_with_m2m_blank, self.publisher]1852 )1853 # Right number/type of migrations?1854 self.assertNumberMigrations(changes, 'testapp', 1)1855 self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])1856 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publishers")1857 def test_create_with_through_model(self):1858 """1859 Adding a m2m with a through model and the models that use it should be1860 ordered correctly.1861 """1862 changes = self.get_changes([], [self.author_with_m2m_through, self.publisher, self.contract])1863 # Right number/type of migrations?1864 self.assertNumberMigrations(changes, "testapp", 1)1865 self.assertOperationTypes(changes, "testapp", 0, [1866 'CreateModel', 'CreateModel', 'CreateModel', 'AddField',1867 ])1868 self.assertOperationAttributes(changes, 'testapp', 0, 0, name='Author')1869 self.assertOperationAttributes(changes, 'testapp', 0, 1, name='Publisher')1870 self.assertOperationAttributes(changes, 'testapp', 0, 2, name='Contract')1871 self.assertOperationAttributes(changes, 'testapp', 0, 3, model_name='author', name='publishers')1872 def test_many_to_many_removed_before_through_model(self):1873 """1874 Removing a ManyToManyField and the "through" model in the same change1875 must remove the field before the model to maintain consistency.1876 """1877 changes = self.get_changes(1878 [self.book_with_multiple_authors_through_attribution, self.author_name, self.attribution],1879 [self.book_with_no_author, self.author_name],1880 )1881 # Remove both the through model and ManyToMany1882 # Right number/type of migrations?1883 self.assertNumberMigrations(changes, "otherapp", 1)1884 self.assertOperationTypes(changes, 'otherapp', 0, ['RemoveField', 'DeleteModel'])1885 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name='authors', model_name='book')1886 self.assertOperationAttributes(changes, 'otherapp', 0, 1, name='Attribution')1887 def test_many_to_many_removed_before_through_model_2(self):1888 """1889 Removing a model that contains a ManyToManyField and the "through" model1890 in the same change must remove the field before the model to maintain1891 consistency.1892 """1893 changes = self.get_changes(1894 [self.book_with_multiple_authors_through_attribution, self.author_name, self.attribution],1895 [self.author_name],1896 )1897 # Remove both the through model and ManyToMany1898 # Right number/type of migrations?1899 self.assertNumberMigrations(changes, "otherapp", 1)1900 self.assertOperationTypes(changes, 'otherapp', 0, ['RemoveField', 'DeleteModel', 'DeleteModel'])1901 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name='authors', model_name='book')1902 self.assertOperationAttributes(changes, 'otherapp', 0, 1, name='Attribution')1903 self.assertOperationAttributes(changes, 'otherapp', 0, 2, name='Book')1904 def test_m2m_w_through_multistep_remove(self):1905 """1906 A model with a m2m field that specifies a "through" model cannot be1907 removed in the same migration as that through model as the schema will1908 pass through an inconsistent state. The autodetector should produce two1909 migrations to avoid this issue.1910 """1911 changes = self.get_changes([self.author_with_m2m_through, self.publisher, self.contract], [self.publisher])1912 # Right number/type of migrations?1913 self.assertNumberMigrations(changes, "testapp", 1)1914 self.assertOperationTypes(changes, "testapp", 0, [1915 "RemoveField", "RemoveField", "DeleteModel", "DeleteModel"1916 ])1917 self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", model_name='contract')1918 self.assertOperationAttributes(changes, "testapp", 0, 1, name="publisher", model_name='contract')1919 self.assertOperationAttributes(changes, "testapp", 0, 2, name="Author")1920 self.assertOperationAttributes(changes, "testapp", 0, 3, name="Contract")1921 def test_concrete_field_changed_to_many_to_many(self):1922 """1923 #23938 - Changing a concrete field into a ManyToManyField1924 first removes the concrete field and then adds the m2m field.1925 """1926 changes = self.get_changes([self.author_with_former_m2m], [self.author_with_m2m, self.publisher])1927 # Right number/type of migrations?1928 self.assertNumberMigrations(changes, "testapp", 1)1929 self.assertOperationTypes(changes, "testapp", 0, ["CreateModel", "RemoveField", "AddField"])1930 self.assertOperationAttributes(changes, 'testapp', 0, 0, name='Publisher')1931 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="publishers", model_name='author')1932 self.assertOperationAttributes(changes, 'testapp', 0, 2, name="publishers", model_name='author')1933 def test_many_to_many_changed_to_concrete_field(self):1934 """1935 #23938 - Changing a ManyToManyField into a concrete field1936 first removes the m2m field and then adds the concrete field.1937 """1938 changes = self.get_changes([self.author_with_m2m, self.publisher], [self.author_with_former_m2m])1939 # Right number/type of migrations?1940 self.assertNumberMigrations(changes, "testapp", 1)1941 self.assertOperationTypes(changes, "testapp", 0, ["RemoveField", "AddField", "DeleteModel"])1942 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publishers", model_name='author')1943 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="publishers", model_name='author')1944 self.assertOperationAttributes(changes, 'testapp', 0, 2, name='Publisher')1945 self.assertOperationFieldAttributes(changes, 'testapp', 0, 1, max_length=100)1946 def test_non_circular_foreignkey_dependency_removal(self):1947 """1948 If two models with a ForeignKey from one to the other are removed at the1949 same time, the autodetector should remove them in the correct order.1950 """1951 changes = self.get_changes([self.author_with_publisher, self.publisher_with_author], [])1952 # Right number/type of migrations?1953 self.assertNumberMigrations(changes, "testapp", 1)1954 self.assertOperationTypes(changes, "testapp", 0, ["RemoveField", "DeleteModel", "DeleteModel"])1955 self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", model_name='publisher')1956 self.assertOperationAttributes(changes, "testapp", 0, 1, name="Author")1957 self.assertOperationAttributes(changes, "testapp", 0, 2, name="Publisher")1958 def test_alter_model_options(self):1959 """Changing a model's options should make a change."""1960 changes = self.get_changes([self.author_empty], [self.author_with_options])1961 # Right number/type of migrations?1962 self.assertNumberMigrations(changes, "testapp", 1)1963 self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])1964 self.assertOperationAttributes(changes, "testapp", 0, 0, options={1965 "permissions": [('can_hire', 'Can hire')],1966 "verbose_name": "Authi",1967 })1968 # Changing them back to empty should also make a change1969 changes = self.get_changes([self.author_with_options], [self.author_empty])1970 # Right number/type of migrations?1971 self.assertNumberMigrations(changes, "testapp", 1)1972 self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])1973 self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", options={})1974 def test_alter_model_options_proxy(self):1975 """Changing a proxy model's options should also make a change."""1976 changes = self.get_changes(1977 [self.author_proxy, self.author_empty], [self.author_proxy_options, self.author_empty]1978 )1979 # Right number/type of migrations?1980 self.assertNumberMigrations(changes, "testapp", 1)1981 self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])1982 self.assertOperationAttributes(changes, "testapp", 0, 0, name="authorproxy", options={1983 "verbose_name": "Super Author"1984 })1985 def test_set_alter_order_with_respect_to(self):1986 """Setting order_with_respect_to adds a field."""1987 changes = self.get_changes([self.book, self.author_with_book], [self.book, self.author_with_book_order_wrt])1988 # Right number/type of migrations?1989 self.assertNumberMigrations(changes, 'testapp', 1)1990 self.assertOperationTypes(changes, 'testapp', 0, ["AlterOrderWithRespectTo"])1991 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="author", order_with_respect_to="book")1992 def test_add_alter_order_with_respect_to(self):1993 """1994 Setting order_with_respect_to when adding the FK too does1995 things in the right order.1996 """1997 changes = self.get_changes([self.author_name], [self.book, self.author_with_book_order_wrt])1998 # Right number/type of migrations?1999 self.assertNumberMigrations(changes, 'testapp', 1)2000 self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AlterOrderWithRespectTo"])2001 self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name="author", name="book")2002 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="author", order_with_respect_to="book")2003 def test_remove_alter_order_with_respect_to(self):2004 """2005 Removing order_with_respect_to when removing the FK too does2006 things in the right order.2007 """2008 changes = self.get_changes([self.book, self.author_with_book_order_wrt], [self.author_name])2009 # Right number/type of migrations?2010 self.assertNumberMigrations(changes, 'testapp', 1)2011 self.assertOperationTypes(changes, 'testapp', 0, ["AlterOrderWithRespectTo", "RemoveField"])2012 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="author", order_with_respect_to=None)2013 self.assertOperationAttributes(changes, 'testapp', 0, 1, model_name="author", name="book")2014 def test_add_model_order_with_respect_to(self):2015 """2016 Setting order_with_respect_to when adding the whole model2017 does things in the right order.2018 """2019 changes = self.get_changes([], [self.book, self.author_with_book_order_wrt])2020 # Right number/type of migrations?2021 self.assertNumberMigrations(changes, 'testapp', 1)2022 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"])2023 self.assertOperationAttributes(2024 changes, 'testapp', 0, 0, name="Author", options={'order_with_respect_to': 'book'}2025 )2026 self.assertNotIn("_order", [name for name, field in changes['testapp'][0].operations[0].fields])2027 def test_add_model_order_with_respect_to_index_foo_together(self):2028 changes = self.get_changes([], [2029 self.book,2030 ModelState('testapp', 'Author', [2031 ('id', models.AutoField(primary_key=True)),2032 ('name', models.CharField(max_length=200)),2033 ('book', models.ForeignKey('otherapp.Book', models.CASCADE)),2034 ], options={2035 'order_with_respect_to': 'book',2036 'index_together': {('name', '_order')},2037 'unique_together': {('id', '_order')},2038 }),2039 ])2040 self.assertNumberMigrations(changes, 'testapp', 1)2041 self.assertOperationTypes(changes, 'testapp', 0, ['CreateModel'])2042 self.assertOperationAttributes(2043 changes,2044 'testapp',2045 0,2046 0,2047 name='Author',2048 options={2049 'order_with_respect_to': 'book',2050 'index_together': {('name', '_order')},2051 'unique_together': {('id', '_order')},2052 },2053 )2054 def test_add_model_order_with_respect_to_index_constraint(self):2055 tests = [2056 (2057 'AddIndex',2058 {'indexes': [2059 models.Index(fields=['_order'], name='book_order_idx'),2060 ]},2061 ),2062 (2063 'AddConstraint',2064 {'constraints': [2065 models.CheckConstraint(2066 check=models.Q(_order__gt=1),2067 name='book_order_gt_1',2068 ),2069 ]},2070 ),2071 ]2072 for operation, extra_option in tests:2073 with self.subTest(operation=operation):2074 after = ModelState('testapp', 'Author', [2075 ('id', models.AutoField(primary_key=True)),2076 ('name', models.CharField(max_length=200)),2077 ('book', models.ForeignKey('otherapp.Book', models.CASCADE)),2078 ], options={2079 'order_with_respect_to': 'book',2080 **extra_option,2081 })2082 changes = self.get_changes([], [self.book, after])2083 self.assertNumberMigrations(changes, 'testapp', 1)2084 self.assertOperationTypes(changes, 'testapp', 0, [2085 'CreateModel', operation,2086 ])2087 self.assertOperationAttributes(2088 changes,2089 'testapp',2090 0,2091 0,2092 name='Author',2093 options={'order_with_respect_to': 'book'},2094 )2095 def test_set_alter_order_with_respect_to_index_constraint_foo_together(self):2096 tests = [2097 (2098 'AddIndex',2099 {'indexes': [2100 models.Index(fields=['_order'], name='book_order_idx'),2101 ]},2102 ),2103 (2104 'AddConstraint',2105 {'constraints': [2106 models.CheckConstraint(2107 check=models.Q(_order__gt=1),2108 name='book_order_gt_1',2109 ),2110 ]},2111 ),2112 ('AlterIndexTogether', {'index_together': {('name', '_order')}}),2113 ('AlterUniqueTogether', {'unique_together': {('id', '_order')}}),2114 ]2115 for operation, extra_option in tests:2116 with self.subTest(operation=operation):2117 after = ModelState('testapp', 'Author', [2118 ('id', models.AutoField(primary_key=True)),2119 ('name', models.CharField(max_length=200)),2120 ('book', models.ForeignKey('otherapp.Book', models.CASCADE)),2121 ], options={2122 'order_with_respect_to': 'book',2123 **extra_option,2124 })2125 changes = self.get_changes(2126 [self.book, self.author_with_book],2127 [self.book, after],2128 )2129 self.assertNumberMigrations(changes, 'testapp', 1)2130 self.assertOperationTypes(changes, 'testapp', 0, [2131 'AlterOrderWithRespectTo', operation,2132 ])2133 def test_alter_model_managers(self):2134 """2135 Changing the model managers adds a new operation.2136 """2137 changes = self.get_changes([self.other_pony], [self.other_pony_food])2138 # Right number/type of migrations?2139 self.assertNumberMigrations(changes, 'otherapp', 1)2140 self.assertOperationTypes(changes, 'otherapp', 0, ["AlterModelManagers"])2141 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="pony")2142 self.assertEqual([name for name, mgr in changes['otherapp'][0].operations[0].managers],2143 ['food_qs', 'food_mgr', 'food_mgr_kwargs'])2144 self.assertEqual(changes['otherapp'][0].operations[0].managers[1][1].args, ('a', 'b', 1, 2))2145 self.assertEqual(changes['otherapp'][0].operations[0].managers[2][1].args, ('x', 'y', 3, 4))2146 def test_swappable_first_inheritance(self):2147 """Swappable models get their CreateModel first."""2148 changes = self.get_changes([], [self.custom_user, self.aardvark])2149 # Right number/type of migrations?2150 self.assertNumberMigrations(changes, 'thirdapp', 1)2151 self.assertOperationTypes(changes, 'thirdapp', 0, ["CreateModel", "CreateModel"])2152 self.assertOperationAttributes(changes, 'thirdapp', 0, 0, name="CustomUser")2153 self.assertOperationAttributes(changes, 'thirdapp', 0, 1, name="Aardvark")2154 def test_default_related_name_option(self):2155 model_state = ModelState('app', 'model', [2156 ('id', models.AutoField(primary_key=True)),2157 ], options={'default_related_name': 'related_name'})2158 changes = self.get_changes([], [model_state])2159 self.assertNumberMigrations(changes, 'app', 1)2160 self.assertOperationTypes(changes, 'app', 0, ['CreateModel'])2161 self.assertOperationAttributes(2162 changes, 'app', 0, 0, name='model',2163 options={'default_related_name': 'related_name'},2164 )2165 altered_model_state = ModelState('app', 'Model', [2166 ('id', models.AutoField(primary_key=True)),2167 ])2168 changes = self.get_changes([model_state], [altered_model_state])2169 self.assertNumberMigrations(changes, 'app', 1)2170 self.assertOperationTypes(changes, 'app', 0, ['AlterModelOptions'])2171 self.assertOperationAttributes(changes, 'app', 0, 0, name='model', options={})2172 @override_settings(AUTH_USER_MODEL="thirdapp.CustomUser")2173 def test_swappable_first_setting(self):2174 """Swappable models get their CreateModel first."""2175 with isolate_lru_cache(apps.get_swappable_settings_name):2176 changes = self.get_changes([], [self.custom_user_no_inherit, self.aardvark])2177 # Right number/type of migrations?2178 self.assertNumberMigrations(changes, 'thirdapp', 1)2179 self.assertOperationTypes(changes, 'thirdapp', 0, ["CreateModel", "CreateModel"])2180 self.assertOperationAttributes(changes, 'thirdapp', 0, 0, name="CustomUser")2181 self.assertOperationAttributes(changes, 'thirdapp', 0, 1, name="Aardvark")2182 def test_bases_first(self):2183 """Bases of other models come first."""2184 changes = self.get_changes([], [self.aardvark_based_on_author, self.author_name])2185 # Right number/type of migrations?2186 self.assertNumberMigrations(changes, 'testapp', 1)2187 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel"])2188 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author")2189 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="Aardvark")2190 def test_multiple_bases(self):2191 """#23956 - Inheriting models doesn't move *_ptr fields into AddField operations."""2192 A = ModelState("app", "A", [("a_id", models.AutoField(primary_key=True))])2193 B = ModelState("app", "B", [("b_id", models.AutoField(primary_key=True))])2194 C = ModelState("app", "C", [], bases=("app.A", "app.B"))2195 D = ModelState("app", "D", [], bases=("app.A", "app.B"))2196 E = ModelState("app", "E", [], bases=("app.A", "app.B"))2197 changes = self.get_changes([], [A, B, C, D, E])2198 # Right number/type of migrations?2199 self.assertNumberMigrations(changes, "app", 1)2200 self.assertOperationTypes(changes, "app", 0, [2201 "CreateModel", "CreateModel", "CreateModel", "CreateModel", "CreateModel"2202 ])2203 self.assertOperationAttributes(changes, "app", 0, 0, name="A")2204 self.assertOperationAttributes(changes, "app", 0, 1, name="B")2205 self.assertOperationAttributes(changes, "app", 0, 2, name="C")2206 self.assertOperationAttributes(changes, "app", 0, 3, name="D")2207 self.assertOperationAttributes(changes, "app", 0, 4, name="E")2208 def test_proxy_bases_first(self):2209 """Bases of proxies come first."""2210 changes = self.get_changes([], [self.author_empty, self.author_proxy, self.author_proxy_proxy])2211 # Right number/type of migrations?2212 self.assertNumberMigrations(changes, 'testapp', 1)2213 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel", "CreateModel"])2214 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author")2215 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="AuthorProxy")2216 self.assertOperationAttributes(changes, 'testapp', 0, 2, name="AAuthorProxyProxy")2217 def test_pk_fk_included(self):2218 """2219 A relation used as the primary key is kept as part of CreateModel.2220 """2221 changes = self.get_changes([], [self.aardvark_pk_fk_author, self.author_name])2222 # Right number/type of migrations?2223 self.assertNumberMigrations(changes, 'testapp', 1)2224 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel"])2225 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author")2226 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="Aardvark")2227 def test_first_dependency(self):2228 """2229 A dependency to an app with no migrations uses __first__.2230 """2231 # Load graph2232 loader = MigrationLoader(connection)2233 before = self.make_project_state([])2234 after = self.make_project_state([self.book_migrations_fk])2235 after.real_apps = ["migrations"]2236 autodetector = MigrationAutodetector(before, after)2237 changes = autodetector._detect_changes(graph=loader.graph)2238 # Right number/type of migrations?2239 self.assertNumberMigrations(changes, 'otherapp', 1)2240 self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"])2241 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="Book")2242 self.assertMigrationDependencies(changes, 'otherapp', 0, [("migrations", "__first__")])2243 @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})2244 def test_last_dependency(self):2245 """2246 A dependency to an app with existing migrations uses the2247 last migration of that app.2248 """2249 # Load graph2250 loader = MigrationLoader(connection)2251 before = self.make_project_state([])2252 after = self.make_project_state([self.book_migrations_fk])2253 after.real_apps = ["migrations"]2254 autodetector = MigrationAutodetector(before, after)2255 changes = autodetector._detect_changes(graph=loader.graph)2256 # Right number/type of migrations?2257 self.assertNumberMigrations(changes, 'otherapp', 1)2258 self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"])2259 self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="Book")2260 self.assertMigrationDependencies(changes, 'otherapp', 0, [("migrations", "0002_second")])2261 def test_alter_fk_before_model_deletion(self):2262 """2263 ForeignKeys are altered _before_ the model they used to2264 refer to are deleted.2265 """2266 changes = self.get_changes(2267 [self.author_name, self.publisher_with_author],2268 [self.aardvark_testapp, self.publisher_with_aardvark_author]2269 )2270 # Right number/type of migrations?2271 self.assertNumberMigrations(changes, 'testapp', 1)2272 self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "AlterField", "DeleteModel"])2273 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Aardvark")2274 self.assertOperationAttributes(changes, 'testapp', 0, 1, name="author")2275 self.assertOperationAttributes(changes, 'testapp', 0, 2, name="Author")2276 def test_fk_dependency_other_app(self):2277 """2278 #23100 - ForeignKeys correctly depend on other apps' models.2279 """2280 changes = self.get_changes([self.author_name, self.book], [self.author_with_book, self.book])2281 # Right number/type of migrations?2282 self.assertNumberMigrations(changes, 'testapp', 1)2283 self.assertOperationTypes(changes, 'testapp', 0, ["AddField"])2284 self.assertOperationAttributes(changes, 'testapp', 0, 0, name="book")2285 self.assertMigrationDependencies(changes, 'testapp', 0, [("otherapp", "__first__")])2286 def test_alter_field_to_fk_dependency_other_app(self):2287 changes = self.get_changes(2288 [self.author_empty, self.book_with_no_author_fk],2289 [self.author_empty, self.book],2290 )2291 self.assertNumberMigrations(changes, 'otherapp', 1)2292 self.assertOperationTypes(changes, 'otherapp', 0, ['AlterField'])2293 self.assertMigrationDependencies(changes, 'otherapp', 0, [('testapp', '__first__')])2294 def test_circular_dependency_mixed_addcreate(self):2295 """2296 #23315 - The dependency resolver knows to put all CreateModel2297 before AddField and not become unsolvable.2298 """2299 address = ModelState("a", "Address", [2300 ("id", models.AutoField(primary_key=True)),2301 ("country", models.ForeignKey("b.DeliveryCountry", models.CASCADE)),2302 ])2303 person = ModelState("a", "Person", [2304 ("id", models.AutoField(primary_key=True)),2305 ])2306 apackage = ModelState("b", "APackage", [2307 ("id", models.AutoField(primary_key=True)),2308 ("person", models.ForeignKey("a.Person", models.CASCADE)),2309 ])2310 country = ModelState("b", "DeliveryCountry", [2311 ("id", models.AutoField(primary_key=True)),2312 ])2313 changes = self.get_changes([], [address, person, apackage, country])2314 # Right number/type of migrations?2315 self.assertNumberMigrations(changes, 'a', 2)2316 self.assertNumberMigrations(changes, 'b', 1)2317 self.assertOperationTypes(changes, 'a', 0, ["CreateModel", "CreateModel"])2318 self.assertOperationTypes(changes, 'a', 1, ["AddField"])2319 self.assertOperationTypes(changes, 'b', 0, ["CreateModel", "CreateModel"])2320 @override_settings(AUTH_USER_MODEL="a.Tenant")2321 def test_circular_dependency_swappable(self):2322 """2323 #23322 - The dependency resolver knows to explicitly resolve2324 swappable models.2325 """2326 with isolate_lru_cache(apps.get_swappable_settings_name):2327 tenant = ModelState("a", "Tenant", [2328 ("id", models.AutoField(primary_key=True)),2329 ("primary_address", models.ForeignKey("b.Address", models.CASCADE))],2330 bases=(AbstractBaseUser,)2331 )2332 address = ModelState("b", "Address", [2333 ("id", models.AutoField(primary_key=True)),2334 ("tenant", models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE)),2335 ])2336 changes = self.get_changes([], [address, tenant])2337 # Right number/type of migrations?2338 self.assertNumberMigrations(changes, 'a', 2)2339 self.assertOperationTypes(changes, 'a', 0, ["CreateModel"])2340 self.assertOperationTypes(changes, 'a', 1, ["AddField"])2341 self.assertMigrationDependencies(changes, 'a', 0, [])2342 self.assertMigrationDependencies(changes, 'a', 1, [('a', 'auto_1'), ('b', 'auto_1')])2343 # Right number/type of migrations?2344 self.assertNumberMigrations(changes, 'b', 1)2345 self.assertOperationTypes(changes, 'b', 0, ["CreateModel"])2346 self.assertMigrationDependencies(changes, 'b', 0, [('__setting__', 'AUTH_USER_MODEL')])2347 @override_settings(AUTH_USER_MODEL="b.Tenant")2348 def test_circular_dependency_swappable2(self):2349 """2350 #23322 - The dependency resolver knows to explicitly resolve2351 swappable models but with the swappable not being the first migrated2352 model.2353 """2354 with isolate_lru_cache(apps.get_swappable_settings_name):2355 address = ModelState("a", "Address", [2356 ("id", models.AutoField(primary_key=True)),2357 ("tenant", models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE)),2358 ])2359 tenant = ModelState("b", "Tenant", [2360 ("id", models.AutoField(primary_key=True)),2361 ("primary_address", models.ForeignKey("a.Address", models.CASCADE))],2362 bases=(AbstractBaseUser,)2363 )2364 changes = self.get_changes([], [address, tenant])2365 # Right number/type of migrations?2366 self.assertNumberMigrations(changes, 'a', 2)2367 self.assertOperationTypes(changes, 'a', 0, ["CreateModel"])2368 self.assertOperationTypes(changes, 'a', 1, ["AddField"])2369 self.assertMigrationDependencies(changes, 'a', 0, [])2370 self.assertMigrationDependencies(changes, 'a', 1, [('__setting__', 'AUTH_USER_MODEL'), ('a', 'auto_1')])2371 # Right number/type of migrations?2372 self.assertNumberMigrations(changes, 'b', 1)2373 self.assertOperationTypes(changes, 'b', 0, ["CreateModel"])2374 self.assertMigrationDependencies(changes, 'b', 0, [('a', 'auto_1')])2375 @override_settings(AUTH_USER_MODEL="a.Person")2376 def test_circular_dependency_swappable_self(self):2377 """2378 #23322 - The dependency resolver knows to explicitly resolve2379 swappable models.2380 """2381 with isolate_lru_cache(apps.get_swappable_settings_name):2382 person = ModelState("a", "Person", [2383 ("id", models.AutoField(primary_key=True)),2384 ("parent1", models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, related_name='children'))2385 ])2386 changes = self.get_changes([], [person])2387 # Right number/type of migrations?2388 self.assertNumberMigrations(changes, 'a', 1)2389 self.assertOperationTypes(changes, 'a', 0, ["CreateModel"])2390 self.assertMigrationDependencies(changes, 'a', 0, [])2391 @override_settings(AUTH_USER_MODEL='a.User')2392 def test_swappable_circular_multi_mti(self):2393 with isolate_lru_cache(apps.get_swappable_settings_name):2394 parent = ModelState('a', 'Parent', [2395 ('user', models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE))2396 ])2397 child = ModelState('a', 'Child', [], bases=('a.Parent',))2398 user = ModelState('a', 'User', [], bases=(AbstractBaseUser, 'a.Child'))2399 changes = self.get_changes([], [parent, child, user])2400 self.assertNumberMigrations(changes, 'a', 1)2401 self.assertOperationTypes(changes, 'a', 0, ['CreateModel', 'CreateModel', 'CreateModel', 'AddField'])2402 @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition',2403 side_effect=AssertionError("Should not have prompted for not null addition"))2404 def test_add_blank_textfield_and_charfield(self, mocked_ask_method):2405 """2406 #23405 - Adding a NOT NULL and blank `CharField` or `TextField`2407 without default should not prompt for a default.2408 """2409 changes = self.get_changes([self.author_empty], [self.author_with_biography_blank])2410 # Right number/type of migrations?2411 self.assertNumberMigrations(changes, 'testapp', 1)2412 self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AddField"])2413 self.assertOperationAttributes(changes, 'testapp', 0, 0)2414 @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition')2415 def test_add_non_blank_textfield_and_charfield(self, mocked_ask_method):2416 """2417 #23405 - Adding a NOT NULL and non-blank `CharField` or `TextField`2418 without default should prompt for a default.2419 """2420 changes = self.get_changes([self.author_empty], [self.author_with_biography_non_blank])2421 self.assertEqual(mocked_ask_method.call_count, 2)2422 # Right number/type of migrations?2423 self.assertNumberMigrations(changes, 'testapp', 1)2424 self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AddField"])2425 self.assertOperationAttributes(changes, 'testapp', 0, 0)2426 def test_mti_inheritance_model_removal(self):2427 Animal = ModelState('app', 'Animal', [2428 ("id", models.AutoField(primary_key=True)),2429 ])2430 Dog = ModelState('app', 'Dog', [], bases=('app.Animal',))2431 changes = self.get_changes([Animal, Dog], [Animal])2432 self.assertNumberMigrations(changes, 'app', 1)2433 self.assertOperationTypes(changes, 'app', 0, ['DeleteModel'])2434 self.assertOperationAttributes(changes, 'app', 0, 0, name='Dog')2435 def test_add_model_with_field_removed_from_base_model(self):2436 """2437 Removing a base field takes place before adding a new inherited model2438 that has a field with the same name.2439 """2440 before = [2441 ModelState('app', 'readable', [2442 ('id', models.AutoField(primary_key=True)),2443 ('title', models.CharField(max_length=200)),2444 ]),2445 ]2446 after = [2447 ModelState('app', 'readable', [2448 ('id', models.AutoField(primary_key=True)),2449 ]),2450 ModelState('app', 'book', [2451 ('title', models.CharField(max_length=200)),2452 ], bases=('app.readable',)),2453 ]2454 changes = self.get_changes(before, after)2455 self.assertNumberMigrations(changes, 'app', 1)2456 self.assertOperationTypes(changes, 'app', 0, ['RemoveField', 'CreateModel'])2457 self.assertOperationAttributes(changes, 'app', 0, 0, name='title', model_name='readable')2458 self.assertOperationAttributes(changes, 'app', 0, 1, name='book')2459class MigrationSuggestNameTests(SimpleTestCase):2460 def test_single_operation(self):2461 class Migration(migrations.Migration):2462 operations = [migrations.CreateModel('Person', fields=[])]2463 migration = Migration('0001_initial', 'test_app')2464 self.assertEqual(migration.suggest_name(), 'person')2465 class Migration(migrations.Migration):2466 operations = [migrations.DeleteModel('Person')]2467 migration = Migration('0002_initial', 'test_app')2468 self.assertEqual(migration.suggest_name(), 'delete_person')...

Full Screen

Full Screen

script.js

Source:script.js Github

copy

Full Screen

1<<<<<<< HEAD2document.addEventListener('DOMContentLoaded', function(){3 let precent = document.getElementsByClassName('precent'),4 dolar = document.getElementsByClassName('dolar'),5 switch_in = document.getElementsByClassName('switch'),6 toggle = document.getElementsByClassName('toggle-bg'),7 input_price = document.getElementsByClassName('input_price'),8 hour_change = document.getElementsByClassName('hour_change'),9 day_change = document.getElementsByClassName('day_change'),10 week_change = document.getElementsByClassName('week_change'),11 month_change = document.getElementsByClassName('month_change'),12 select = document.getElementById('option_id');13 14 var promise = $.ajax({15 url: 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCUSD'16 }).then(function (result) { 17 18 for (var i = 0; i < input_price.length; i++) {19 input_price[i].value = '$' + result.ask;20 };21 for (var i = 0; i < hour_change.length; i++) {22 hour_change[i].value = result.changes.percent.hour + '%';23 }24 for (var i = 0; i < day_change.length; i++) {25 day_change[i].value = result.changes.percent.day + '%';26 }27 for (var i = 0; i < week_change.length; i++) { 28 week_change[i].value = result.changes.percent.week + '%';29 }30 for (var i = 0; i < month_change.length; i++) {31 month_change[i].value = result.changes.percent.month + '%';32 };33 $('#option_id').change(function(){34 val = select.value;35 36 if (val == "USD"){37 var promise = $.ajax({38 url: 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCUSD'39 }).then(function (result) { 40 precent[0].addEventListener('change', function(){41 switch_in[0].classList.remove('power');42 switch_in[0].classList.add('off');43 toggle[0].style.background = "#3aa5da";44 hour_change[0].value = result.changes.percent.hour + '%';45 day_change[0].value = result.changes.percent.day + '%';46 week_change[0].value = result.changes.percent.week + '%';47 month_change[0].value = result.changes.percent.month + '%';48 });49 dolar[0].addEventListener('change', function(){50 switch_in[0].classList.remove('off');51 switch_in[0].classList.add('power');52 toggle[0].style.background = "#b0b1b2";53 hour_change[0].value = '$' + result.changes.price.hour;54 day_change[0].value = '$' + result.changes.price.day;55 week_change[0].value = '$' + result.changes.price.week;56 month_change[0].value = '$' + result.changes.price.month;57 });58 precent[1].addEventListener('change', function(){59 switch_in[1].classList.add('off');60 switch_in[1].classList.remove('power');61 toggle[1].style.background = "#3aa5da";62 hour_change[1].value = result.changes.percent.hour + '%';63 day_change[1].value = result.changes.percent.day + '%';64 week_change[1].value = result.changes.percent.week + '%';65 month_change[1].value = result.changes.percent.month + '%';66 });67 dolar[1].addEventListener('change', function(){68 switch_in[1].classList.add('power');69 switch_in[1].classList.remove('off');70 toggle[1].style.background = "#b0b1b2";71 hour_change[1].value = '$' + result.changes.price.hour;72 day_change[1].value = '$' + result.changes.price.day;73 week_change[1].value = '$' + result.changes.price.week;74 month_change[1].value = '$' + result.changes.price.month;75 });76 precent[2].addEventListener('change', function(){77 switch_in[2].classList.add('off');78 switch_in[2].classList.remove('power');79 toggle[2].style.background = "#3aa5da";80 hour_change[2].value = result.changes.percent.hour + '%';81 day_change[2].value = result.changes.percent.day + '%';82 week_change[2].value = result.changes.percent.week + '%';83 month_change[2].value = result.changes.percent.month + '%';84 });85 dolar[2].addEventListener('change', function(){86 switch_in[2].classList.add('power');87 switch_in[2].classList.remove('off');88 toggle[2].style.background = "#b0b1b2";89 hour_change[2].value = '$' + result.changes.price.hour;90 day_change[2].value = '$' + result.changes.price.day;91 week_change[2].value = '$' + result.changes.price.week;92 month_change[2].value = '$' + result.changes.price.month;93 });94 95 96 });97 98 };99 if(val == "RUB"){100 var promise = $.ajax({101 url: 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCUSD'102 }).then(function (result) { 103 precent[0].addEventListener('change', function(){104 switch_in[0].classList.remove('power');105 switch_in[0].classList.add('off');106 toggle[0].style.background = "#3aa5da";107 hour_change[0].value = result.changes.percent.hour + '%';108 day_change[0].value = result.changes.percent.day + '%';109 week_change[0].value = result.changes.percent.week + '%';110 month_change[0].value = result.changes.percent.month + '%';111 });112 dolar[0].addEventListener('change', function(){113 switch_in[0].classList.remove('off');114 switch_in[0].classList.add('power');115 toggle[0].style.background = "#b0b1b2";116 hour_change[0].value = '₽' + 67*result.changes.price.hour;117 day_change[0].value = '₽' + 67*result.changes.price.day;118 week_change[0].value = '₽' + 67*result.changes.price.week;119 month_change[0].value = '₽' + 67*result.changes.price.month;120 });121 precent[1].addEventListener('change', function(){122 switch_in[1].classList.add('off');123 switch_in[1].classList.remove('power');124 toggle[1].style.background = "#3aa5da";125 hour_change[1].value = result.changes.percent.hour + '%';126 day_change[1].value = result.changes.percent.day + '%';127 week_change[1].value = result.changes.percent.week + '%';128 month_change[1].value = result.changes.percent.month + '%';129 });130 dolar[1].addEventListener('change', function(){131 switch_in[1].classList.add('power');132 switch_in[1].classList.remove('off');133 toggle[1].style.background = "#b0b1b2";134 hour_change[1].value = '₽' + result.changes.price.hour;135 day_change[1].value = '₽' + result.changes.price.day;136 week_change[1].value = '₽' + result.changes.price.week;137 month_change[1].value = '₽' + result.changes.price.month;138 });139 precent[2].addEventListener('change', function(){140 switch_in[2].classList.add('off');141 switch_in[2].classList.remove('power');142 toggle[2].style.background = "#3aa5da";143 hour_change[2].value = result.changes.percent.hour + '%';144 day_change[2].value = result.changes.percent.day + '%';145 week_change[2].value = result.changes.percent.week + '%';146 month_change[2].value = result.changes.percent.month + '%';147 });148 dolar[2].addEventListener('change', function(){149 switch_in[2].classList.add('power');150 switch_in[2].classList.remove('off');151 toggle[2].style.background = "#b0b1b2";152 hour_change[2].value = '₽' + result.changes.price.hour;153 day_change[2].value = '₽' + result.changes.price.day;154 week_change[2].value = '₽' + result.changes.price.week;155 month_change[2].value = '₽' + result.changes.price.month;156 });157 158 159 });160 };161 if(val == "GBR"){162 var promise = $.ajax({163 url: 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCUSD'164 }).then(function (result) { 165 precent[0].addEventListener('change', function(){166 switch_in[0].classList.remove('power');167 switch_in[0].classList.add('off');168 toggle[0].style.background = "#3aa5da";169 hour_change[0].value = result.changes.percent.hour + '%';170 day_change[0].value = result.changes.percent.day + '%';171 week_change[0].value = result.changes.percent.week + '%';172 month_change[0].value = result.changes.percent.month + '%';173 });174 dolar[0].addEventListener('change', function(){175 switch_in[0].classList.remove('off');176 switch_in[0].classList.add('power');177 toggle[0].style.background = "#b0b1b2";178 hour_change[0].value = '£' + 1.30*67*result.changes.price.hour;179 day_change[0].value = '£' + 1.30*67*result.changes.price.day;180 week_change[0].value = '£' + 1.30*67*result.changes.price.week;181 month_change[0].value = '£' + 1.30*67*result.changes.price.month;182 });183 precent[1].addEventListener('change', function(){184 switch_in[1].classList.add('off');185 switch_in[1].classList.remove('power');186 toggle[1].style.background = "#3aa5da";187 hour_change[1].value = result.changes.percent.hour + '%';188 day_change[1].value = result.changes.percent.day + '%';189 week_change[1].value = result.changes.percent.week + '%';190 month_change[1].value = result.changes.percent.month + '%';191 });192 dolar[1].addEventListener('change', function(){193 switch_in[1].classList.add('power');194 switch_in[1].classList.remove('off');195 toggle[1].style.background = "#b0b1b2";196 hour_change[1].value = '£' + 1.30*result.changes.price.hour;197 day_change[1].value = '£' + 1.30*result.changes.price.day;198 week_change[1].value = '£' + 1.30*result.changes.price.week;199 month_change[1].value = '£' + 1.30*result.changes.price.month;200 });201 precent[2].addEventListener('change', function(){202 switch_in[2].classList.add('off');203 switch_in[2].classList.remove('power');204 toggle[2].style.background = "#3aa5da";205 hour_change[2].value = result.changes.percent.hour + '%';206 day_change[2].value = result.changes.percent.day + '%';207 week_change[2].value = result.changes.percent.week + '%';208 month_change[2].value = result.changes.percent.month + '%';209 });210 dolar[2].addEventListener('change', function(){211 switch_in[2].classList.add('power');212 switch_in[2].classList.remove('off');213 toggle[2].style.background = "#b0b1b2";214 hour_change[2].value = '£' + 1.30*result.changes.price.hour;215 day_change[2].value = '£' + 1.30*result.changes.price.day;216 week_change[2].value = '£' + 1.30*result.changes.price.week;217 month_change[2].value = '£' + 1.30*result.changes.price.month;218 });219 220 221 });222 };223 224 });225 precent[0].addEventListener('change', function(){226 switch_in[0].classList.remove('power');227 switch_in[0].classList.add('off');228 toggle[0].style.background = "#3aa5da";229 hour_change[0].value = result.changes.percent.hour + '%';230 day_change[0].value = result.changes.percent.day + '%';231 week_change[0].value = result.changes.percent.week + '%';232 month_change[0].value = result.changes.percent.month + '%';233 });234 dolar[0].addEventListener('change', function(){235 switch_in[0].classList.remove('off');236 switch_in[0].classList.add('power');237 toggle[0].style.background = "#b0b1b2";238 hour_change[0].value = '$' + result.changes.price.hour;239 day_change[0].value = '$' + result.changes.price.day;240 week_change[0].value = '$' + result.changes.price.week;241 month_change[0].value = '$' + result.changes.price.month;242 });243 precent[1].addEventListener('change', function(){244 switch_in[1].classList.add('off');245 switch_in[1].classList.remove('power');246 toggle[1].style.background = "#3aa5da";247 hour_change[1].value = result.changes.percent.hour + '%';248 day_change[1].value = result.changes.percent.day + '%';249 week_change[1].value = result.changes.percent.week + '%';250 month_change[1].value = result.changes.percent.month + '%';251 });252 dolar[1].addEventListener('change', function(){253 switch_in[1].classList.add('power');254 switch_in[1].classList.remove('off');255 toggle[1].style.background = "#b0b1b2";256 hour_change[1].value = '$' + result.changes.price.hour;257 day_change[1].value = '$' + result.changes.price.day;258 week_change[1].value = '$' + result.changes.price.week;259 month_change[1].value = '$' + result.changes.price.month;260 });261 precent[2].addEventListener('change', function(){262 switch_in[2].classList.add('off');263 switch_in[2].classList.remove('power');264 toggle[2].style.background = "#3aa5da";265 hour_change[2].value = result.changes.percent.hour + '%';266 day_change[2].value = result.changes.percent.day + '%';267 week_change[2].value = result.changes.percent.week + '%';268 month_change[2].value = result.changes.percent.month + '%';269 });270 dolar[2].addEventListener('change', function(){271 switch_in[2].classList.add('power');272 switch_in[2].classList.remove('off');273 toggle[2].style.background = "#b0b1b2";274 hour_change[2].value = '$' + result.changes.price.hour;275 day_change[2].value = '$' + result.changes.price.day;276 week_change[2].value = '$' + result.changes.price.week;277 month_change[2].value = '$' + result.changes.price.month;278 });279 280 }).catch(function (err) {281 console.log('err', err)282 })283 284});285=======286document.addEventListener('DOMContentLoaded', function(){287 let precent = document.getElementsByClassName('precent'),288 dolar = document.getElementsByClassName('dolar'),289 switch_in = document.getElementsByClassName('switch'),290 toggle = document.getElementsByClassName('toggle-bg'),291 input_price = document.getElementsByClassName('input_price'),292 hour_change = document.getElementsByClassName('hour_change'),293 day_change = document.getElementsByClassName('day_change'),294 week_change = document.getElementsByClassName('week_change'),295 month_change = document.getElementsByClassName('month_change'),296 select = document.getElementById('option_id');297 298 var promise = $.ajax({299 url: 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCUSD'300 }).then(function (result) { 301 302 for (var i = 0; i < input_price.length; i++) {303 input_price[i].value = '$' + result.ask;304 };305 for (var i = 0; i < hour_change.length; i++) {306 hour_change[i].value = result.changes.percent.hour + '%';307 }308 for (var i = 0; i < day_change.length; i++) {309 day_change[i].value = result.changes.percent.day + '%';310 }311 for (var i = 0; i < week_change.length; i++) { 312 week_change[i].value = result.changes.percent.week + '%';313 }314 for (var i = 0; i < month_change.length; i++) {315 month_change[i].value = result.changes.percent.month + '%';316 };317 $('#option_id').change(function(){318 val = select.value;319 320 if (val == "USD"){321 var promise = $.ajax({322 url: 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCUSD'323 }).then(function (result) { 324 precent[0].addEventListener('change', function(){325 switch_in[0].classList.remove('power');326 switch_in[0].classList.add('off');327 toggle[0].style.background = "#3aa5da";328 hour_change[0].value = result.changes.percent.hour + '%';329 day_change[0].value = result.changes.percent.day + '%';330 week_change[0].value = result.changes.percent.week + '%';331 month_change[0].value = result.changes.percent.month + '%';332 });333 dolar[0].addEventListener('change', function(){334 switch_in[0].classList.remove('off');335 switch_in[0].classList.add('power');336 toggle[0].style.background = "#b0b1b2";337 hour_change[0].value = '$' + result.changes.price.hour;338 day_change[0].value = '$' + result.changes.price.day;339 week_change[0].value = '$' + result.changes.price.week;340 month_change[0].value = '$' + result.changes.price.month;341 });342 precent[1].addEventListener('change', function(){343 switch_in[1].classList.add('off');344 switch_in[1].classList.remove('power');345 toggle[1].style.background = "#3aa5da";346 hour_change[1].value = result.changes.percent.hour + '%';347 day_change[1].value = result.changes.percent.day + '%';348 week_change[1].value = result.changes.percent.week + '%';349 month_change[1].value = result.changes.percent.month + '%';350 });351 dolar[1].addEventListener('change', function(){352 switch_in[1].classList.add('power');353 switch_in[1].classList.remove('off');354 toggle[1].style.background = "#b0b1b2";355 hour_change[1].value = '$' + result.changes.price.hour;356 day_change[1].value = '$' + result.changes.price.day;357 week_change[1].value = '$' + result.changes.price.week;358 month_change[1].value = '$' + result.changes.price.month;359 });360 precent[2].addEventListener('change', function(){361 switch_in[2].classList.add('off');362 switch_in[2].classList.remove('power');363 toggle[2].style.background = "#3aa5da";364 hour_change[2].value = result.changes.percent.hour + '%';365 day_change[2].value = result.changes.percent.day + '%';366 week_change[2].value = result.changes.percent.week + '%';367 month_change[2].value = result.changes.percent.month + '%';368 });369 dolar[2].addEventListener('change', function(){370 switch_in[2].classList.add('power');371 switch_in[2].classList.remove('off');372 toggle[2].style.background = "#b0b1b2";373 hour_change[2].value = '$' + result.changes.price.hour;374 day_change[2].value = '$' + result.changes.price.day;375 week_change[2].value = '$' + result.changes.price.week;376 month_change[2].value = '$' + result.changes.price.month;377 });378 379 380 });381 382 };383 if(val == "RUB"){384 var promise = $.ajax({385 url: 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCUSD'386 }).then(function (result) { 387 precent[0].addEventListener('change', function(){388 switch_in[0].classList.remove('power');389 switch_in[0].classList.add('off');390 toggle[0].style.background = "#3aa5da";391 hour_change[0].value = result.changes.percent.hour + '%';392 day_change[0].value = result.changes.percent.day + '%';393 week_change[0].value = result.changes.percent.week + '%';394 month_change[0].value = result.changes.percent.month + '%';395 });396 dolar[0].addEventListener('change', function(){397 switch_in[0].classList.remove('off');398 switch_in[0].classList.add('power');399 toggle[0].style.background = "#b0b1b2";400 hour_change[0].value = '₽' + 67*result.changes.price.hour;401 day_change[0].value = '₽' + 67*result.changes.price.day;402 week_change[0].value = '₽' + 67*result.changes.price.week;403 month_change[0].value = '₽' + 67*result.changes.price.month;404 });405 precent[1].addEventListener('change', function(){406 switch_in[1].classList.add('off');407 switch_in[1].classList.remove('power');408 toggle[1].style.background = "#3aa5da";409 hour_change[1].value = result.changes.percent.hour + '%';410 day_change[1].value = result.changes.percent.day + '%';411 week_change[1].value = result.changes.percent.week + '%';412 month_change[1].value = result.changes.percent.month + '%';413 });414 dolar[1].addEventListener('change', function(){415 switch_in[1].classList.add('power');416 switch_in[1].classList.remove('off');417 toggle[1].style.background = "#b0b1b2";418 hour_change[1].value = '₽' + result.changes.price.hour;419 day_change[1].value = '₽' + result.changes.price.day;420 week_change[1].value = '₽' + result.changes.price.week;421 month_change[1].value = '₽' + result.changes.price.month;422 });423 precent[2].addEventListener('change', function(){424 switch_in[2].classList.add('off');425 switch_in[2].classList.remove('power');426 toggle[2].style.background = "#3aa5da";427 hour_change[2].value = result.changes.percent.hour + '%';428 day_change[2].value = result.changes.percent.day + '%';429 week_change[2].value = result.changes.percent.week + '%';430 month_change[2].value = result.changes.percent.month + '%';431 });432 dolar[2].addEventListener('change', function(){433 switch_in[2].classList.add('power');434 switch_in[2].classList.remove('off');435 toggle[2].style.background = "#b0b1b2";436 hour_change[2].value = '₽' + result.changes.price.hour;437 day_change[2].value = '₽' + result.changes.price.day;438 week_change[2].value = '₽' + result.changes.price.week;439 month_change[2].value = '₽' + result.changes.price.month;440 });441 442 443 });444 };445 if(val == "GBR"){446 var promise = $.ajax({447 url: 'https://apiv2.bitcoinaverage.com/indices/global/ticker/BTCUSD'448 }).then(function (result) { 449 precent[0].addEventListener('change', function(){450 switch_in[0].classList.remove('power');451 switch_in[0].classList.add('off');452 toggle[0].style.background = "#3aa5da";453 hour_change[0].value = result.changes.percent.hour + '%';454 day_change[0].value = result.changes.percent.day + '%';455 week_change[0].value = result.changes.percent.week + '%';456 month_change[0].value = result.changes.percent.month + '%';457 });458 dolar[0].addEventListener('change', function(){459 switch_in[0].classList.remove('off');460 switch_in[0].classList.add('power');461 toggle[0].style.background = "#b0b1b2";462 hour_change[0].value = '£' + 1.30*67*result.changes.price.hour;463 day_change[0].value = '£' + 1.30*67*result.changes.price.day;464 week_change[0].value = '£' + 1.30*67*result.changes.price.week;465 month_change[0].value = '£' + 1.30*67*result.changes.price.month;466 });467 precent[1].addEventListener('change', function(){468 switch_in[1].classList.add('off');469 switch_in[1].classList.remove('power');470 toggle[1].style.background = "#3aa5da";471 hour_change[1].value = result.changes.percent.hour + '%';472 day_change[1].value = result.changes.percent.day + '%';473 week_change[1].value = result.changes.percent.week + '%';474 month_change[1].value = result.changes.percent.month + '%';475 });476 dolar[1].addEventListener('change', function(){477 switch_in[1].classList.add('power');478 switch_in[1].classList.remove('off');479 toggle[1].style.background = "#b0b1b2";480 hour_change[1].value = '£' + 1.30*result.changes.price.hour;481 day_change[1].value = '£' + 1.30*result.changes.price.day;482 week_change[1].value = '£' + 1.30*result.changes.price.week;483 month_change[1].value = '£' + 1.30*result.changes.price.month;484 });485 precent[2].addEventListener('change', function(){486 switch_in[2].classList.add('off');487 switch_in[2].classList.remove('power');488 toggle[2].style.background = "#3aa5da";489 hour_change[2].value = result.changes.percent.hour + '%';490 day_change[2].value = result.changes.percent.day + '%';491 week_change[2].value = result.changes.percent.week + '%';492 month_change[2].value = result.changes.percent.month + '%';493 });494 dolar[2].addEventListener('change', function(){495 switch_in[2].classList.add('power');496 switch_in[2].classList.remove('off');497 toggle[2].style.background = "#b0b1b2";498 hour_change[2].value = '£' + 1.30*result.changes.price.hour;499 day_change[2].value = '£' + 1.30*result.changes.price.day;500 week_change[2].value = '£' + 1.30*result.changes.price.week;501 month_change[2].value = '£' + 1.30*result.changes.price.month;502 });503 504 505 });506 };507 508 });509 precent[0].addEventListener('change', function(){510 switch_in[0].classList.remove('power');511 switch_in[0].classList.add('off');512 toggle[0].style.background = "#3aa5da";513 hour_change[0].value = result.changes.percent.hour + '%';514 day_change[0].value = result.changes.percent.day + '%';515 week_change[0].value = result.changes.percent.week + '%';516 month_change[0].value = result.changes.percent.month + '%';517 });518 dolar[0].addEventListener('change', function(){519 switch_in[0].classList.remove('off');520 switch_in[0].classList.add('power');521 toggle[0].style.background = "#b0b1b2";522 hour_change[0].value = '$' + result.changes.price.hour;523 day_change[0].value = '$' + result.changes.price.day;524 week_change[0].value = '$' + result.changes.price.week;525 month_change[0].value = '$' + result.changes.price.month;526 });527 precent[1].addEventListener('change', function(){528 switch_in[1].classList.add('off');529 switch_in[1].classList.remove('power');530 toggle[1].style.background = "#3aa5da";531 hour_change[1].value = result.changes.percent.hour + '%';532 day_change[1].value = result.changes.percent.day + '%';533 week_change[1].value = result.changes.percent.week + '%';534 month_change[1].value = result.changes.percent.month + '%';535 });536 dolar[1].addEventListener('change', function(){537 switch_in[1].classList.add('power');538 switch_in[1].classList.remove('off');539 toggle[1].style.background = "#b0b1b2";540 hour_change[1].value = '$' + result.changes.price.hour;541 day_change[1].value = '$' + result.changes.price.day;542 week_change[1].value = '$' + result.changes.price.week;543 month_change[1].value = '$' + result.changes.price.month;544 });545 precent[2].addEventListener('change', function(){546 switch_in[2].classList.add('off');547 switch_in[2].classList.remove('power');548 toggle[2].style.background = "#3aa5da";549 hour_change[2].value = result.changes.percent.hour + '%';550 day_change[2].value = result.changes.percent.day + '%';551 week_change[2].value = result.changes.percent.week + '%';552 month_change[2].value = result.changes.percent.month + '%';553 });554 dolar[2].addEventListener('change', function(){555 switch_in[2].classList.add('power');556 switch_in[2].classList.remove('off');557 toggle[2].style.background = "#b0b1b2";558 hour_change[2].value = '$' + result.changes.price.hour;559 day_change[2].value = '$' + result.changes.price.day;560 week_change[2].value = '$' + result.changes.price.week;561 month_change[2].value = '$' + result.changes.price.month;562 });563 564 }).catch(function (err) {565 console.log('err', err)566 })567 568});...

Full Screen

Full Screen

test_collection.py

Source:test_collection.py Github

copy

Full Screen

...4import voluptuous as vol5from homeassistant.helpers import collection, entity, entity_component, storage6from tests.common import flush_store7_LOGGER = logging.getLogger(__name__)8def track_changes(coll: collection.ObservableCollection):9 """Create helper to track changes in a collection."""10 changes = []11 async def listener(*args):12 changes.append(args)13 coll.async_add_listener(listener)14 return changes15class MockEntity(entity.Entity):16 """Entity that is config based."""17 def __init__(self, config):18 """Initialize entity."""19 self._config = config20 @property21 def unique_id(self):22 """Return unique ID of entity."""23 return self._config["id"]24 @property25 def name(self):26 """Return name of entity."""27 return self._config["name"]28 @property29 def state(self):30 """Return state of entity."""31 return self._config["state"]32 async def async_update_config(self, config):33 """Update entity config."""34 self._config = config35 self.async_write_ha_state()36class MockStorageCollection(collection.StorageCollection):37 """Mock storage collection."""38 async def _process_create_data(self, data: dict) -> dict:39 """Validate the config is valid."""40 if "name" not in data:41 raise ValueError("invalid")42 return data43 def _get_suggested_id(self, info: dict) -> str:44 """Suggest an ID based on the config."""45 return info["name"]46 async def _update_data(self, data: dict, update_data: dict) -> dict:47 """Return a new updated data object."""48 return {**data, **update_data}49def test_id_manager():50 """Test the ID manager."""51 id_manager = collection.IDManager()52 assert not id_manager.has_id("some_id")53 data = {}54 id_manager.add_collection(data)55 assert not id_manager.has_id("some_id")56 data["some_id"] = 157 assert id_manager.has_id("some_id")58 assert id_manager.generate_id("some_id") == "some_id_2"59 assert id_manager.generate_id("bla") == "bla"60async def test_observable_collection():61 """Test observerable collection."""62 coll = collection.ObservableCollection(_LOGGER)63 assert coll.async_items() == []64 coll.data["bla"] = 165 assert coll.async_items() == [1]66 changes = track_changes(coll)67 await coll.notify_changes(68 [collection.CollectionChangeSet("mock_type", "mock_id", {"mock": "item"})]69 )70 assert len(changes) == 171 assert changes[0] == ("mock_type", "mock_id", {"mock": "item"})72async def test_yaml_collection():73 """Test a YAML collection."""74 id_manager = collection.IDManager()75 coll = collection.YamlCollection(_LOGGER, id_manager)76 changes = track_changes(coll)77 await coll.async_load(78 [{"id": "mock-1", "name": "Mock 1"}, {"id": "mock-2", "name": "Mock 2"}]79 )80 assert id_manager.has_id("mock-1")81 assert id_manager.has_id("mock-2")82 assert len(changes) == 283 assert changes[0] == (84 collection.CHANGE_ADDED,85 "mock-1",86 {"id": "mock-1", "name": "Mock 1"},87 )88 assert changes[1] == (89 collection.CHANGE_ADDED,90 "mock-2",91 {"id": "mock-2", "name": "Mock 2"},92 )93 # Test loading new data. Mock 1 is updated, 2 removed, 3 added.94 await coll.async_load(95 [{"id": "mock-1", "name": "Mock 1-updated"}, {"id": "mock-3", "name": "Mock 3"}]96 )97 assert len(changes) == 598 assert changes[2] == (99 collection.CHANGE_UPDATED,100 "mock-1",101 {"id": "mock-1", "name": "Mock 1-updated"},102 )103 assert changes[3] == (104 collection.CHANGE_ADDED,105 "mock-3",106 {"id": "mock-3", "name": "Mock 3"},107 )108 assert changes[4] == (109 collection.CHANGE_REMOVED,110 "mock-2",111 {"id": "mock-2", "name": "Mock 2"},112 )113async def test_yaml_collection_skipping_duplicate_ids():114 """Test YAML collection skipping duplicate IDs."""115 id_manager = collection.IDManager()116 id_manager.add_collection({"existing": True})117 coll = collection.YamlCollection(_LOGGER, id_manager)118 changes = track_changes(coll)119 await coll.async_load(120 [{"id": "mock-1", "name": "Mock 1"}, {"id": "existing", "name": "Mock 2"}]121 )122 assert len(changes) == 1123 assert changes[0] == (124 collection.CHANGE_ADDED,125 "mock-1",126 {"id": "mock-1", "name": "Mock 1"},127 )128async def test_storage_collection(hass):129 """Test storage collection."""130 store = storage.Store(hass, 1, "test-data")131 await store.async_save(132 {133 "items": [134 {"id": "mock-1", "name": "Mock 1", "data": 1},135 {"id": "mock-2", "name": "Mock 2", "data": 2},136 ]137 }138 )139 id_manager = collection.IDManager()140 coll = MockStorageCollection(store, _LOGGER, id_manager)141 changes = track_changes(coll)142 await coll.async_load()143 assert id_manager.has_id("mock-1")144 assert id_manager.has_id("mock-2")145 assert len(changes) == 2146 assert changes[0] == (147 collection.CHANGE_ADDED,148 "mock-1",149 {"id": "mock-1", "name": "Mock 1", "data": 1},150 )151 assert changes[1] == (152 collection.CHANGE_ADDED,153 "mock-2",154 {"id": "mock-2", "name": "Mock 2", "data": 2},155 )156 item = await coll.async_create_item({"name": "Mock 3"})157 assert item["id"] == "mock_3"158 assert len(changes) == 3159 assert changes[2] == (160 collection.CHANGE_ADDED,161 "mock_3",162 {"id": "mock_3", "name": "Mock 3"},163 )164 updated_item = await coll.async_update_item("mock-2", {"name": "Mock 2 updated"})165 assert id_manager.has_id("mock-2")166 assert updated_item == {"id": "mock-2", "name": "Mock 2 updated", "data": 2}167 assert len(changes) == 4168 assert changes[3] == (collection.CHANGE_UPDATED, "mock-2", updated_item)169 with pytest.raises(ValueError):170 await coll.async_update_item("mock-2", {"id": "mock-2-updated"})171 assert id_manager.has_id("mock-2")172 assert not id_manager.has_id("mock-2-updated")173 assert len(changes) == 4174 await flush_store(store)175 assert await storage.Store(hass, 1, "test-data").async_load() == {176 "items": [177 {"id": "mock-1", "name": "Mock 1", "data": 1},178 {"id": "mock-2", "name": "Mock 2 updated", "data": 2},179 {"id": "mock_3", "name": "Mock 3"},180 ]181 }182async def test_attach_entity_component_collection(hass):183 """Test attaching collection to entity component."""184 ent_comp = entity_component.EntityComponent(_LOGGER, "test", hass)185 coll = collection.ObservableCollection(_LOGGER)186 collection.sync_entity_lifecycle(hass, "test", "test", ent_comp, coll, MockEntity)187 await coll.notify_changes(188 [189 collection.CollectionChangeSet(190 collection.CHANGE_ADDED,191 "mock_id",192 {"id": "mock_id", "state": "initial", "name": "Mock 1"},193 )194 ],195 )196 assert hass.states.get("test.mock_1").name == "Mock 1"197 assert hass.states.get("test.mock_1").state == "initial"198 await coll.notify_changes(199 [200 collection.CollectionChangeSet(201 collection.CHANGE_UPDATED,202 "mock_id",203 {"id": "mock_id", "state": "second", "name": "Mock 1 updated"},204 )205 ],206 )207 assert hass.states.get("test.mock_1").name == "Mock 1 updated"208 assert hass.states.get("test.mock_1").state == "second"209 await coll.notify_changes(210 [collection.CollectionChangeSet(collection.CHANGE_REMOVED, "mock_id", None)],211 )212 assert hass.states.get("test.mock_1") is None213async def test_storage_collection_websocket(hass, hass_ws_client):214 """Test exposing a storage collection via websockets."""215 store = storage.Store(hass, 1, "test-data")216 coll = MockStorageCollection(store, _LOGGER)217 changes = track_changes(coll)218 collection.StorageCollectionWebsocket(219 coll,220 "test_item/collection",221 "test_item",222 {vol.Required("name"): str, vol.Required("immutable_string"): str},223 {vol.Optional("name"): str},224 ).async_setup(hass)225 client = await hass_ws_client(hass)226 # Create invalid227 await client.send_json(228 {229 "id": 1,230 "type": "test_item/collection/create",231 "name": 1,...

Full Screen

Full Screen

Callout.js

Source:Callout.js Github

copy

Full Screen

1/**2 * @class Ext.chart.label.Callout3 * @extends Ext.draw.modifier.Modifier4 *5 * This is a modifier to place labels and callouts by additional attributes.6 */7Ext.define('Ext.chart.label.Callout', {8 extend: 'Ext.draw.modifier.Modifier',9 prepareAttributes: function (attr) {10 if (!attr.hasOwnProperty('calloutOriginal')) {11 attr.calloutOriginal = Ext.Object.chain(attr);12 // No __proto__, nor getPrototypeOf in IE8,13 // so manually saving a reference to 'attr' after chaining.14 attr.calloutOriginal.prototype = attr;15 }16 if (this._previous) {17 this._previous.prepareAttributes(attr.calloutOriginal);18 }19 },20 setAttrs: function (attr, changes) {21 var callout = attr.callout,22 origin = attr.calloutOriginal,23 bbox = attr.bbox.plain,24 width = (bbox.width || 0) + attr.labelOverflowPadding,25 height = (bbox.height || 0) + attr.labelOverflowPadding,26 dx, dy;27 if ('callout' in changes) {28 callout = changes.callout;29 }30 if ('callout' in changes || 'calloutPlaceX' in changes || 'calloutPlaceY' in changes || 'x' in changes || 'y' in changes) {31 var rotationRads = 'rotationRads' in changes ? origin.rotationRads = changes.rotationRads : origin.rotationRads,32 x = 'x' in changes ? (origin.x = changes.x) : origin.x,33 y = 'y' in changes ? (origin.y = changes.y) : origin.y,34 calloutPlaceX = 'calloutPlaceX' in changes ? changes.calloutPlaceX : attr.calloutPlaceX,35 calloutPlaceY = 'calloutPlaceY' in changes ? changes.calloutPlaceY : attr.calloutPlaceY,36 calloutVertical = 'calloutVertical' in changes ? changes.calloutVertical : attr.calloutVertical,37 temp;38 // Normalize Rotations39 rotationRads %= Math.PI * 2;40 if (Math.cos(rotationRads) < 0) {41 rotationRads = (rotationRads + Math.PI) % (Math.PI * 2);42 }43 if (rotationRads > Math.PI) {44 rotationRads -= Math.PI * 2;45 }46 if (calloutVertical) {47 rotationRads = rotationRads * (1 - callout) - Math.PI / 2 * callout;48 temp = width;49 width = height;50 height = temp;51 } else {52 rotationRads = rotationRads * (1 - callout);53 }54 changes.rotationRads = rotationRads;55 // Placing a label in the middle of a pie slice (x/y)56 // if callout doesn't exists (callout=0),57 // or outside the pie slice (calloutPlaceX/Y) if it does (callout=1).58 changes.x = x * (1 - callout) + calloutPlaceX * callout;59 changes.y = y * (1 - callout) + calloutPlaceY * callout;60 dx = calloutPlaceX - x;61 dy = calloutPlaceY - y;62 // Finding where the callout line intersects the bbox of the label63 // if it were to go to the center of the label,64 // and make that intersection point the end of the callout line.65 // Effectively, the end of the callout line traces label's bbox when chart is rotated.66 if (Math.abs(dy * width) > Math.abs(dx * height)) {67 // on top/bottom68 if (dy > 0) {69 changes.calloutEndX = changes.x - (height / 2) * (dx / dy) * callout;70 changes.calloutEndY = changes.y - (height / 2) * callout;71 } else {72 changes.calloutEndX = changes.x + (height / 2) * (dx / dy) * callout;73 changes.calloutEndY = changes.y + (height / 2) * callout;74 }75 } else {76 // on left/right77 if (dx > 0) {78 changes.calloutEndX = changes.x - width / 2;79 changes.calloutEndY = changes.y - (width / 2) * (dy / dx) * callout;80 } else {81 changes.calloutEndX = changes.x + width / 2;82 changes.calloutEndY = changes.y + (width / 2) * (dy / dx) * callout;83 }84 }85 // Since the length of the callout line is adjusted depending on the label's position86 // and dimensions, we hide the callout line if the length becomes negative.87 if (changes.calloutStartX && changes.calloutStartY) {88 changes.calloutHasLine =89 (dx > 0 && changes.calloutStartX < changes.calloutEndX) ||90 (dx <= 0 && changes.calloutStartX > changes.calloutEndX) ||91 (dy > 0 && changes.calloutStartY < changes.calloutEndY) ||92 (dy <= 0 && changes.calloutStartY > changes.calloutEndY);93 } else {94 changes.calloutHasLine = true;95 }96 }97 return changes;98 },99 pushDown: function (attr, changes) {100 changes = Ext.draw.modifier.Modifier.prototype.pushDown.call(this, attr.calloutOriginal, changes);101 return this.setAttrs(attr, changes);102 },103 popUp: function (attr, changes) {104 attr = attr.prototype;105 changes = this.setAttrs(attr, changes);106 if (this._next) {107 return this._next.popUp(attr, changes);108 } else {109 return Ext.apply(attr, changes);110 }111 }...

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run localstack automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful