from time import time from django.conf import settings from django.core.cache import caches from django.test import override_settings from django.urls import reverse import pytest from cachalot.settings import cachalot_settings from baserow.contrib.database.fields.handler import FieldHandler from baserow.contrib.database.rows.handler import RowHandler from baserow.contrib.database.views.handler import ViewHandler from baserow.test_utils.helpers import AnyInt @override_settings(CACHALOT_ENABLED=True) @pytest.mark.django_db(transaction=True) def test_cachalot_cache_count_for_filtered_views(data_fixture): user = data_fixture.create_user() table_a, _, link_field = data_fixture.create_two_linked_tables(user=user) cache = caches[settings.CACHALOT_CACHE] grid_view = data_fixture.create_grid_view(table=table_a) ViewHandler().create_filter( user=user, view=grid_view, field=link_field, type_name="link_row_has", value="1", ) queries = {} def get_mocked_query_cache_key(compiler): sql, _ = compiler.as_sql() sql_lower = sql.lower() if "count(*)" in sql_lower: key = "count" elif f"database_table_{table_a.id}" in sql_lower: key = "select_table" else: key = f"{time()}" queries[key] = sql_lower return key cachalot_settings.CACHALOT_QUERY_KEYGEN = get_mocked_query_cache_key cachalot_settings.CACHALOT_TABLE_KEYGEN = lambda _, table: table.rsplit("_", 1)[1] table_model = table_a.get_model() table_model.objects.create() queryset = ViewHandler().get_queryset(view=grid_view) def assert_cachalot_cache_queryset_count_of(expected_count): # count() should save the result of the query in the cache assert queryset.count() == expected_count # the count query has been cached inserted_cache_entry = cache.get("count") assert inserted_cache_entry is not None assert inserted_cache_entry[1][0] == expected_count assert_cachalot_cache_queryset_count_of(0) @override_settings(CACHALOT_ENABLED=True) @pytest.mark.django_db(transaction=True) def test_cachalot_cache_multiple_select_correctly(api_client, data_fixture): user, token = data_fixture.create_user_and_token() database = data_fixture.create_database_application(user=user) table = data_fixture.create_database_table(database=database) field_handler = FieldHandler() row_handler = RowHandler() grid_view = data_fixture.create_grid_view(table=table) field = field_handler.create_field( user=user, table=table, name="Multiple select", type_name="multiple_select", select_options=[ {"value": "Option 1", "color": "red"}, {"value": "Option 2", "color": "blue"}, {"value": "Option 3", "color": "orange"}, {"value": "Option 4", "color": "black"}, ], ) select_options = field.select_options.all() model = table.get_model() rows = row_handler.create_rows( user, table, rows_values=[ {f"field_{field.id}": [select_options[0].id, select_options[1].value]}, {f"field_{field.id}": [select_options[2].value, select_options[0].id]}, ], ) url = reverse("api:database:views:grid:list", kwargs={"view_id": grid_view.id}) response = api_client.get(url, **{"HTTP_AUTHORIZATION": f"JWT {token}"}) response_json = response.json() assert response_json["count"] == 2 assert response_json["results"][0][f"field_{field.id}"] == [ {"id": AnyInt(), "value": "Option 1", "color": "red"}, {"id": AnyInt(), "value": "Option 2", "color": "blue"}, ] row_handler.update_rows( user, table, [ {"id": rows[0].id, f"field_{field.id}": []}, ], model, [rows[0]], ) # Before #1772 this would raise an error because the cache would not be correctly # invalidated when updating a row so the old value would be returned. response = api_client.get(url, **{"HTTP_AUTHORIZATION": f"JWT {token}"}) response_json = response.json() assert response_json["count"] == 2 assert response_json["results"][0][f"field_{field.id}"] == []