def get(self, request, field_id): """Selects a single field and responds with a serialized version.""" field = FieldHandler().get_field(field_id) field.table.database.group.has_user(request.user, raise_error=True) serializer = field_type_registry.get_serializer(field, FieldSerializer) return Response(serializer.data)
def test_link_row_enhance_queryset(data_fixture, django_assert_num_queries): 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) 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], }) row_3 = row_handler.create_row(user=user, table=example_table, values={ f'field_{link_row_field.id}': [customers_row_3.id], }) model = example_table.get_model() rows = list(model.objects.all().enhance_by_fields()) with django_assert_num_queries(0): for row in rows: list(getattr(row, f'field_{link_row_field.id}').all())
def test_single_select_field_type_random_value(data_fixture): """ Verify that the random_value function of the single select field type correctly returns one option of a given select_options list. If the select_options list is empty or the passed field type is not of single select field type by any chance it should return None. """ 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() cache = {} fake = Faker() field = field_handler.create_field( user=user, table=table, type_name='single_select', name='Single select', select_options=[{ 'value': 'Option 1', 'color': 'blue' }, { 'value': 'Option 2', 'color': 'red' }], ) select_options = field.select_options.all() random_choice = SingleSelectFieldType().random_value(field, fake, cache) assert random_choice in select_options random_choice = SingleSelectFieldType().random_value(field, fake, cache) assert random_choice in select_options email_field = field_handler.create_field( user=user, table=table, type_name='email', name='E-Mail', ) random_choice_2 = SingleSelectFieldType().random_value( email_field, fake, cache) assert random_choice_2 is None
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_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_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_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 post(self, request, data, table_id): """Creates a new field for a table.""" type_name = data.pop('type') table = TableHandler().get_table(request.user, table_id) field = FieldHandler().create_field(request.user, table, type_name, **data) serializer = field_type_registry.get_serializer(field, FieldSerializer) return Response(serializer.data)
def patch(self, request, field_id): """Updates the field if the user belongs to the group.""" field = FieldHandler().get_field( request.user, field_id, base_queryset=Field.objects.select_for_update() ).specific type_name = type_from_data_or_registry(request.data, field_type_registry, field) field_type = field_type_registry.get(type_name) data = validate_data_custom_fields(type_name, field_type_registry, request.data, base_serializer_class=UpdateFieldSerializer) # Because each field type can raise custom exceptions at while updating the # field we need to be able to map those to the correct API exceptions which are # defined in the type. with field_type.map_api_exceptions(): field = FieldHandler().update_field(request.user, field, type_name, **data) serializer = field_type_registry.get_serializer(field, FieldSerializer) return Response(serializer.data)
def test_field_updated(mock_broadcast_to_channel_group, data_fixture): user = data_fixture.create_user() field = data_fixture.create_text_field(user=user) FieldHandler().update_field(user=user, field=field, name="field") mock_broadcast_to_channel_group.delay.assert_called_once() args = mock_broadcast_to_channel_group.delay.call_args assert args[0][0] == f"table-{field.table.id}" assert args[0][1]["type"] == "field_updated" assert args[0][1]["field_id"] == field.id assert args[0][1]["field"]["id"] == field.id
def test_call_apps_registry_pending_operations(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 ) field_handler = FieldHandler() field_handler.create_field( user=user, table=table, type_name="link_row", name="Test", link_row_table=customers_table, ) table.get_model() # Make sure that there are no pending operations in the app registry. Because a # Django ManyToManyField registers pending operations every time a table model is # generated, which can causes a memory leak if they are not triggered. assert len(apps._pending_operations) == 0
def test_field_updated(mock_broadcast_to_channel_group, data_fixture): user = data_fixture.create_user() field = data_fixture.create_text_field(user=user) FieldHandler().update_field(user=user, field=field, name='field') mock_broadcast_to_channel_group.delay.assert_called_once() args = mock_broadcast_to_channel_group.delay.call_args assert args[0][0] == f'table-{field.table.id}' assert args[0][1]['type'] == 'field_updated' assert args[0][1]['field_id'] == field.id assert args[0][1]['field']['id'] == field.id
def patch(self, request, field_id): """Updates the field if the user belongs to the group.""" field = FieldHandler().get_field( request.user, field_id, base_queryset=Field.objects.select_for_update()).specific type_name = type_from_data_or_registry(request.data, field_type_registry, field) data = validate_data_custom_fields( type_name, field_type_registry, request.data, base_serializer_class=UpdateFieldSerializer) field = FieldHandler().update_field(request.user, field, type_name, **data) serializer = field_type_registry.get_serializer(field, FieldSerializer) return Response(serializer.data)
def test_field_created(mock_broadcast_to_channel_group, data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field = FieldHandler().create_field(user=user, table=table, type_name="text", name="Grid") mock_broadcast_to_channel_group.delay.assert_called_once() args = mock_broadcast_to_channel_group.delay.call_args assert args[0][0] == f"table-{table.id}" assert args[0][1]["type"] == "field_created" assert args[0][1]["field"]["id"] == field.id
def test_field_created(mock_broadcast_to_channel_group, data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) field = FieldHandler().create_field(user=user, table=table, type_name='text', name='Grid') mock_broadcast_to_channel_group.delay.assert_called_once() args = mock_broadcast_to_channel_group.delay.call_args assert args[0][0] == f'table-{table.id}' assert args[0][1]['type'] == 'field_created' assert args[0][1]['field']['id'] == field.id
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 fill_initial_table_data(self, user, table): """ Fills the table with some initial data. A new table is expected that already has the a primary field named 'name'. :param user: The user on whose behalf the table is filled. :type: user: User :param table: The table that needs the initial data. :type table: User """ view_handler = ViewHandler() field_handler = FieldHandler() view = view_handler.create_view(user, table, GridViewType.type, name='Grid') notes = field_handler.create_field(user, table, LongTextFieldType.type, name='Notes') active = field_handler.create_field(user, table, BooleanFieldType.type, name='Active') field_options = {notes.id: {'width': 400}, active.id: {'width': 100}} fields = [notes, active] view_handler.update_grid_view_field_options(view, field_options, fields=fields) model = table.get_model(attribute_names=True) model.objects.create(name='Tesla', active=True) model.objects.create(name='Amazon', active=False)
def post(self, request, data, table_id): """Creates a new field for a table.""" type_name = data.pop('type') field_type = field_type_registry.get(type_name) table = TableHandler().get_table(request.user, table_id) # Because each field type can raise custom exceptions while creating the # field we need to be able to map those to the correct API exceptions which are # defined in the type. with field_type.map_api_exceptions(): field = FieldHandler().create_field(request.user, table, type_name, **data) serializer = field_type_registry.get_serializer(field, FieldSerializer) return Response(serializer.data)
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_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_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_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_import_export_link_row_field(data_fixture, user_tables_in_separate_db): user = data_fixture.create_user() imported_group = data_fixture.create_group(user=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 ) field_handler = FieldHandler() core_handler = CoreHandler() link_row_field = field_handler.create_field( user=user, table=table, type_name="link_row", link_row_table=customers_table ) row_handler = RowHandler() c_row = row_handler.create_row(user=user, table=customers_table, values={}) c_row_2 = row_handler.create_row(user=user, table=customers_table, values={}) row = row_handler.create_row( user=user, table=table, values={f"field_{link_row_field.id}": [c_row.id, c_row_2.id]}, ) exported_applications = core_handler.export_group_applications( database.group, BytesIO() ) imported_applications, id_mapping = core_handler.import_applications_to_group( imported_group, exported_applications, BytesIO(), None ) imported_database = imported_applications[0] imported_tables = imported_database.table_set.all() imported_table = imported_tables[0] imported_customers_table = imported_tables[1] imported_link_row_field = imported_table.field_set.all().first().specific imported_link_row_relation_field = ( imported_customers_table.field_set.all().first().specific ) assert imported_table.id != table.id assert imported_table.name == table.name assert imported_customers_table.id != customers_table.id assert imported_customers_table.name == customers_table.name assert imported_link_row_field.id != link_row_field.id assert imported_link_row_field.name == link_row_field.name assert imported_link_row_field.link_row_table_id == imported_customers_table.id assert imported_link_row_relation_field.link_row_table_id == imported_table.id assert imported_link_row_field.link_row_relation_id == ( imported_link_row_relation_field.link_row_relation_id ) imported_c_row = row_handler.get_row( user=user, table=imported_customers_table, row_id=c_row.id ) imported_c_row_2 = row_handler.get_row( user=user, table=imported_customers_table, row_id=c_row_2.id ) imported_row = row_handler.get_row(user=user, table=imported_table, row_id=row.id) assert imported_row.id == row.id assert imported_c_row.id == c_row.id assert imported_c_row_2.id == c_row_2.id assert [ r.id for r in getattr(imported_row, f"field_{imported_link_row_field.id}").all() ] == [imported_c_row.id, imported_c_row_2.id]
def test_link_row_field_type_api_row_views(api_client, data_fixture): user, token = data_fixture.create_user_and_token() 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 ) grid = data_fixture.create_grid_view(table=example_table) data_fixture.create_text_field(name="Name", table=example_table, primary=True) customers_primary = data_fixture.create_text_field( name="Customer name", table=customers_table, primary=True ) 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, values={f"field_{customers_primary.id}": "John Doe"}, ) customers_row_2 = row_handler.create_row( user=user, table=customers_table, values={f"field_{customers_primary.id}": "Jane Doe"}, ) customers_row_3 = row_handler.create_row(user=user, table=customers_table) response = api_client.post( reverse("api:database:rows:list", kwargs={"table_id": example_table.id}), { f"field_{link_row_field.id}": "Random", }, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) response_json = response.json() assert response.status_code == HTTP_400_BAD_REQUEST assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION" assert ( response_json["detail"][f"field_{link_row_field.id}"][0]["code"] == "not_a_list" ) response = api_client.post( reverse("api:database:rows:list", kwargs={"table_id": example_table.id}), { f"field_{link_row_field.id}": ["a"], }, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) response_json = response.json() assert response.status_code == HTTP_400_BAD_REQUEST assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION" assert ( response_json["detail"][f"field_{link_row_field.id}"]["0"][0]["code"] == "invalid" ) response = api_client.post( reverse("api:database:rows:list", kwargs={"table_id": example_table.id}), { f"field_{link_row_field.id}": [customers_row_1.id, customers_row_2.id, 999], }, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) response_json = response.json() row_id = response_json["id"] assert response.status_code == HTTP_200_OK assert len(response_json[f"field_{link_row_field.id}"]) == 2 assert response_json[f"field_{link_row_field.id}"][0]["id"] == customers_row_1.id assert response_json[f"field_{link_row_field.id}"][0]["value"] == "John Doe" assert response_json[f"field_{link_row_field.id}"][1]["id"] == customers_row_2.id assert response_json[f"field_{link_row_field.id}"][1]["value"] == "Jane Doe" model = example_table.get_model() assert model.objects.all().count() == 1 url = reverse( "api:database:rows:item", kwargs={"table_id": example_table.id, "row_id": row_id}, ) response = api_client.patch( url, { f"field_{link_row_field.id}": [], }, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) response_json = response.json() assert response.status_code == HTTP_200_OK assert len(response_json[f"field_{link_row_field.id}"]) == 0 url = reverse( "api:database:rows:item", kwargs={"table_id": example_table.id, "row_id": row_id}, ) response = api_client.patch( url, { f"field_{link_row_field.id}": [customers_row_2.id, customers_row_3.id], }, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) response_json = response.json() assert response.status_code == HTTP_200_OK assert len(response_json[f"field_{link_row_field.id}"]) == 2 assert response_json[f"field_{link_row_field.id}"][0]["id"] == customers_row_2.id assert response_json[f"field_{link_row_field.id}"][0]["value"] == "Jane Doe" assert response_json[f"field_{link_row_field.id}"][1]["id"] == customers_row_3.id assert not response_json[f"field_{link_row_field.id}"][1]["value"] url = reverse("api:database:views:grid:list", kwargs={"view_id": grid.id}) response = api_client.get(url, **{"HTTP_AUTHORIZATION": f"JWT {token}"}) response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json["count"] == 1 assert response_json["results"][0]["id"] == row_id assert len(response_json["results"][0][f"field_{link_row_field.id}"]) == 2 url = reverse( "api:database:rows:item", kwargs={"table_id": example_table.id, "row_id": row_id}, ) response = api_client.delete(url, HTTP_AUTHORIZATION=f"JWT {token}") assert response.status_code == HTTP_204_NO_CONTENT assert model.objects.all().count() == 0 response = api_client.post( reverse("api:database:rows:list", kwargs={"table_id": example_table.id}), {}, format="json", HTTP_AUTHORIZATION=f"JWT {token}", ) response_json = response.json() assert response.status_code == HTTP_200_OK assert len(response_json[f"field_{link_row_field.id}"]) == 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 ) 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_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_not_empty_filter_type(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) grid_view = data_fixture.create_grid_view(table=table) text_field = data_fixture.create_text_field(table=table) long_text_field = data_fixture.create_long_text_field(table=table) integer_field = data_fixture.create_number_field(table=table) decimal_field = data_fixture.create_number_field(table=table, number_type='DECIMAL', number_decimal_places=2) date_field = data_fixture.create_date_field(table=table) date_time_field = data_fixture.create_date_field(table=table, date_include_time=True) boolean_field = data_fixture.create_boolean_field(table=table) file_field = data_fixture.create_file_field(table=table) single_select_field = data_fixture.create_single_select_field(table=table) option_1 = data_fixture.create_select_option(field=single_select_field) tmp_table = data_fixture.create_database_table(database=table.database) tmp_field = data_fixture.create_text_field(table=tmp_table, primary=True) link_row_field = FieldHandler().create_field(user=user, table=table, type_name='link_row', link_row_table=tmp_table) tmp_row = tmp_table.get_model().objects.create( **{f'field_{tmp_field.id}': 'Test'}) handler = ViewHandler() model = table.get_model() utc = timezone('UTC') model.objects.create( **{ f'field_{text_field.id}': '', f'field_{long_text_field.id}': '', f'field_{integer_field.id}': None, f'field_{decimal_field.id}': None, f'field_{date_field.id}': None, f'field_{date_time_field.id}': None, f'field_{boolean_field.id}': False, f'field_{file_field.id}': [], f'field_{single_select_field.id}': None }) row_2 = model.objects.create( **{ f'field_{text_field.id}': 'Value', f'field_{long_text_field.id}': 'Value', f'field_{integer_field.id}': 10, f'field_{decimal_field.id}': 1022, f'field_{date_field.id}': date(2020, 6, 17), f'field_{date_time_field.id}': make_aware(datetime(2020, 6, 17, 1, 30, 0), utc), f'field_{boolean_field.id}': True, f'field_{file_field.id}': [{ 'name': 'test_file.png' }], f'field_{single_select_field.id}_id': option_1.id }) getattr(row_2, f'field_{link_row_field.id}').add(tmp_row.id) filter = data_fixture.create_view_filter(view=grid_view, field=text_field, type='not_empty', value='') assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = long_text_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = integer_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = decimal_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = date_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = date_time_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = link_row_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = boolean_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = file_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id filter.field = single_select_field filter.save() assert handler.apply_filters(grid_view, model.objects.all()).get().id == row_2.id
def test_link_row_field_type_api_row_views(api_client, data_fixture): user, token = data_fixture.create_user_and_token() 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) grid = data_fixture.create_grid_view(table=example_table) example_primary = data_fixture.create_text_field(name='Name', table=example_table, primary=True) customers_primary = data_fixture.create_text_field(name='Customer name', table=customers_table, primary=True) 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, values={f'field_{customers_primary.id}': 'John Doe'}) customers_row_2 = row_handler.create_row( user=user, table=customers_table, values={f'field_{customers_primary.id}': 'Jane Doe'}) customers_row_3 = row_handler.create_row(user=user, table=customers_table) response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': example_table.id}), { f'field_{link_row_field.id}': 'Random', }, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_400_BAD_REQUEST assert response_json['error'] == 'ERROR_REQUEST_BODY_VALIDATION' assert (response_json['detail'][f'field_{link_row_field.id}'][0]['code'] == 'not_a_list') response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': example_table.id}), { f'field_{link_row_field.id}': ['a'], }, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_400_BAD_REQUEST assert response_json['error'] == 'ERROR_REQUEST_BODY_VALIDATION' assert (response_json['detail'][f'field_{link_row_field.id}']['0'][0] ['code'] == 'invalid') response = api_client.post(reverse( 'api:database:rows:list', kwargs={'table_id': example_table.id}), { f'field_{link_row_field.id}': [customers_row_1.id, customers_row_2.id, 999], }, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() row_id = response_json['id'] assert response.status_code == HTTP_200_OK assert len(response_json[f'field_{link_row_field.id}']) == 2 assert response_json[f'field_{link_row_field.id}'][0][ 'id'] == customers_row_1.id assert response_json[f'field_{link_row_field.id}'][0][ 'value'] == 'John Doe' assert response_json[f'field_{link_row_field.id}'][1][ 'id'] == customers_row_2.id assert response_json[f'field_{link_row_field.id}'][1][ 'value'] == 'Jane Doe' model = example_table.get_model() assert model.objects.all().count() == 1 url = reverse('api:database:rows:item', kwargs={ 'table_id': example_table.id, 'row_id': row_id }) response = api_client.patch(url, { f'field_{link_row_field.id}': [], }, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert len(response_json[f'field_{link_row_field.id}']) == 0 url = reverse('api:database:rows:item', kwargs={ 'table_id': example_table.id, 'row_id': row_id }) response = api_client.patch(url, { f'field_{link_row_field.id}': [customers_row_2.id, customers_row_3.id], }, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert len(response_json[f'field_{link_row_field.id}']) == 2 assert response_json[f'field_{link_row_field.id}'][0][ 'id'] == customers_row_2.id assert response_json[f'field_{link_row_field.id}'][0][ 'value'] == 'Jane Doe' assert response_json[f'field_{link_row_field.id}'][1][ 'id'] == customers_row_3.id assert not response_json[f'field_{link_row_field.id}'][1]['value'] url = reverse('api:database:views:grid:list', kwargs={'view_id': grid.id}) response = api_client.get(url, **{'HTTP_AUTHORIZATION': f'JWT {token}'}) response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json['count'] == 1 assert response_json['results'][0]['id'] == row_id assert len(response_json['results'][0][f'field_{link_row_field.id}']) == 2 url = reverse('api:database:rows:item', kwargs={ 'table_id': example_table.id, 'row_id': row_id }) response = api_client.delete(url, HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_204_NO_CONTENT assert model.objects.all().count() == 0 response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': example_table.id}), {}, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert len(response_json[f'field_{link_row_field.id}']) == 0
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