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 fill_example_table_data(self, user, table): """ Fills the table with some initial example 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 create_a_column_for_every_type(table): field_handler = FieldHandler() all_kwargs_per_type = construct_all_possible_field_kwargs(None) for field_type_name, all_possible_kwargs in all_kwargs_per_type.items( ): if field_type_name == "link_row": continue i = 0 for kwargs in all_possible_kwargs: i = i + 1 field_handler.create_field(table.database.group.users.first(), table, field_type_name, **kwargs)
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_import_export_single_select_field(data_fixture): user = data_fixture.create_user() table = data_fixture.create_database_table(user=user) 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' }]) select_option = field.select_options.all().first() field_type = field_type_registry.get_by_model(field) field_serialized = field_type.export_serialized(field) id_mapping = {} field_imported = field_type.import_serialized(table, field_serialized, id_mapping) assert field_imported.select_options.all().count() == 1 imported_select_option = field_imported.select_options.all().first() assert imported_select_option.id != select_option.id assert imported_select_option.value == select_option.value assert imported_select_option.color == select_option.color assert imported_select_option.order == select_option.order
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_create_primary_field(data_fixture): user = data_fixture.create_user() table_1 = data_fixture.create_database_table(user=user) table_2 = data_fixture.create_database_table(user=user) data_fixture.create_text_field(table=table_1, primary=True) with pytest.raises(PrimaryFieldAlreadyExists): handler = FieldHandler() handler.create_field(user=user, table=table_1, type_name='text', primary=True) handler = FieldHandler() field = handler.create_field(user=user, table=table_2, type_name='text', primary=True) assert field.primary with pytest.raises(PrimaryFieldAlreadyExists): handler.create_field(user=user, table=table_2, type_name='text', primary=True) # Should be able to create a regular field when there is already a primary field. handler.create_field(user=user, table=table_2, type_name='text', primary=False)
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_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_handler.create_row( user=user, table=example_table, values={ f"field_{link_row_field.id}": [customers_row_1.id, customers_row_2.id], }, ) row_handler.create_row( user=user, table=example_table, values={ f"field_{link_row_field.id}": [customers_row_1.id], }, ) 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(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_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_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_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_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_single_select_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') table = data_fixture.create_database_table(name='Example', database=database) other_select_option = data_fixture.create_select_option() field_handler = FieldHandler() 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' }]) select_options = field.select_options.all() response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), {f'field_{field.id}': 'Nothing'}, 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_{field.id}'][0][ 'code'] == 'incorrect_type' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), {f'field_{field.id}': 999999}, 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_{field.id}'][0][ 'code'] == 'does_not_exist' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), {f'field_{field.id}': other_select_option.id}, 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_{field.id}'][0][ 'code'] == 'does_not_exist' response = api_client.post(reverse('api:database:rows:list', kwargs={'table_id': table.id}), {f'field_{field.id}': select_options[0].id}, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json[f'field_{field.id}']['id'] == select_options[0].id assert response_json[f'field_{field.id}']['value'] == 'Option 1' assert response_json[f'field_{field.id}']['color'] == 'red' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': response_json['id'] }) response = api_client.patch(url, {}, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_200_OK url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': response_json['id'] }) response = api_client.patch(url, {f'field_{field.id}': select_options[1].id}, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json[f'field_{field.id}']['id'] == select_options[1].id assert response_json[f'field_{field.id}']['value'] == 'Option 2' assert response_json[f'field_{field.id}']['color'] == 'blue' url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': response_json['id'] }) response = api_client.patch(url, {f'field_{field.id}': None}, format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert response.status_code == HTTP_200_OK assert response_json[f'field_{field.id}'] is None url = reverse('api:database:rows:item', kwargs={ 'table_id': table.id, 'row_id': response_json['id'] }) response = api_client.delete(url, format='json', HTTP_AUTHORIZATION=f'JWT {token}') assert response.status_code == HTTP_204_NO_CONTENT assert SelectOption.objects.all().count() == 3
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_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
def test_can_convert_between_all_fields(data_fixture): """ A nuclear option test turned off by default to help verify changes made to field conversions work in every possible conversion scenario. This test checks is possible to convert from every possible field to every other possible field including converting to themselves. It only checks that the conversion does not raise any exceptions. """ user = data_fixture.create_user() database = data_fixture.create_database_application(user=user) table = data_fixture.create_database_table(database=database, user=user) link_table = data_fixture.create_database_table(database=database, user=user) data_fixture.create_text_field(table=link_table, name="text_field", primary=True) handler = FieldHandler() row_handler = RowHandler() fake = Faker() model = table.get_model() cache = {} # Make a blank row to test empty field conversion also. model.objects.create(**{}) second_row_with_values = model.objects.create(**{}) # Some baserow field types have multiple different 'modes' which result in # different conversion behaviour or entirely different database columns being # created. Here the kwargs which control these modes are enumerated so we can then # generate every possible type of conversion. all_possible_kwargs_per_type = construct_all_possible_field_kwargs( link_table) i = 1 for field_type_name, all_possible_kwargs in all_possible_kwargs_per_type.items( ): for kwargs in all_possible_kwargs: for inner_field_type_name in field_type_registry.get_types(): for inner_kwargs in all_possible_kwargs_per_type[ inner_field_type_name]: field_type = field_type_registry.get(field_type_name) from_field = handler.create_field( user=user, table=table, type_name=field_type_name, **kwargs, ) random_value = field_type.random_value( from_field, fake, cache) if isinstance(random_value, date): # Faker produces subtypes of date / datetime which baserow # does not want, instead just convert to str. random_value = str(random_value) row_handler.update_row( user=user, table=table, row_id=second_row_with_values.id, values={f"field_{from_field.id}": random_value}, ) handler.update_field( user=user, field=from_field, new_type_name=inner_field_type_name, **inner_kwargs, ) i = i + 1
def test_create_field(data_fixture): user = data_fixture.create_user() user_2 = data_fixture.create_user() table = data_fixture.create_database_table(user=user) handler = FieldHandler() handler.create_field(user=user, table=table, type_name='text', name='Test text field', text_default='Some default') assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 1 text_field = TextField.objects.all().first() assert text_field.name == 'Test text field' assert text_field.order == 1 assert text_field.table == table assert text_field.text_default == 'Some default' assert not text_field.primary table_model = table.get_model() field_name = f'field_{text_field.id}' assert field_name in [ field.name for field in table_model._meta.get_fields() ] instance = table_model.objects.create(**{field_name: 'Test 1'}) assert getattr(instance, field_name) == 'Test 1' instance_2 = table_model.objects.create() assert getattr(instance_2, field_name) == 'Some default' with pytest.raises(ValueError): handler.create_field(user=user, table=table, type_name='number', name='Test number field', number_type='NOT_EXISTING') with pytest.raises(ValueError): handler.create_field(user=user, table=table, type_name='number', name='Test number field', number_type='DECIMAL', number_decimal_places=9999) handler.create_field(user=user, table=table, type_name='number', name='Test number field', number_type='INTEGER', number_decimal_places=2, number_negative=True) number_field = NumberField.objects.all().first() assert number_field.name == 'Test number field' assert number_field.order == 2 assert number_field.table == table assert number_field.number_type == 'INTEGER' assert number_field.number_decimal_places == 2 assert number_field.number_negative handler.create_field(user=user, table=table, type_name='boolean', name='Test boolean field', random_other_field='WILL_BE_IGNORED') boolean_field = BooleanField.objects.all().first() assert boolean_field.name == 'Test boolean field' assert boolean_field.order == 3 assert boolean_field.table == table assert Field.objects.all().count() == 3 assert TextField.objects.all().count() == 1 assert NumberField.objects.all().count() == 1 assert BooleanField.objects.all().count() == 1 with pytest.raises(UserNotInGroupError): handler.create_field(user=user_2, table=table, type_name='text') with pytest.raises(FieldTypeDoesNotExist): handler.create_field(user=user, table=table, type_name='UNKNOWN')
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_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_primary_single_select_field_with_link_row_field( api_client, data_fixture, django_assert_num_queries): """ We expect the relation to a table that has a single select field to work. """ 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) field_handler = FieldHandler() row_handler = RowHandler() data_fixture.create_text_field(name='Name', table=example_table, primary=True) customers_primary = field_handler.create_field(user=user, table=customers_table, type_name='single_select', select_options=[{ 'value': 'Option 1', 'color': 'red' }, { 'value': 'Option 2', 'color': 'blue' }, { 'value': 'Option 3', 'color': 'orange' }], primary=True) link_row_field = field_handler.create_field(user=user, table=example_table, type_name='link_row', link_row_table=customers_table) select_options = customers_primary.select_options.all() customers_row_1 = row_handler.create_row( user=user, table=customers_table, values={f'field_{customers_primary.id}': select_options[0].id}) customers_row_2 = row_handler.create_row( user=user, table=customers_table, values={f'field_{customers_primary.id}': select_options[1].id}) customers_row_3 = row_handler.create_row( user=user, table=customers_table, values={f'field_{customers_primary.id}': select_options[2].id}) row_handler.create_row(user, table=example_table, values={ f'field_{link_row_field.id}': [customers_row_1.id, customers_row_2.id] }) row_handler.create_row( user, table=example_table, values={f'field_{link_row_field.id}': [customers_row_1.id]}) row_handler.create_row( user, table=example_table, values={f'field_{link_row_field.id}': [customers_row_3.id]}) model = example_table.get_model() queryset = model.objects.all().enhance_by_fields() serializer_class = get_row_serializer_class(model, RowSerializer, is_response=True) with django_assert_num_queries(3): serializer = serializer_class(queryset, many=True) serializer.data response = api_client.get(reverse('api:database:rows:list', kwargs={'table_id': example_table.id}), format='json', HTTP_AUTHORIZATION=f'JWT {token}') response_json = response.json() assert (response_json['results'][0][f'field_{link_row_field.id}'][0] ['value'] == 'Option 1') assert (response_json['results'][0][f'field_{link_row_field.id}'][1] ['value'] == 'Option 2') assert (response_json['results'][1][f'field_{link_row_field.id}'][0] ['value'] == 'Option 1') assert (response_json['results'][2][f'field_{link_row_field.id}'][0] ['value'] == 'Option 3')
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_create_field(send_mock, data_fixture): user = data_fixture.create_user() user_2 = data_fixture.create_user() table = data_fixture.create_database_table(user=user) handler = FieldHandler() field = handler.create_field( user=user, table=table, type_name="text", name="Test text field", text_default="Some default", ) 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 assert Field.objects.all().count() == 1 assert TextField.objects.all().count() == 1 text_field = TextField.objects.all().first() assert text_field.name == "Test text field" assert text_field.order == 1 assert text_field.table == table assert text_field.text_default == "Some default" assert not text_field.primary table_model = table.get_model() field_name = f"field_{text_field.id}" assert field_name in [ field.name for field in table_model._meta.get_fields() ] instance = table_model.objects.create(**{field_name: "Test 1"}) assert getattr(instance, field_name) == "Test 1" instance_2 = table_model.objects.create() assert getattr(instance_2, field_name) == "Some default" with pytest.raises(ValueError): handler.create_field( user=user, table=table, type_name="number", name="Test number field", number_type="NOT_EXISTING", ) with pytest.raises(ValueError): handler.create_field( user=user, table=table, type_name="number", name="Test number field", number_type="DECIMAL", number_decimal_places=9999, ) handler.create_field( user=user, table=table, type_name="number", name="Test number field", number_type="INTEGER", number_decimal_places=2, number_negative=True, ) number_field = NumberField.objects.all().first() assert number_field.name == "Test number field" assert number_field.order == 2 assert number_field.table == table assert number_field.number_type == "INTEGER" assert number_field.number_decimal_places == 2 assert number_field.number_negative handler.create_field( user=user, table=table, type_name="boolean", name="Test boolean field", random_other_field="WILL_BE_IGNORED", ) boolean_field = BooleanField.objects.all().first() assert boolean_field.name == "Test boolean field" assert boolean_field.order == 3 assert boolean_field.table == table assert Field.objects.all().count() == 3 assert TextField.objects.all().count() == 1 assert NumberField.objects.all().count() == 1 assert BooleanField.objects.all().count() == 1 field_limit = settings.MAX_FIELD_LIMIT settings.MAX_FIELD_LIMIT = 2 with pytest.raises(MaxFieldLimitExceeded): handler.create_field( user=user, table=table, type_name="text", name="Test text field", text_default="Some default", ) settings.MAX_FIELD_LIMIT = field_limit with pytest.raises(UserNotInGroup): handler.create_field(user=user_2, table=table, type_name="text") with pytest.raises(FieldTypeDoesNotExist): handler.create_field(user=user, table=table, type_name="UNKNOWN")
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_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_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 is False assert date_field_2.date_include_time is 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 is None assert row.datetime is 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 is 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 is True assert date_field_2.date_include_time is False model = table.get_model(attribute_names=True) rows = model.objects.all() assert rows[0].date is None assert rows[0].datetime is None assert rows[1].date == datetime(2020, 4, 1, tzinfo=timezone("UTC")) assert rows[1].datetime == date(2020, 4, 1) assert rows[2].date is 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_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 wide_test(data_fixture, storage_mock, expected, options): user = data_fixture.create_user() database = data_fixture.create_database_application(user=user) table = data_fixture.create_database_table(database=database, user=user) link_table = data_fixture.create_database_table(database=database, user=user) handler = FieldHandler() row_handler = RowHandler() all_possible_kwargs_per_type = construct_all_possible_field_kwargs( link_table) name_to_field_id = {} i = 0 for field_type_name, all_possible_kwargs in all_possible_kwargs_per_type.items( ): for kwargs in all_possible_kwargs: field = handler.create_field( user=user, table=table, type_name=field_type_name, order=i, **kwargs, ) i += 1 name_to_field_id[kwargs["name"]] = field.id grid_view = data_fixture.create_grid_view(table=table) row_handler = RowHandler() other_table_primary_text_field = data_fixture.create_text_field( table=link_table, name="text_field", primary=True) def add_linked_row(text): return row_handler.create_row( user=user, table=link_table, values={ other_table_primary_text_field.id: text, }, ) model = table.get_model() # A dictionary of field names to a tuple of (value to create the row model with, # the expected value of this value after being exported to csv) assert expected.keys() == name_to_field_id.keys(), ( "Please update the dictionary above with what your new field type should look " "like when serialized to csv. ") row_values = {} for field_type, val in expected.items(): if isinstance(val, tuple): val = val[0] if callable(val): val = val() if val is not None: row_values[f"field_{name_to_field_id[field_type]}"] = val # Make a blank row to test empty field conversion also. model.objects.create(**{}) row = model.objects.create(**row_values) linked_row_1 = add_linked_row("linked_row_1") linked_row_2 = add_linked_row("linked_row_2") linked_row_3 = add_linked_row(None) getattr(row, f"field_{name_to_field_id['link_row']}").add( linked_row_1.id, linked_row_2.id, linked_row_3.id) job, contents = run_export_job_with_mock_storage(table, grid_view, storage_mock, user, options) return contents