def test_update_field_with_type_error_on_conversion_should_null_field( data_fixture): class AlwaysThrowsSqlExceptionOnConversionField(TextFieldType): type = 'throws_field' model_class = LongTextField def get_alter_column_prepare_new_value(self, connection, from_field, to_field): return '''p_in = (lower(p_in::numeric::text));''' user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) existing_text_field = data_fixture.create_text_field(table=table, order=1) model = table.get_model() field_name = f'field_{existing_text_field.id}' row = model.objects.create(**{ field_name: 'Test', }) handler = FieldHandler() with patch.dict( field_type_registry.registry, {'throws_field': AlwaysThrowsSqlExceptionOnConversionField()}): handler.update_field(user=user, field=existing_text_field, new_type_name='throws_field') row.refresh_from_db() assert getattr(row, field_name) is None assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 0 assert LongTextField.objects.all().count() == 1
def test_alter_number_field_column_type(expected, field_kwargs, data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field = data_fixture.create_text_field(table=table, order=1) handler = FieldHandler() field = handler.update_field(user=user, field=field, name='Text field') model = table.get_model() model.objects.create(**{f'field_{field.id}': '100'}) model.objects.create(**{f'field_{field.id}': '100.22'}) model.objects.create(**{f'field_{field.id}': '100.59999'}) model.objects.create(**{f'field_{field.id}': '-100'}) model.objects.create(**{f'field_{field.id}': '-100.22'}) model.objects.create(**{f'field_{field.id}': '-100.5999'}) model.objects.create(**{f'field_{field.id}': '100.59.99'}) model.objects.create(**{f'field_{field.id}': '-100.59.99'}) model.objects.create(**{f'field_{field.id}': '100TEST100.10'}) model.objects.create(**{f'field_{field.id}': '!@#$%%^^&&^^%$$'}) model.objects.create(**{f'field_{field.id}': '!@#$%%^^5.2&&^^%$$'}) # Change the field type to a number and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name='number', **field_kwargs) model = table.get_model() rows = model.objects.all() for index, row in enumerate(rows): assert getattr(row, f'field_{field.id}') == expected[index]
def test_alter_number_field_column_type(expected, field_kwargs, data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field = data_fixture.create_text_field(table=table, order=1) handler = FieldHandler() field = handler.update_field(user=user, field=field, name="Text field") model = table.get_model() model.objects.create(**{f"field_{field.id}": "9223372036854775807"}) model.objects.create(**{f"field_{field.id}": "100"}) model.objects.create(**{f"field_{field.id}": "100.22"}) model.objects.create(**{f"field_{field.id}": "100.59999"}) model.objects.create(**{f"field_{field.id}": "-9223372036854775808"}) model.objects.create(**{f"field_{field.id}": "-100"}) model.objects.create(**{f"field_{field.id}": "-100.22"}) model.objects.create(**{f"field_{field.id}": "-100.5999"}) model.objects.create(**{f"field_{field.id}": "100.59.99"}) model.objects.create(**{f"field_{field.id}": "-100.59.99"}) model.objects.create(**{f"field_{field.id}": "100TEST100.10"}) model.objects.create(**{f"field_{field.id}": "!@#$%%^^&&^^%$$"}) model.objects.create(**{f"field_{field.id}": "!@#$%%^^5.2&&^^%$$"}) # Change the field type to a number and test if the values have been changed. field = handler.update_field( user=user, field=field, new_type_name="number", **field_kwargs ) model = table.get_model() rows = model.objects.all() for index, row in enumerate(rows): assert getattr(row, f"field_{field.id}") == expected[index]
def test_alter_number_field_column_type_negative(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) number_field = data_fixture.create_number_field(table=table, order=1, number_negative=True) decimal_field = data_fixture.create_number_field(table=table, order=2, number_type='DECIMAL', number_negative=True, number_decimal_places=2) model = table.get_model() model.objects.create( **{ f'field_{number_field.id}': -10, f'field_{decimal_field.id}': Decimal('-10.10') }) handler = FieldHandler() number_field = handler.update_field(user=user, field=number_field, number_negative=False) decimal_field = handler.update_field(user=user, field=decimal_field, number_negative=False) model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{number_field.id}') == 0 assert getattr(rows[0], f'field_{decimal_field.id}') == 0.00
def test_when_field_type_forces_same_type_alter_fields_alter_sql_is_run( data_fixture): class SameTypeAlwaysReverseOnUpdateField(TextFieldType): def get_alter_column_prepare_new_value(self, connection, from_field, to_field): return """p_in = (reverse(p_in));""" def force_same_type_alter_column(self, from_field, to_field): return True user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) existing_text_field = data_fixture.create_text_field(table=table, order=1) model = table.get_model() field_name = f"field_{existing_text_field.id}" row = model.objects.create(**{ field_name: "Test", }) handler = FieldHandler() with patch.dict(field_type_registry.registry, {"text": SameTypeAlwaysReverseOnUpdateField()}): handler.update_field(user=user, field=existing_text_field, new_type_name="text", name="new_name") row.refresh_from_db() # The alter sql has been run due to the force override assert getattr(row, field_name) == "tseT" assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 1
def test_update_field_when_underlying_sql_type_doesnt_change(data_fixture): class AlwaysLowercaseTextField(TextFieldType): type = "lowercase_text" model_class = LongTextField def get_alter_column_prepare_new_value(self, connection, from_field, to_field): return """p_in = (lower(p_in));""" user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) existing_text_field = data_fixture.create_text_field(table=table, order=1) model = table.get_model() field_name = f"field_{existing_text_field.id}" row = model.objects.create(**{ field_name: "Test", }) handler = FieldHandler() with patch.dict(field_type_registry.registry, {"lowercase_text": AlwaysLowercaseTextField()}): handler.update_field(user=user, field=existing_text_field, new_type_name="lowercase_text") row.refresh_from_db() assert getattr(row, field_name) == "test" assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 0 assert LongTextField.objects.all().count() == 1
def test_just_changing_a_fields_name_will_not_run_alter_sql(data_fixture): class AlwaysReverseOnUpdateField(TextFieldType): def get_alter_column_prepare_new_value(self, connection, from_field, to_field): return """p_in = (reverse(p_in));""" user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) existing_text_field = data_fixture.create_text_field(table=table, order=1) model = table.get_model() field_name = f"field_{existing_text_field.id}" row = model.objects.create(**{ field_name: "Test", }) handler = FieldHandler() with patch.dict(field_type_registry.registry, {"text": AlwaysReverseOnUpdateField()}): handler.update_field(user=user, field=existing_text_field, new_type_name="text", name="new_name") row.refresh_from_db() # The field has not been reversed as just the name changed! assert getattr(row, field_name) == "Test" assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 1
def test_field_type_changed(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table(user=user, database=table.database) text_field = data_fixture.create_text_field(table=table) grid_view = data_fixture.create_grid_view(table=table) data_fixture.create_view_filter(view=grid_view, field=text_field, type='contains', value='test') data_fixture.create_view_sort(view=grid_view, field=text_field, order='ASC') field_handler = FieldHandler() long_text_field = field_handler.update_field(user=user, field=text_field, new_type_name='long_text') assert ViewFilter.objects.all().count() == 1 assert ViewSort.objects.all().count() == 1 field_handler.update_field(user=user, field=long_text_field, new_type_name='number') assert ViewFilter.objects.all().count() == 0 assert ViewSort.objects.all().count() == 1 field_handler.update_field(user=user, field=long_text_field, new_type_name='link_row', link_row_table=table_2) assert ViewFilter.objects.all().count() == 0 assert ViewSort.objects.all().count() == 0
def test_update_field_when_underlying_sql_type_doesnt_change_with_vars( data_fixture): class ReversesWhenConvertsAwayTextField(LongTextFieldType): type = "reserves_text" model_class = LongTextField def get_alter_column_prepare_old_value(self, connection, from_field, to_field): return """p_in = concat(reverse(p_in), %(some_variable)s);""", { "some_variable": "_POST_FIX" } class AlwaysLowercaseTextField(TextFieldType): type = "lowercase_text" model_class = LongTextField def get_alter_column_prepare_new_value(self, connection, from_field, to_field): return """p_in = concat(%(other_variable)s, lower(p_in));""", { "other_variable": "pre_fix_" } user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) existing_field_with_old_value_prep = data_fixture.create_long_text_field( table=table) model = table.get_model() field_name = f"field_{existing_field_with_old_value_prep.id}" row = model.objects.create(**{ field_name: "Test", }) handler = FieldHandler() with patch.dict( field_type_registry.registry, { "lowercase_text": AlwaysLowercaseTextField(), "long_text": ReversesWhenConvertsAwayTextField(), }, ): handler.update_field( user=user, field=existing_field_with_old_value_prep, new_type_name="lowercase_text", ) row.refresh_from_db() assert getattr(row, field_name) == "pre_fix_tset_post_fix" assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 0 assert LongTextField.objects.all().count() == 1
def test_long_text_field_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field = data_fixture.create_text_field(table=table, order=1, name='name') handler = FieldHandler() handler.create_field(user=user, table=table, type_name='long_text', name='description') field = handler.update_field(user=user, field=field, new_type_name='long_text') assert len(LongTextField.objects.all()) == 2 fake = Faker() text = fake.text() model = table.get_model(attribute_names=True) row = model.objects.create(description=text, name='Test') assert row.description == text assert row.name == 'Test' handler.delete_field(user=user, field=field) assert len(LongTextField.objects.all()) == 1
def test_field_which_changes_its_underlying_type_will_have_alter_sql_run( data_fixture): class ReversingTextFieldUsingBothVarCharAndTextSqlTypes(TextFieldType): def get_alter_column_prepare_new_value(self, connection, from_field, to_field): return """p_in = (reverse(p_in));""" def get_model_field(self, instance, **kwargs): kwargs["null"] = True kwargs["blank"] = True if instance.text_default == "use_other_sql_type": return models.TextField(**kwargs) else: return models.CharField(**kwargs) user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) existing_text_field = data_fixture.create_text_field(table=table, order=1) model = table.get_model() field_name = f"field_{existing_text_field.id}" row = model.objects.create(**{ field_name: "Test", }) handler = FieldHandler() with patch.dict( field_type_registry.registry, {"text": ReversingTextFieldUsingBothVarCharAndTextSqlTypes()}, ): # Update to the same baserow type, but due to this fields implementation of # get_model_field this will alter the underlying database column from type # of varchar to text, which should make our reversing alter sql run. handler.update_field( user=user, field=existing_text_field, new_type_name="text", text_default="use_other_sql_type", ) row.refresh_from_db() assert getattr(row, field_name) == "tseT" assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 1
def test_update_field_when_underlying_sql_type_doesnt_change_old_prep( data_fixture): class ReversesWhenConvertsAwayTextField(LongTextFieldType): type = 'reserves_text' model_class = LongTextField def get_alter_column_prepare_old_value(self, connection, from_field, to_field): return '''p_in = (reverse(p_in));''' class AlwaysLowercaseTextField(TextFieldType): type = 'lowercase_text' model_class = LongTextField def get_alter_column_prepare_new_value(self, connection, from_field, to_field): return '''p_in = (lower(p_in));''' user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) existing_field_with_old_value_prep = data_fixture.create_long_text_field( table=table) model = table.get_model() field_name = f'field_{existing_field_with_old_value_prep.id}' row = model.objects.create(**{ field_name: 'Test', }) handler = FieldHandler() with patch.dict( field_type_registry.registry, { 'lowercase_text': AlwaysLowercaseTextField(), 'long_text': ReversesWhenConvertsAwayTextField() }): handler.update_field(user=user, field=existing_field_with_old_value_prep, new_type_name='lowercase_text') row.refresh_from_db() assert getattr(row, field_name) == 'tset' assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 0 assert LongTextField.objects.all().count() == 1
def test_update_field_failing(data_fixture): # This failing field type triggers the CannotChangeFieldType error if a field is # changed into this type. class FailingFieldType(TextFieldType): def get_alter_column_prepare_new_value(self, connection, from_field, to_field): return 'p_in::NOT_VALID_SQL_SO_IT_WILL_FAIL(' user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field = data_fixture.create_number_field(table=table, order=1) handler = FieldHandler() with patch.dict(field_type_registry.registry, {'text': FailingFieldType()}): with pytest.raises(CannotChangeFieldType): handler.update_field(user=user, field=field, new_type_name='text') handler.update_field(user, field=field, new_type_name='text') assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 1
def test_alter_boolean_field_column_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field = data_fixture.create_text_field(table=table, order=1) handler = FieldHandler() field = handler.update_field(user=user, field=field, name='Text field') model = table.get_model() mapping = { '1': True, 't': True, 'y': True, 'yes': True, 'on': True, 'YES': True, '': False, 'f': False, 'n': False, 'false': False, 'off': False, 'Random text': False, } for value in mapping.keys(): model.objects.create(**{f'field_{field.id}': value}) # Change the field type to a number and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name='boolean') model = table.get_model() rows = model.objects.all() for index, value in enumerate(mapping.values()): assert getattr(rows[index], f'field_{field.id}') == value
def test_alter_boolean_field_column_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field = data_fixture.create_text_field(table=table, order=1) handler = FieldHandler() field = handler.update_field(user=user, field=field, name="Text field") model = table.get_model() mapping = { "1": True, "t": True, "y": True, "yes": True, "on": True, "YES": True, "": False, "f": False, "n": False, "false": False, "off": False, "Random text": False, } for value in mapping.keys(): model.objects.create(**{f"field_{field.id}": value}) # Change the field type to a number and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name="boolean") model = table.get_model() rows = model.objects.all() for index, value in enumerate(mapping.values()): assert getattr(rows[index], f"field_{field.id}") == value
def test_field_type_changed(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table(user=user, database=table.database) text_field = data_fixture.create_text_field(table=table) grid_view = data_fixture.create_grid_view(table=table) data_fixture.create_view_filter(view=grid_view, field=text_field, type="contains", value="test") data_fixture.create_view_sort(view=grid_view, field=text_field, order="ASC") field_handler = FieldHandler() long_text_field = field_handler.update_field(user=user, field=text_field, new_type_name="long_text") assert ViewFilter.objects.all().count() == 1 assert ViewSort.objects.all().count() == 1 field_handler.update_field(user=user, field=long_text_field, new_type_name="boolean") assert ViewFilter.objects.all().count() == 0 assert ViewSort.objects.all().count() == 1 field_handler.update_field( user=user, field=long_text_field, new_type_name="link_row", link_row_table=table_2, ) assert ViewFilter.objects.all().count() == 0 assert ViewSort.objects.all().count() == 0
def test_single_select_field_type(data_fixture): user = data_fixture.create_user() database = data_fixture.create_database_application(user=user, name='Placeholder') table = data_fixture.create_database_table(name='Example', database=database) field_handler = FieldHandler() field = field_handler.create_field( user=user, table=table, type_name='single_select', name='Single select', select_options=[{'value': 'Option 1', 'color': 'blue'}] ) assert SingleSelectField.objects.all().first().id == field.id assert SelectOption.objects.all().count() == 1 select_options = field.select_options.all() assert len(select_options) == 1 assert select_options[0].order == 0 assert select_options[0].field_id == field.id assert select_options[0].value == 'Option 1' assert select_options[0].color == 'blue' field = field_handler.update_field( user=user, table=table, field=field, select_options=[ {'value': 'Option 2 B', 'color': 'red 2'}, {'id': select_options[0].id, 'value': 'Option 1 B', 'color': 'blue 2'}, ] ) assert SelectOption.objects.all().count() == 2 select_options_2 = field.select_options.all() assert len(select_options_2) == 2 assert select_options_2[0].order == 0 assert select_options_2[0].field_id == field.id assert select_options_2[0].value == 'Option 2 B' assert select_options_2[0].color == 'red 2' assert select_options_2[1].id == select_options[0].id assert select_options_2[1].order == 1 assert select_options_2[1].field_id == field.id assert select_options_2[1].value == 'Option 1 B' assert select_options_2[1].color == 'blue 2' field_handler.delete_field(user=user, field=field) assert SelectOption.objects.all().count() == 0
def test_link_row_field_type(data_fixture): user = data_fixture.create_user() database = data_fixture.create_database_application(user=user, name='Placeholder') table = data_fixture.create_database_table(name='Example', database=database) customers_table = data_fixture.create_database_table(name='Customers', database=database) cars_table = data_fixture.create_database_table(name='Cars', database=database) unrelated_table_1 = data_fixture.create_database_table(name='Unrelated') field_handler = FieldHandler() row_handler = RowHandler() # Create a primary field and some example data for the customers table. customers_primary_field = field_handler.create_field(user=user, table=customers_table, type_name='text', name='Name', primary=True) customers_row_1 = row_handler.create_row( user=user, table=customers_table, values={f'field_{customers_primary_field.id}': 'John'}) customers_row_2 = row_handler.create_row( user=user, table=customers_table, values={f'field_{customers_primary_field.id}': 'Jane'}) # Create a primary field and some example data for the cars table. cars_primary_field = field_handler.create_field(user=user, table=cars_table, type_name='text', name='Name', primary=True) cars_row_1 = row_handler.create_row( user=user, table=cars_table, values={f'field_{cars_primary_field.id}': 'BMW'}) cars_row_2 = row_handler.create_row( user=user, table=cars_table, values={f'field_{cars_primary_field.id}': 'Audi'}) with pytest.raises(LinkRowTableNotProvided): field_handler.create_field(user=user, table=table, type_name='link_row', name='Without table') with pytest.raises(LinkRowTableNotInSameDatabase): field_handler.create_field(user=user, table=table, type_name='link_row', name='Unrelated', link_row_table=unrelated_table_1) link_field_1 = field_handler.create_field(user=user, table=table, type_name='link_row', name='Customer', link_row_table=customers_table) link_field_2 = field_handler.create_field(user=user, table=table, type_name='link_row', name='Customer', link_row_table=customers_table) assert link_field_1.link_row_related_field.name == 'Example' assert link_field_2.link_row_related_field.name == 'Example' connection = connections['default'] tables = connection.introspection.table_names() assert (link_field_1.through_table_name == link_field_1.link_row_related_field.through_table_name) assert (link_field_2.through_table_name == link_field_2.link_row_related_field.through_table_name) assert link_field_1.through_table_name in tables assert link_field_2.through_table_name in tables model = table.get_model() table_row = model.objects.create() getattr(table_row, f'field_{link_field_1.id}').add(customers_row_1.id) results = getattr(table_row, f'field_{link_field_1.id}').all() assert len(results) == 1 assert getattr(results[0], f'field_{customers_primary_field.id}') == 'John' getattr(table_row, f'field_{link_field_2.id}').add(customers_row_1.id, customers_row_2.id) results = getattr(table_row, f'field_{link_field_2.id}').all() assert len(results) == 2 assert getattr(results[0], f'field_{customers_primary_field.id}') == 'John' assert getattr(results[1], f'field_{customers_primary_field.id}') == 'Jane' table_row_2 = model.objects.create() getattr(table_row_2, f'field_{link_field_1.id}').add(customers_row_2.id) results = getattr(table_row_2, f'field_{link_field_1.id}').all() assert len(results) == 1 assert getattr(results[0], f'field_{customers_primary_field.id}') == 'Jane' # Going to change only the name of the field. This should not result in any errors # of schema changes. link_field_1 = field_handler.update_field(user, link_field_1, name='Customer 2') with pytest.raises(LinkRowTableNotInSameDatabase): field_handler.update_field(user, link_field_1, link_row_table=unrelated_table_1) model = table.get_model() assert model.objects.all().count() == 2 # Change the table, this should destroy all relations. old_link_field_1_relation_id = link_field_1.link_row_relation_id link_field_1 = field_handler.update_field(user, link_field_1, link_row_table=cars_table) model = table.get_model() table_rows = model.objects.all() table_row = table_rows[0] table_row_2 = table_rows[1] assert link_field_1.link_row_table.id == cars_table.id assert link_field_1.link_row_relation_id == old_link_field_1_relation_id assert getattr(table_row, f'field_{link_field_1.id}').all().count() == 0 assert getattr(table_row, f'field_{link_field_2.id}').all().count() == 2 assert getattr(table_row_2, f'field_{link_field_1.id}').all().count() == 0 assert getattr(table_row_2, f'field_{link_field_2.id}').all().count() == 0 link_field_2 = field_handler.update_field(user, link_field_2, new_type_name='text') model = table.get_model() table_row = model.objects.all().first() assert getattr(table_row, f'field_{link_field_2.id}') is None assert LinkRowField.objects.all().count() == 2 setattr(table_row, f'field_{link_field_2.id}', 'Text value') table_row.save() assert getattr(table_row, f'field_{link_field_2.id}') == 'Text value' # Delete the existing field. Alter that the related field should be deleted and # no table named _relation_ should exist. field_handler.delete_field(user, link_field_1) assert LinkRowField.objects.all().count() == 0 for t in connection.introspection.table_names(): if '_relation_' in t: assert False # Change a the text field back into a link row field. link_field_2 = field_handler.update_field(user, link_field_2, new_type_name='link_row', link_row_table=customers_table) assert link_field_2.link_row_related_field.name == 'Example' assert (link_field_2.through_table_name == link_field_2.link_row_related_field.through_table_name) assert link_field_2.through_table_name in connection.introspection.table_names( ) assert LinkRowField.objects.all().count() == 2 model = table.get_model() table_row = model.objects.all().first() getattr(table_row, f'field_{link_field_2.id}').add(customers_row_1.id, customers_row_2.id) results = getattr(table_row, f'field_{link_field_2.id}').all() assert len(results) == 2 assert getattr(results[0], f'field_{customers_primary_field.id}') == 'John' assert getattr(results[1], f'field_{customers_primary_field.id}') == 'Jane'
def test_link_row_field_type_rows(data_fixture): user = data_fixture.create_user() database = data_fixture.create_database_application(user=user, name='Placeholder') example_table = data_fixture.create_database_table(name='Example', database=database) customers_table = data_fixture.create_database_table(name='Customers', database=database) users_table = data_fixture.create_database_table(name='Users', database=database) field_handler = FieldHandler() row_handler = RowHandler() link_row_field = field_handler.create_field(user=user, table=example_table, type_name='link_row', link_row_table=customers_table) customers_row_1 = row_handler.create_row(user=user, table=customers_table) customers_row_2 = row_handler.create_row(user=user, table=customers_table) customers_row_3 = row_handler.create_row(user=user, table=customers_table) row = row_handler.create_row(user=user, table=example_table, values={ f'field_{link_row_field.id}': [customers_row_1.id, customers_row_2.id], }) row_2 = row_handler.create_row(user=user, table=example_table, values={ f'field_{link_row_field.id}': [customers_row_1.id], }) example_table.name = 'Example2' example_table.save() customers_table.name = 'Customers2' customers_table.save() row_1_all = getattr(row, f'field_{link_row_field.id}').all() row_2_all = getattr(row_2, f'field_{link_row_field.id}').all() row_1_ids = [i.id for i in row_1_all] row_2_ids = [i.id for i in row_2_all] assert row_1_all.count() == 2 assert row_2_all.count() == 1 assert customers_row_1.id in row_1_ids assert customers_row_2.id in row_1_ids assert customers_row_1.id in row_2_ids row = row_handler.update_row( user=user, table=example_table, row_id=row.id, values={f'field_{link_row_field.id}': [customers_row_3.id]}) row_2 = row_handler.update_row( user=user, table=example_table, row_id=row_2.id, values={ f'field_{link_row_field.id}': [customers_row_2.id, customers_row_1.id] }) row_1_all = getattr(row, f'field_{link_row_field.id}').all() row_2_all = getattr(row_2, f'field_{link_row_field.id}').all() row_1_ids = [i.id for i in row_1_all] row_2_ids = [i.id for i in row_2_all] assert row_1_all.count() == 1 assert row_2_all.count() == 2 assert customers_row_3.id in row_1_ids assert customers_row_1.id in row_2_ids assert customers_row_2.id in row_2_ids # Check if the relations are there via the customers table. customers_table.refresh_from_db() customers_model = customers_table.get_model() related_field = link_row_field.link_row_related_field customer_rows = customers_model.objects.all() assert customer_rows.count() == 3 customers_row_1 = customer_rows[0] customers_row_2 = customer_rows[1] customers_row_3 = customer_rows[2] customer_row_1_all = getattr(customers_row_1, f'field_{related_field.id}').all() customer_row_2_all = getattr(customers_row_2, f'field_{related_field.id}').all() customer_row_3_all = getattr(customers_row_3, f'field_{related_field.id}').all() assert customer_row_1_all.count() == 1 assert customer_row_2_all.count() == 1 assert customer_row_3_all.count() == 1 customers_row_1_ids = [i.id for i in customer_row_1_all] customers_row_2_ids = [i.id for i in customer_row_2_all] customers_row_3_ids = [i.id for i in customer_row_3_all] assert row_2.id in customers_row_1_ids assert row_2.id in customers_row_2_ids assert row.id in customers_row_3_ids # When changing the link row table table all the existing relations should be # deleted. link_row_field = field_handler.update_field(user=user, field=link_row_field, type_name='link_row', link_row_table=users_table) example_table.refresh_from_db() model = example_table.get_model() rows = model.objects.all() row = rows[0] row_2 = rows[1] assert getattr(row, f'field_{link_row_field.id}').all().count() == 0 assert getattr(row_2, f'field_{link_row_field.id}').all().count() == 0 # Just check if the field can be deleted can be deleted. field_handler.delete_field(user=user, field=link_row_field) assert Field.objects.all().count() == 0
def test_update_field(send_mock, data_fixture): user = data_fixture.create_user() user_2 = data_fixture.create_user() table = data_fixture.create_database_table(user=user) data_fixture.create_text_field(table=table, order=0) field = data_fixture.create_text_field(table=table, order=1) handler = FieldHandler() with pytest.raises(UserNotInGroupError): handler.update_field(user=user_2, field=field) with pytest.raises(ValueError): handler.update_field(user=user, field=object()) with pytest.raises(FieldTypeDoesNotExist): handler.update_field(user=user, field=field, new_type_name='NOT_EXISTING') # The link row field is not compatible with a primary field so an exception # is expected. field.primary = True field.save() with pytest.raises(IncompatiblePrimaryFieldTypeError): handler.update_field(user=user, field=field, new_type_name='link_row') field.primary = False field.save() # Change some values of the text field and test if they have been changed. field = handler.update_field(user=user, field=field, name='Text field', text_default='Default value') assert field.name == 'Text field' assert field.text_default == 'Default value' assert isinstance(field, TextField) send_mock.assert_called_once() assert send_mock.call_args[1]['field'].id == field.id assert send_mock.call_args[1]['user'].id == user.id # Insert some rows to the table which should be converted later. model = table.get_model() model.objects.create(**{f'field_{field.id}': 'Text value'}) model.objects.create(**{f'field_{field.id}': '100.22'}) model.objects.create(**{f'field_{field.id}': '10'}) # Change the field type to a number and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name='number', name='Number field', number_type='INTEGER', number_negative=False) assert field.name == 'Number field' assert field.number_type == 'INTEGER' assert field.number_negative is False assert not hasattr(field, 'text_default') model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{field.id}') is None assert getattr(rows[1], f'field_{field.id}') == 100 assert getattr(rows[2], f'field_{field.id}') == 10 # Change the field type to a decimal and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name='number', name='Price field', number_type='DECIMAL', number_decimal_places=2, number_negative=True) assert field.name == 'Price field' assert field.number_type == 'DECIMAL' assert field.number_decimal_places == 2 assert field.number_negative is True model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{field.id}') is None assert getattr(rows[1], f'field_{field.id}') == Decimal('100.00') assert getattr(rows[2], f'field_{field.id}') == Decimal('10.00') # Change the field type to a boolean and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name='boolean', name='Active') field.refresh_from_db() assert field.name == 'Active' assert not hasattr(field, 'number_type') assert not hasattr(field, 'number_decimal_places') assert not hasattr(field, 'number_negative') model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{field.id}') is False assert getattr(rows[1], f'field_{field.id}') is False assert getattr(rows[2], f'field_{field.id}') is False
def test_converting_date_field_value(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field_handler = FieldHandler() row_handler = RowHandler() utc = timezone('utc') date_field_eu = data_fixture.create_text_field(table=table) date_field_us = data_fixture.create_text_field(table=table) date_field_iso = data_fixture.create_text_field(table=table) date_field_eu_12 = data_fixture.create_text_field(table=table) date_field_us_12 = data_fixture.create_text_field(table=table) date_field_iso_12 = data_fixture.create_text_field(table=table) date_field_eu_24 = data_fixture.create_text_field(table=table) date_field_us_24 = data_fixture.create_text_field(table=table) date_field_iso_24 = data_fixture.create_text_field(table=table) model = table.get_model() row_handler.create_row( user=user, table=table, model=model, values={ f'field_{date_field_eu.id}': '22/07/2021', f'field_{date_field_us.id}': '07/22/2021', f'field_{date_field_iso.id}': '2021-07-22', f'field_{date_field_eu_12.id}': '22/07/2021 12:45 PM', f'field_{date_field_us_12.id}': '07/22/2021 12:45 PM', f'field_{date_field_iso_12.id}': '2021-07-22 12:45 PM', f'field_{date_field_eu_24.id}': '22/07/2021 12:45', f'field_{date_field_us_24.id}': '07/22/2021 12:45', f'field_{date_field_iso_24.id}': '2021-07-22 12:45', }) row_handler.create_row( user=user, table=table, model=model, values={ f'field_{date_field_eu.id}': '22-7-2021', f'field_{date_field_us.id}': '7-22-2021', f'field_{date_field_iso.id}': '2021/7/22', f'field_{date_field_eu_12.id}': '22-7-2021 12:45am', f'field_{date_field_us_12.id}': '7-22-2021 12:45am', f'field_{date_field_iso_12.id}': '2021/7/22 12:45am', f'field_{date_field_eu_24.id}': '22-7-2021 7:45', f'field_{date_field_us_24.id}': '7-22-2021 7:45', f'field_{date_field_iso_24.id}': '2021/7/22 7:45', }) row_handler.create_row( user=user, table=table, model=model, values={ f'field_{date_field_eu.id}': '22/07/2021 12:00', f'field_{date_field_us.id}': '07/22/2021 12:00am', f'field_{date_field_iso.id}': '2021-07-22 12:00 PM', f'field_{date_field_eu_12.id}': 'INVALID', f'field_{date_field_us_12.id}': '2222-2222-2222', f'field_{date_field_iso_12.id}': 'x-7--1', f'field_{date_field_eu_24.id}': '22-7-2021 7:45:12', f'field_{date_field_us_24.id}': '7-22-2021 7:45:23', f'field_{date_field_iso_24.id}': '2021/7/22 7:45:70' }) row_handler.create_row( user=user, table=table, model=model, values={ f'field_{date_field_eu.id}': '2018-08-20T13:20:10', f'field_{date_field_us.id}': '2017 Mar 03 05:12:41.211', f'field_{date_field_iso.id}': '19/Apr/2017:06:36:15', f'field_{date_field_eu_12.id}': 'Dec 2, 2017 2:39:58 AM', f'field_{date_field_us_12.id}': 'Jun 09 2018 15:28:14', f'field_{date_field_iso_12.id}': 'Apr 20 00:00:35 2010', f'field_{date_field_eu_24.id}': 'Apr 20 00:00:35 2010', f'field_{date_field_us_24.id}': '2018-02-27 15:35:20.311', f'field_{date_field_iso_24.id}': '10-04-19 12:00:17' }) date_field_eu = field_handler.update_field(user=user, field=date_field_eu, new_type_name='date', date_format='EU') date_field_us = field_handler.update_field(user=user, field=date_field_us, new_type_name='date', date_format='US') date_field_iso = field_handler.update_field(user=user, field=date_field_iso, new_type_name='date', date_format='ISO') date_field_eu_12 = field_handler.update_field(user=user, field=date_field_eu_12, new_type_name='date', date_format='EU', date_include_time=True, date_time_format='12') date_field_us_12 = field_handler.update_field(user=user, field=date_field_us_12, new_type_name='date', date_format='US', date_include_time=True, date_time_format='12') date_field_iso_12 = field_handler.update_field(user=user, field=date_field_iso_12, new_type_name='date', date_format='ISO', date_include_time=True, date_time_format='12') date_field_eu_24 = field_handler.update_field(user=user, field=date_field_eu_24, new_type_name='date', date_format='EU', date_include_time=True, date_time_format='24') date_field_us_24 = field_handler.update_field(user=user, field=date_field_us_24, new_type_name='date', date_format='US', date_include_time=True, date_time_format='24') date_field_iso_24 = field_handler.update_field(user=user, field=date_field_iso_24, new_type_name='date', date_format='ISO', date_include_time=True, date_time_format='24') model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{date_field_eu.id}') == date(2021, 7, 22) assert getattr(rows[0], f'field_{date_field_us.id}') == date(2021, 7, 22) assert getattr(rows[0], f'field_{date_field_iso.id}') == date(2021, 7, 22) assert getattr(rows[0], f'field_{date_field_eu_12.id}') == (datetime(2021, 7, 22, 12, 45, 0, tzinfo=utc)) assert getattr(rows[0], f'field_{date_field_us_12.id}') == (datetime(2021, 7, 22, 12, 45, 0, tzinfo=utc)) assert getattr(rows[0], f'field_{date_field_iso_12.id}') == (datetime(2021, 7, 22, 12, 45, 0, tzinfo=utc)) assert getattr(rows[0], f'field_{date_field_eu_24.id}') == (datetime(2021, 7, 22, 12, 45, 0, tzinfo=utc)) assert getattr(rows[0], f'field_{date_field_us_24.id}') == (datetime(2021, 7, 22, 12, 45, 0, tzinfo=utc)) assert getattr(rows[0], f'field_{date_field_iso_24.id}') == (datetime(2021, 7, 22, 12, 45, 0, tzinfo=utc)) assert getattr(rows[1], f'field_{date_field_eu.id}') == date(2021, 7, 22) assert getattr(rows[1], f'field_{date_field_us.id}') == date(2021, 7, 22) assert getattr(rows[1], f'field_{date_field_iso.id}') == date(2021, 7, 22) assert getattr(rows[1], f'field_{date_field_eu_12.id}') == (datetime(2021, 7, 22, 0, 45, 0, tzinfo=utc)) assert getattr(rows[1], f'field_{date_field_us_12.id}') == (datetime(2021, 7, 22, 0, 45, 0, tzinfo=utc)) assert getattr(rows[1], f'field_{date_field_iso_12.id}') == (datetime(2021, 7, 22, 0, 45, 0, tzinfo=utc)) assert getattr(rows[1], f'field_{date_field_eu_24.id}') == (datetime(2021, 7, 22, 7, 45, 0, tzinfo=utc)) assert getattr(rows[1], f'field_{date_field_us_24.id}') == (datetime(2021, 7, 22, 7, 45, 0, tzinfo=utc)) assert getattr(rows[1], f'field_{date_field_iso_24.id}') == (datetime(2021, 7, 22, 7, 45, 0, tzinfo=utc)) assert getattr(rows[2], f'field_{date_field_eu.id}') == date(2021, 7, 22) assert getattr(rows[2], f'field_{date_field_us.id}') == date(2021, 7, 22) assert getattr(rows[2], f'field_{date_field_iso.id}') == date(2021, 7, 22) assert getattr(rows[2], f'field_{date_field_eu_12.id}') is None assert getattr(rows[2], f'field_{date_field_us_12.id}') is None assert getattr(rows[2], f'field_{date_field_iso_12.id}') is None assert getattr(rows[2], f'field_{date_field_eu_24.id}') == (datetime(2021, 7, 22, 7, 45, 0, tzinfo=utc)) assert getattr(rows[2], f'field_{date_field_us_24.id}') == (datetime(2021, 7, 22, 7, 45, 0, tzinfo=utc)) assert getattr(rows[2], f'field_{date_field_iso_24.id}') == (datetime(2021, 7, 22, 7, 45, 0, tzinfo=utc)) """ f'field_{date_field_eu.id}': '2018-08-20T13:20:10', f'field_{date_field_us.id}': '2017 Mar 03 05:12:41.211', f'field_{date_field_iso.id}': '19/Apr/2017:06:36:15', f'field_{date_field_eu_12.id}': 'Dec 2, 2017 2:39:58 AM', f'field_{date_field_us_12.id}': 'Jun 09 2018 15:28:14', f'field_{date_field_iso_12.id}': 'Apr 20 00:00:35 2010', f'field_{date_field_eu_24.id}': 'Apr 20 00:00:35 2010', f'field_{date_field_us_24.id}': '2018-02-27 15:35:20.311', f'field_{date_field_iso_24.id}': '10-04-19 12:00:17' """ assert getattr(rows[3], f'field_{date_field_eu.id}') == date(2018, 8, 20) assert getattr(rows[3], f'field_{date_field_us.id}') == date(2017, 3, 3) assert getattr(rows[3], f'field_{date_field_iso.id}') == date(2017, 4, 19) assert getattr(rows[3], f'field_{date_field_eu_12.id}') == (datetime(2017, 12, 2, 2, 39, 58, tzinfo=utc)) assert getattr(rows[3], f'field_{date_field_us_12.id}') == (datetime(2018, 6, 9, 15, 28, 14, tzinfo=utc)) assert getattr(rows[3], f'field_{date_field_iso_12.id}') == (datetime(2010, 4, 20, 0, 0, 35, tzinfo=utc)) assert getattr(rows[3], f'field_{date_field_eu_24.id}') == (datetime(2010, 4, 20, 0, 0, 35, tzinfo=utc)) assert getattr(rows[3], f'field_{date_field_us_24.id}') == (datetime(2018, 2, 27, 15, 35, 20, 311000, tzinfo=utc)) assert getattr(rows[3], f'field_{date_field_iso_24.id}') == (datetime(10, 4, 19, 12, 0, tzinfo=utc)) date_field_eu = field_handler.update_field(user=user, field=date_field_eu, new_type_name='text') date_field_us = field_handler.update_field(user=user, field=date_field_us, new_type_name='text') date_field_iso = field_handler.update_field(user=user, field=date_field_iso, new_type_name='text') date_field_eu_12 = field_handler.update_field(user=user, field=date_field_eu_12, new_type_name='text') date_field_us_12 = field_handler.update_field(user=user, field=date_field_us_12, new_type_name='text') date_field_iso_12 = field_handler.update_field(user=user, field=date_field_iso_12, new_type_name='text') date_field_eu_24 = field_handler.update_field(user=user, field=date_field_eu_24, new_type_name='text') date_field_us_24 = field_handler.update_field(user=user, field=date_field_us_24, new_type_name='text') date_field_iso_24 = field_handler.update_field(user=user, field=date_field_iso_24, new_type_name='text') model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{date_field_eu.id}') == '22/07/2021' assert getattr(rows[0], f'field_{date_field_us.id}') == '07/22/2021' assert getattr(rows[0], f'field_{date_field_iso.id}') == '2021-07-22' assert getattr(rows[0], f'field_{date_field_eu_12.id}') == '22/07/2021 12:45PM' assert getattr(rows[0], f'field_{date_field_us_12.id}') == '07/22/2021 12:45PM' assert getattr(rows[0], f'field_{date_field_iso_12.id}') == '2021-07-22 12:45PM' assert getattr(rows[0], f'field_{date_field_eu_24.id}') == '22/07/2021 12:45' assert getattr(rows[0], f'field_{date_field_us_24.id}') == '07/22/2021 12:45' assert getattr(rows[0], f'field_{date_field_iso_24.id}') == '2021-07-22 12:45' assert getattr(rows[2], f'field_{date_field_eu_12.id}') is None
def test_date_field_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field_handler = FieldHandler() row_handler = RowHandler() amsterdam = timezone('Europe/Amsterdam') utc = timezone('utc') date_field_1 = field_handler.create_field(user=user, table=table, type_name='date', name='Date') date_field_2 = field_handler.create_field(user=user, table=table, type_name='date', name='Datetime', date_include_time=True) assert date_field_1.date_include_time == False assert date_field_2.date_include_time == True assert len(DateField.objects.all()) == 2 model = table.get_model(attribute_names=True) row = row_handler.create_row(user=user, table=table, values={}, model=model) assert row.date == None assert row.datetime == None row = row_handler.create_row(user=user, table=table, values={ 'date': '2020-4-1', 'datetime': '2020-4-1 12:30:30' }, model=model) row.refresh_from_db() assert row.date == date(2020, 4, 1) assert row.datetime == datetime(2020, 4, 1, 12, 30, 30, tzinfo=utc) row = row_handler.create_row(user=user, table=table, values={ 'datetime': make_aware( datetime(2020, 4, 1, 12, 30, 30), amsterdam) }, model=model) row.refresh_from_db() assert row.date == None assert row.datetime == datetime(2020, 4, 1, 10, 30, 30, tzinfo=timezone('UTC')) date_field_1 = field_handler.update_field(user=user, field=date_field_1, date_include_time=True) date_field_2 = field_handler.update_field(user=user, field=date_field_2, date_include_time=False) assert date_field_1.date_include_time == True assert date_field_2.date_include_time == False model = table.get_model(attribute_names=True) rows = model.objects.all() assert rows[0].date == None assert rows[0].datetime == None assert rows[1].date == datetime(2020, 4, 1, tzinfo=timezone('UTC')) assert rows[1].datetime == date(2020, 4, 1) assert rows[2].date == None assert rows[2].datetime == date(2020, 4, 1) field_handler.delete_field(user=user, field=date_field_1) field_handler.delete_field(user=user, field=date_field_2) assert len(DateField.objects.all()) == 0
def test_email_field_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table(user=user, database=table.database) field = data_fixture.create_text_field(table=table, order=1, name='name') field_handler = FieldHandler() row_handler = RowHandler() field_2 = field_handler.create_field(user=user, table=table, type_name='email', name='email') number = field_handler.create_field(user=user, table=table, type_name='number', name='number') assert len(EmailField.objects.all()) == 1 model = table.get_model(attribute_names=True) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={'email': 'invalid_email'}, model=model) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={'email': 'invalid@email'}, model=model) row_0 = row_handler.create_row(user=user, table=table, values={ 'name': '*****@*****.**', 'email': '*****@*****.**', 'number': 5 }, model=model) row_1 = row_handler.create_row(user=user, table=table, values={ 'name': 'someuser', 'email': '*****@*****.**', 'number': 10 }, model=model) row_2 = row_handler.create_row(user=user, table=table, values={ 'name': 'http://www.baserow.io', 'email': '*****@*****.**' }, model=model) row_3 = row_handler.create_row(user=user, table=table, values={ 'name': 'NOT AN EMAIL', 'email': '*****@*****.**' }, model=model) row_4 = row_handler.create_row(user=user, table=table, values={ 'name': '*****@*****.**', 'email': '' }, model=model) row_5 = row_handler.create_row(user=user, table=table, values={ 'email': None, }, model=model) row_6 = row_handler.create_row(user=user, table=table, values={}, model=model) # Convert the text field to a url field so we can check how the conversion of # values went. field_handler.update_field(user=user, field=field, new_type_name='email') field_handler.update_field(user=user, field=number, new_type_name='email') model = table.get_model(attribute_names=True) rows = model.objects.all() assert rows[0].name == '*****@*****.**' assert rows[0].email == '*****@*****.**' assert rows[0].number == '' assert rows[1].name == '' assert rows[1].email == '*****@*****.**' assert rows[1].number == '' assert rows[2].name == '' assert rows[2].email == '*****@*****.**' assert rows[2].number == '' assert rows[3].name == '' assert rows[3].email == '*****@*****.**' assert rows[3].number == '' assert rows[4].name == '*****@*****.**' assert rows[4].email == '' assert rows[4].number == '' assert rows[5].name == '' assert rows[5].email == '' assert rows[5].number == '' assert rows[6].name == '' assert rows[6].email == '' assert rows[6].number == '' field_handler.delete_field(user=user, field=field_2) assert len(EmailField.objects.all()) == 2
def test_url_field_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table(user=user, database=table.database) field = data_fixture.create_text_field(table=table, order=1, name='name') field_handler = FieldHandler() row_handler = RowHandler() field_2 = field_handler.create_field(user=user, table=table, type_name='url', name='url') number = field_handler.create_field(user=user, table=table, type_name='number', name='number') assert len(URLField.objects.all()) == 1 model = table.get_model(attribute_names=True) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={'url': 'invalid_url'}, model=model) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={'url': 'httpss'}, model=model) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={'url': 'httpss'}, model=model) row_0 = row_handler.create_row(user=user, table=table, values={ 'name': 'http://test.nl', 'url': 'https://baserow.io', 'number': 5 }, model=model) row_1 = row_handler.create_row(user=user, table=table, values={ 'name': 'http;//', 'url': 'http://localhost', 'number': 10 }, model=model) row_2 = row_handler.create_row(user=user, table=table, values={ 'name': '*****@*****.**', 'url': 'http://www.baserow.io' }, model=model) row_3 = row_handler.create_row( user=user, table=table, values={ 'name': 'NOT A URL', 'url': 'http://www.baserow.io/blog/building-a-database' }, model=model) row_4 = row_handler.create_row( user=user, table=table, values={ 'name': 'ftps://www.complex.website.com?querystring=test&something=else', 'url': '' }, model=model) row_5 = row_handler.create_row(user=user, table=table, values={ 'url': None, }, model=model) row_6 = row_handler.create_row(user=user, table=table, values={}, model=model) # Convert to text field to a url field so we can check how the conversion of values # went. field_handler.update_field(user=user, field=field, new_type_name='url') field_handler.update_field(user=user, field=number, new_type_name='url') model = table.get_model(attribute_names=True) rows = model.objects.all() assert rows[0].name == 'http://test.nl' assert rows[0].url == 'https://baserow.io' assert rows[0].number == '' assert rows[1].name == '' assert rows[1].url == 'http://localhost' assert rows[1].number == '' assert rows[2].name == '' assert rows[2].url == 'http://www.baserow.io' assert rows[2].number == '' assert rows[3].name == '' assert rows[3].url == 'http://www.baserow.io/blog/building-a-database' assert rows[3].number == '' assert (rows[4].name == 'ftps://www.complex.website.com?querystring=test&something=else') assert rows[4].url == '' assert rows[4].number == '' assert rows[5].name == '' assert rows[5].url == '' assert rows[5].number == '' assert rows[6].name == '' assert rows[6].url == '' assert rows[6].number == '' field_handler.delete_field(user=user, field=field_2) assert len(URLField.objects.all()) == 2
def test_update_field(data_fixture): """ @TODO somehow trigger the CannotChangeFieldType and test if it is raised. """ user = data_fixture.create_user() user_2 = data_fixture.create_user() table = data_fixture.create_database_table(user=user) data_fixture.create_text_field(table=table, order=0) field = data_fixture.create_text_field(table=table, order=1) handler = FieldHandler() with pytest.raises(UserNotInGroupError): handler.update_field(user=user_2, field=field) with pytest.raises(ValueError): handler.update_field(user=user, field=object()) with pytest.raises(FieldTypeDoesNotExist): handler.update_field(user=user, field=field, new_type_name='NOT_EXISTING') # Change some values of the text field and test if they have been changed. field = handler.update_field(user=user, field=field, name='Text field', text_default='Default value') assert field.name == 'Text field' assert field.text_default == 'Default value' assert isinstance(field, TextField) # Insert some rows to the table which should be converted later. model = table.get_model() model.objects.create(**{f'field_{field.id}': 'Text value'}) model.objects.create(**{f'field_{field.id}': '100.22'}) model.objects.create(**{f'field_{field.id}': '10'}) # Change the field type to a number and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name='number', name='Number field', number_type='INTEGER', number_negative=False) assert field.name == 'Number field' assert field.number_type == 'INTEGER' assert field.number_negative == False assert not hasattr(field, 'text_default') model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{field.id}') == None assert getattr(rows[1], f'field_{field.id}') == 100 assert getattr(rows[2], f'field_{field.id}') == 10 # Change the field type to a decimal and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name='number', name='Price field', number_type='DECIMAL', number_decimal_places=2, number_negative=True) assert field.name == 'Price field' assert field.number_type == 'DECIMAL' assert field.number_decimal_places == 2 assert field.number_negative == True model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{field.id}') == None assert getattr(rows[1], f'field_{field.id}') == Decimal('100.00') assert getattr(rows[2], f'field_{field.id}') == Decimal('10.00') # Change the field type to a boolean and test if the values have been changed. field = handler.update_field(user=user, field=field, new_type_name='boolean', name='Active') field.refresh_from_db() assert field.name == 'Active' assert not hasattr(field, 'number_type') assert not hasattr(field, 'number_decimal_places') assert not hasattr(field, 'number_negative') model = table.get_model() rows = model.objects.all() assert getattr(rows[0], f'field_{field.id}') == False assert getattr(rows[1], f'field_{field.id}') == False assert getattr(rows[2], f'field_{field.id}') == False
def test_link_row_field_type_rows(data_fixture): user = data_fixture.create_user() database = data_fixture.create_database_application(user=user, name="Placeholder") example_table = data_fixture.create_database_table( name="Example", database=database ) customers_table = data_fixture.create_database_table( name="Customers", database=database ) data_fixture.create_text_field(name="Name", table=customers_table, primary=True) users_table = data_fixture.create_database_table(name="Users", database=database) field_handler = FieldHandler() row_handler = RowHandler() link_row_field = field_handler.create_field( user=user, table=example_table, type_name="link_row", link_row_table=customers_table, ) customers_row_1 = row_handler.create_row(user=user, table=customers_table) customers_row_2 = row_handler.create_row(user=user, table=customers_table) customers_row_3 = row_handler.create_row(user=user, table=customers_table) row = row_handler.create_row( user=user, table=example_table, values={ f"field_{link_row_field.id}": [customers_row_1.id, customers_row_2.id], }, ) row_2 = row_handler.create_row( user=user, table=example_table, values={ f"field_{link_row_field.id}": [customers_row_1.id], }, ) example_table.name = "Example2" example_table.save() customers_table.name = "Customers2" customers_table.save() row_1_all = getattr(row, f"field_{link_row_field.id}").all() row_2_all = getattr(row_2, f"field_{link_row_field.id}").all() row_1_ids = [i.id for i in row_1_all] row_2_ids = [i.id for i in row_2_all] assert row_1_all.count() == 2 assert row_2_all.count() == 1 assert customers_row_1.id in row_1_ids assert customers_row_2.id in row_1_ids assert customers_row_1.id in row_2_ids row = row_handler.update_row( user=user, table=example_table, row_id=row.id, values={f"field_{link_row_field.id}": [customers_row_3.id]}, ) row_2 = row_handler.update_row( user=user, table=example_table, row_id=row_2.id, values={f"field_{link_row_field.id}": [customers_row_2.id, customers_row_1.id]}, ) row_1_all = getattr(row, f"field_{link_row_field.id}").all() row_2_all = getattr(row_2, f"field_{link_row_field.id}").all() row_1_ids = [i.id for i in row_1_all] row_2_ids = [i.id for i in row_2_all] assert row_1_all.count() == 1 assert row_2_all.count() == 2 assert customers_row_3.id in row_1_ids assert customers_row_1.id in row_2_ids assert customers_row_2.id in row_2_ids # Check if the relations are there via the customers table. customers_table.refresh_from_db() customers_model = customers_table.get_model() related_field = link_row_field.link_row_related_field customer_rows = customers_model.objects.all() assert customer_rows.count() == 3 customers_row_1 = customer_rows[0] customers_row_2 = customer_rows[1] customers_row_3 = customer_rows[2] customer_row_1_all = getattr(customers_row_1, f"field_{related_field.id}").all() customer_row_2_all = getattr(customers_row_2, f"field_{related_field.id}").all() customer_row_3_all = getattr(customers_row_3, f"field_{related_field.id}").all() assert customer_row_1_all.count() == 1 assert customer_row_2_all.count() == 1 assert customer_row_3_all.count() == 1 customers_row_1_ids = [i.id for i in customer_row_1_all] customers_row_2_ids = [i.id for i in customer_row_2_all] customers_row_3_ids = [i.id for i in customer_row_3_all] assert row_2.id in customers_row_1_ids assert row_2.id in customers_row_2_ids assert row.id in customers_row_3_ids # When changing the link row table table all the existing relations should be # deleted. link_row_field = field_handler.update_field( user=user, field=link_row_field, type_name="link_row", link_row_table=users_table, ) example_table.refresh_from_db() model = example_table.get_model() rows = model.objects.all() row = rows[0] row_2 = rows[1] assert getattr(row, f"field_{link_row_field.id}").all().count() == 0 assert getattr(row_2, f"field_{link_row_field.id}").all().count() == 0 # Just check if the field can be deleted can be deleted. field_handler.delete_field(user=user, field=link_row_field) # We expect only the primary field to be left. assert Field.objects.all().count() == 1
def test_single_select_field_type_rows(data_fixture, django_assert_num_queries): user = data_fixture.create_user() database = data_fixture.create_database_application(user=user, name='Placeholder') table = data_fixture.create_database_table(name='Example', database=database) other_select_option = data_fixture.create_select_option() field_handler = FieldHandler() row_handler = RowHandler() field = field_handler.create_field(user=user, table=table, type_name='single_select', select_options=[{ 'value': 'Option 1', 'color': 'red' }, { 'value': 'Option 2', 'color': 'blue' }]) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={f'field_{field.id}': 999999}) with pytest.raises(ValidationError): row_handler.create_row( user=user, table=table, values={f'field_{field.id}': other_select_option.id}) select_options = field.select_options.all() row = row_handler.create_row( user=user, table=table, values={f'field_{field.id}': select_options[0].id}) assert getattr(row, f'field_{field.id}').id == select_options[0].id assert getattr(row, f'field_{field.id}').value == select_options[0].value assert getattr(row, f'field_{field.id}').color == select_options[0].color assert getattr(row, f'field_{field.id}_id') == select_options[0].id field = field_handler.update_field(user=user, field=field, select_options=[ { 'value': 'Option 3', 'color': 'orange' }, { 'value': 'Option 4', 'color': 'purple' }, ]) select_options = field.select_options.all() row_2 = row_handler.create_row( user=user, table=table, values={f'field_{field.id}': select_options[0].id}) assert getattr(row_2, f'field_{field.id}').id == select_options[0].id assert getattr(row_2, f'field_{field.id}').value == select_options[0].value assert getattr(row_2, f'field_{field.id}').color == select_options[0].color assert getattr(row_2, f'field_{field.id}_id') == select_options[0].id row_3 = row_handler.create_row( user=user, table=table, values={f'field_{field.id}': select_options[1].id}) assert getattr(row_3, f'field_{field.id}').id == select_options[1].id assert getattr(row_3, f'field_{field.id}_id') == select_options[1].id row_4 = row_handler.create_row( user=user, table=table, values={f'field_{field.id}': select_options[0].id}) assert getattr(row_4, f'field_{field.id}').id == select_options[0].id assert getattr(row_4, f'field_{field.id}_id') == select_options[0].id model = table.get_model() with django_assert_num_queries(2): rows = list(model.objects.all().enhance_by_fields()) assert getattr(rows[0], f'field_{field.id}') is None assert getattr(rows[1], f'field_{field.id}').id == select_options[0].id assert getattr(rows[2], f'field_{field.id}').id == select_options[1].id assert getattr(rows[3], f'field_{field.id}').id == select_options[0].id row.refresh_from_db() assert getattr(row, f'field_{field.id}') is None assert getattr(row, f'field_{field.id}_id') is None field = field_handler.update_field(user=user, field=field, new_type_name='text') assert field.select_options.all().count() == 0 model = table.get_model() rows = model.objects.all().enhance_by_fields() assert getattr(rows[0], f'field_{field.id}') is None assert getattr(rows[1], f'field_{field.id}') == 'Option 3' assert getattr(rows[2], f'field_{field.id}') == 'Option 4' assert getattr(rows[3], f'field_{field.id}') == 'Option 3' field = field_handler.update_field(user=user, field=field, new_type_name='single_select', select_options=[ { 'value': 'Option 2', 'color': 'blue' }, { 'value': 'option 3', 'color': 'purple' }, ]) assert field.select_options.all().count() == 2 model = table.get_model() rows = model.objects.all().enhance_by_fields() select_options = field.select_options.all() assert getattr(rows[0], f'field_{field.id}') is None assert getattr(rows[1], f'field_{field.id}').id == select_options[1].id assert getattr(rows[2], f'field_{field.id}') is None assert getattr(rows[3], f'field_{field.id}').id == select_options[1].id row_4 = row_handler.update_row(user=user, table=table, row_id=row_4.id, values={f'field_{field.id}': None}) assert getattr(row_4, f'field_{field.id}') is None assert getattr(row_4, f'field_{field.id}_id') is None field = field_handler.update_field(user=user, field=field, new_type_name='text') assert field.select_options.all().count() == 0 model = table.get_model() rows = model.objects.all().enhance_by_fields() assert getattr(rows[0], f'field_{field.id}') is None assert getattr(rows[1], f'field_{field.id}') == 'option 3' assert getattr(rows[2], f'field_{field.id}') is None assert getattr(rows[3], f'field_{field.id}') is None field = field_handler.update_field(user=user, field=field, new_type_name='single_select') assert field.select_options.all().count() == 0 model = table.get_model() rows = model.objects.all().enhance_by_fields() assert getattr(rows[0], f'field_{field.id}') is None assert getattr(rows[1], f'field_{field.id}') is None assert getattr(rows[2], f'field_{field.id}') is None assert getattr(rows[3], f'field_{field.id}') is None
def test_phone_number_field_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) data_fixture.create_database_table(user=user, database=table.database) field_handler = FieldHandler() row_handler = RowHandler() text_field = field_handler.create_field(user=user, table=table, order=1, type_name='text', name='name') phone_number_field = field_handler.create_field(user=user, table=table, type_name='phone_number', name='phonenumber') email_field = field_handler.create_field(user=user, table=table, type_name='email', name='email') number_field = data_fixture.create_number_field(table=table, order=1, number_negative=True, name="number") assert len(PhoneNumberField.objects.all()) == 1 model = table.get_model(attribute_names=True) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={ 'phonenumber': 'invalid phone number' }, model=model) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={ 'phonenumber': 'Phone: 2312321 2349432 ' }, model=model) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={ 'phonenumber': '1' * (PhoneNumberFieldType.MAX_PHONE_NUMBER_LENGTH+1) }, model=model) max_length_phone_number = '1' * PhoneNumberFieldType.MAX_PHONE_NUMBER_LENGTH row_handler.create_row(user=user, table=table, values={ 'name': '+45(1424) 322314 324234', 'phonenumber': max_length_phone_number, 'number': 1234534532, 'email': '*****@*****.**' }, model=model) row_handler.create_row(user=user, table=table, values={ 'name': 'some text which should be blanked out after conversion', 'phonenumber': '1234567890 NnXx,+._*()#=;/ -', 'number': 0 }, model=model) row_handler.create_row(user=user, table=table, values={ 'name': max_length_phone_number, 'phonenumber': '', 'number': -10230450, }, model=model) row_handler.create_row(user=user, table=table, values={ 'phonenumber': None, 'name': '1' * (PhoneNumberFieldType.MAX_PHONE_NUMBER_LENGTH+1) }, model=model) row_handler.create_row(user=user, table=table, values={}, model=model) # No actual database type change occurs here as a phone number field is also a text # field. Instead the after_update hook is being used to clear out invalid # phone numbers. field_handler.update_field(user=user, field=text_field, new_type_name='phone_number') field_handler.update_field(user=user, field=number_field, new_type_name='phone_number') field_handler.update_field(user=user, field=email_field, new_type_name='phone_number') model = table.get_model(attribute_names=True) rows = model.objects.all() assert rows[0].name == '+45(1424) 322314 324234' assert rows[0].phonenumber == max_length_phone_number assert rows[0].number == '1234534532' assert rows[0].email == '' assert rows[1].name == '' assert rows[1].phonenumber == '1234567890 NnXx,+._*()#=;/ -' assert rows[1].number == '0' assert rows[2].name == max_length_phone_number assert rows[2].phonenumber == '' assert rows[2].number == '-10230450' assert rows[3].name == '' assert rows[3].phonenumber == '' assert rows[3].number == '' field_handler.delete_field(user=user, field=phone_number_field) assert len(PhoneNumberField.objects.all()) == 3
def test_link_row_field_type(data_fixture): user = data_fixture.create_user() database = data_fixture.create_database_application(user=user, name="Placeholder") table = data_fixture.create_database_table(name="Example", database=database) customers_table = data_fixture.create_database_table( name="Customers", database=database ) cars_table = data_fixture.create_database_table(name="Cars", database=database) unrelated_table_1 = data_fixture.create_database_table(name="Unrelated") field_handler = FieldHandler() row_handler = RowHandler() # Create a primary field and some example data for the customers table. customers_primary_field = field_handler.create_field( user=user, table=customers_table, type_name="text", name="Name", primary=True ) customers_row_1 = row_handler.create_row( user=user, table=customers_table, values={f"field_{customers_primary_field.id}": "John"}, ) customers_row_2 = row_handler.create_row( user=user, table=customers_table, values={f"field_{customers_primary_field.id}": "Jane"}, ) # Create a primary field and some example data for the cars table. cars_primary_field = field_handler.create_field( user=user, table=cars_table, type_name="text", name="Name", primary=True ) row_handler.create_row( user=user, table=cars_table, values={f"field_{cars_primary_field.id}": "BMW"} ) row_handler.create_row( user=user, table=cars_table, values={f"field_{cars_primary_field.id}": "Audi"} ) with pytest.raises(LinkRowTableNotProvided): field_handler.create_field( user=user, table=table, type_name="link_row", name="Without table" ) with pytest.raises(LinkRowTableNotInSameDatabase): field_handler.create_field( user=user, table=table, type_name="link_row", name="Unrelated", link_row_table=unrelated_table_1, ) link_field_1 = field_handler.create_field( user=user, table=table, type_name="link_row", name="Customer", link_row_table=customers_table, ) link_field_2 = field_handler.create_field( user=user, table=table, type_name="link_row", name="Customer", link_row_table=customers_table, ) assert link_field_1.link_row_related_field.name == "Example" assert link_field_2.link_row_related_field.name == "Example" connection = connections["default"] tables = connection.introspection.table_names() assert ( link_field_1.through_table_name == link_field_1.link_row_related_field.through_table_name ) assert ( link_field_2.through_table_name == link_field_2.link_row_related_field.through_table_name ) assert link_field_1.through_table_name in tables assert link_field_2.through_table_name in tables model = table.get_model() table_row = model.objects.create() getattr(table_row, f"field_{link_field_1.id}").add(customers_row_1.id) results = getattr(table_row, f"field_{link_field_1.id}").all() assert len(results) == 1 assert getattr(results[0], f"field_{customers_primary_field.id}") == "John" getattr(table_row, f"field_{link_field_2.id}").add( customers_row_1.id, customers_row_2.id ) results = getattr(table_row, f"field_{link_field_2.id}").all() assert len(results) == 2 assert getattr(results[0], f"field_{customers_primary_field.id}") == "John" assert getattr(results[1], f"field_{customers_primary_field.id}") == "Jane" table_row_2 = model.objects.create() getattr(table_row_2, f"field_{link_field_1.id}").add(customers_row_2.id) results = getattr(table_row_2, f"field_{link_field_1.id}").all() assert len(results) == 1 assert getattr(results[0], f"field_{customers_primary_field.id}") == "Jane" # Going to change only the name of the field. This should not result in any errors # of schema changes. link_field_1 = field_handler.update_field(user, link_field_1, name="Customer 2") with pytest.raises(LinkRowTableNotInSameDatabase): field_handler.update_field(user, link_field_1, link_row_table=unrelated_table_1) model = table.get_model() assert model.objects.all().count() == 2 # Change the table, this should destroy all relations. old_link_field_1_relation_id = link_field_1.link_row_relation_id link_field_1 = field_handler.update_field( user, link_field_1, link_row_table=cars_table ) model = table.get_model() table_rows = model.objects.all() table_row = table_rows[0] table_row_2 = table_rows[1] assert link_field_1.link_row_table.id == cars_table.id assert link_field_1.link_row_relation_id == old_link_field_1_relation_id assert getattr(table_row, f"field_{link_field_1.id}").all().count() == 0 assert getattr(table_row, f"field_{link_field_2.id}").all().count() == 2 assert getattr(table_row_2, f"field_{link_field_1.id}").all().count() == 0 assert getattr(table_row_2, f"field_{link_field_2.id}").all().count() == 0 link_field_2 = field_handler.update_field(user, link_field_2, new_type_name="text") model = table.get_model() table_row = model.objects.all().first() assert getattr(table_row, f"field_{link_field_2.id}") is None assert LinkRowField.objects.all().count() == 2 setattr(table_row, f"field_{link_field_2.id}", "Text value") table_row.save() assert getattr(table_row, f"field_{link_field_2.id}") == "Text value" # Delete the existing field. Alter that the related field should be deleted and # no table named _relation_ should exist. field_handler.delete_field(user, link_field_1) assert LinkRowField.objects.all().count() == 0 for t in connection.introspection.table_names(): if "_relation_" in t: assert False # Change a the text field back into a link row field. link_field_2 = field_handler.update_field( user, link_field_2, new_type_name="link_row", link_row_table=customers_table ) assert link_field_2.link_row_related_field.name == "Example" assert ( link_field_2.through_table_name == link_field_2.link_row_related_field.through_table_name ) assert link_field_2.through_table_name in connection.introspection.table_names() assert LinkRowField.objects.all().count() == 2 model = table.get_model() table_row = model.objects.all().first() getattr(table_row, f"field_{link_field_2.id}").add( customers_row_1.id, customers_row_2.id ) results = getattr(table_row, f"field_{link_field_2.id}").all() assert len(results) == 2 assert getattr(results[0], f"field_{customers_primary_field.id}") == "John" assert getattr(results[1], f"field_{customers_primary_field.id}") == "Jane"
def test_file_field_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) user_file_1 = data_fixture.create_user_file() user_file_2 = data_fixture.create_user_file() user_file_3 = data_fixture.create_user_file() field_handler = FieldHandler() row_handler = RowHandler() file = field_handler.create_field(user=user, table=table, type_name='file', name='File') assert FileField.objects.all().count() == 1 model = table.get_model(attribute_names=True) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={ 'file': 'not_a_json' }, model=model) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={ 'file': {} }, model=model) with pytest.raises(ValidationError): row_handler.create_row(user=user, table=table, values={ 'file': [{'no_name': 'test'}] }, model=model) with pytest.raises(InvalidUserFileNameError): row_handler.create_row(user=user, table=table, values={ 'file': [{'name': 'wrongfilename.jpg'}] }, model=model) with pytest.raises(UserFileDoesNotExist): row_handler.create_row(user=user, table=table, values={ 'file': [{'name': 'file_name.jpg'}] }, model=model) row = row_handler.create_row(user=user, table=table, values={ 'file': [{'name': user_file_1.name}] }, model=model) assert row.file[0]['visible_name'] == user_file_1.original_name del row.file[0]['visible_name'] assert row.file[0] == user_file_1.serialize() row = row_handler.create_row(user=user, table=table, values={ 'file': [ {'name': user_file_2.name}, {'name': user_file_1.name}, {'name': user_file_1.name} ] }, model=model) assert row.file[0]['visible_name'] == user_file_2.original_name assert row.file[1]['visible_name'] == user_file_1.original_name assert row.file[2]['visible_name'] == user_file_1.original_name del row.file[0]['visible_name'] del row.file[1]['visible_name'] del row.file[2]['visible_name'] assert row.file[0] == user_file_2.serialize() assert row.file[1] == user_file_1.serialize() assert row.file[2] == user_file_1.serialize() row = row_handler.create_row(user=user, table=table, values={ 'file': [ {'name': user_file_1.name}, {'name': user_file_3.name}, {'name': user_file_2.name} ] }, model=model) assert row.file[0]['visible_name'] == user_file_1.original_name assert row.file[1]['visible_name'] == user_file_3.original_name assert row.file[2]['visible_name'] == user_file_2.original_name del row.file[0]['visible_name'] del row.file[1]['visible_name'] del row.file[2]['visible_name'] assert row.file[0] == user_file_1.serialize() assert row.file[1] == user_file_3.serialize() assert row.file[2] == user_file_2.serialize() row = row_handler.update_row(user=user, table=table, row_id=row.id, values={ 'file': [ {'name': user_file_1.name, 'visible_name': 'not_original.jpg'}, ] }, model=model) assert row.file[0]['visible_name'] == 'not_original.jpg' del row.file[0]['visible_name'] assert row.file[0] == user_file_1.serialize() assert model.objects.all().count() == 3 field_handler.delete_field(user=user, field=file) assert FileField.objects.all().count() == 0 model.objects.all().delete() text = field_handler.create_field(user=user, table=table, type_name='text', name='Text') model = table.get_model(attribute_names=True) row = row_handler.create_row(user=user, table=table, values={ 'text': 'Some random text' }, model=model) row_handler.create_row(user=user, table=table, values={ 'text': '["Not compatible"]' }, model=model) row_handler.create_row(user=user, table=table, values={ 'text': json.dumps(user_file_1.serialize()) }, model=model) file = field_handler.update_field(user=user, table=table, field=text, new_type_name='file', name='File') model = table.get_model(attribute_names=True) results = model.objects.all() assert results[0].file == [] assert results[1].file == [] assert results[2].file == [] row_handler.update_row(user=user, table=table, row_id=row.id, values={ 'file': [ {'name': user_file_1.name, 'visible_name': 'not_original.jpg'}, ] }, model=model) field_handler.update_field(user=user, table=table, field=file, new_type_name='text', name='text') model = table.get_model(attribute_names=True) results = model.objects.all() assert results[0].text is None assert results[1].text is None assert results[2].text is None