1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-14 09:08:32 +00:00

Merge branch '2784-user_field_names-is-ignored-with-row-api-call' into 'develop'

Resolve "user_field_names value is ignored with row api call"

Closes 

See merge request 
This commit is contained in:
Przemyslaw Kukulski 2024-07-26 09:28:39 +00:00
commit dfbefedf53
9 changed files with 115 additions and 40 deletions
backend
src/baserow/contrib/database/api
tests/baserow/contrib/database/rows
changelog/entries/unreleased/bug
web-frontend
locales
modules/database
components/docs/sections
locales

View file

@ -0,0 +1,8 @@
from rest_framework import serializers
from baserow.contrib.database.api.utils import extract_user_field_names_from_params
class UserFieldNamesField(serializers.BooleanField):
def to_internal_value(self, data):
return extract_user_field_names_from_params({"user_field_names": data})

View file

@ -9,6 +9,7 @@ from rest_framework import serializers
from baserow.api.search.serializers import SearchQueryParamSerializer
from baserow.api.utils import get_serializer_class
from baserow.contrib.database.api.rows.fields import UserFieldNamesField
from baserow.contrib.database.fields.registries import field_type_registry
from baserow.contrib.database.rows.models import RowHistory
from baserow.contrib.database.rows.registries import row_metadata_registry
@ -241,8 +242,9 @@ def get_example_row_serializer_class(example_type="get", user_field_names=False)
optional_user_field_names_info = ""
if user_field_names:
optional_user_field_names_info = (
" If the GET parameter `user_field_names` is provided then the key will "
"instead be the actual name of the field."
" If the GET parameter user_field_names is provided and its value is "
"one of the following: `y`, `yes`, `true`, `t`, `on`, `1`, or empty, "
"then the key will instead be the actual name of the field."
)
for i, field_type in enumerate(field_types):
@ -334,6 +336,12 @@ def remap_serialized_row_to_user_field_names(
return new_row
class UserFieldNamesSerializer(serializers.Serializer):
user_field_names = UserFieldNamesField(
required=False, default=False, allow_null=True
)
class MoveRowQueryParamsSerializer(serializers.Serializer):
before_id = serializers.IntegerField(required=False)
@ -346,8 +354,9 @@ class BatchCreateRowsQueryParamsSerializer(serializers.Serializer):
before = serializers.IntegerField(required=False)
class ListRowsQueryParamsSerializer(SearchQueryParamSerializer):
user_field_names = serializers.BooleanField(required=False, default=False)
class ListRowsQueryParamsSerializer(
SearchQueryParamSerializer, UserFieldNamesSerializer
):
order_by = serializers.CharField(required=False)
include = serializers.CharField(required=False)
exclude = serializers.CharField(required=False)
@ -395,8 +404,9 @@ def get_example_batch_rows_serializer_class(example_type="get", user_field_names
return class_object
class GetRowAdjacentSerializer(SearchQueryParamSerializer, serializers.Serializer):
user_field_names = serializers.BooleanField(required=False, default=False)
class GetRowAdjacentSerializer(
SearchQueryParamSerializer, UserFieldNamesSerializer, serializers.Serializer
):
previous = serializers.BooleanField(required=False, default=False)
view_id = serializers.IntegerField(required=False)

View file

@ -47,7 +47,10 @@ from baserow.contrib.database.api.rows.serializers import GetRowAdjacentSerializ
from baserow.contrib.database.api.tables.errors import ERROR_TABLE_DOES_NOT_EXIST
from baserow.contrib.database.api.tokens.authentications import TokenAuthentication
from baserow.contrib.database.api.tokens.errors import ERROR_NO_PERMISSION_TO_TABLE
from baserow.contrib.database.api.utils import get_include_exclude_fields
from baserow.contrib.database.api.utils import (
extract_user_field_names_from_params,
get_include_exclude_fields,
)
from baserow.contrib.database.api.views.errors import (
ERROR_VIEW_DOES_NOT_EXIST,
ERROR_VIEW_FILTER_TYPE_DOES_NOT_EXIST,
@ -259,9 +262,11 @@ class RowsView(APIView):
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
description=(
"A flag query parameter which if provided the returned json "
"will use the user specified field names instead of internal "
"Baserow field names (field_123 etc). "
"A flag query parameter that, if provided with one of the "
"following values: `y`, `yes`, `true`, `t`, `on`, `1`, or an "
"empty value, will cause the returned JSON to use the "
"user-specified field names instead of the internal Baserow "
"field names (e.g., field_123)."
),
),
OpenApiParameter(
@ -418,9 +423,11 @@ class RowsView(APIView):
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
description=(
"A flag query parameter which if provided this endpoint will "
"expect and return the user specified field names instead of "
"internal Baserow field names (field_123 etc)."
"A flag query parameter that, if provided with one of the "
"following values: `y`, `yes`, `true`, `t`, `on`, `1`, or an "
"empty value, will cause this endpoint to expect and return the "
"user-specified field names instead of the internal Baserow "
"field names (e.g., field_123)."
),
),
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
@ -490,7 +497,8 @@ class RowsView(APIView):
context=table,
)
user_field_names = "user_field_names" in request.GET
user_field_names = extract_user_field_names_from_params(request.GET)
model = table.get_model()
validation_serializer = get_row_serializer_class(
@ -658,9 +666,11 @@ class RowView(APIView):
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
description=(
"A flag query parameter which if provided the returned json "
"will use the user specified field names instead of internal "
"Baserow field names (field_123 etc). "
"A flag query parameter that, if provided with one of the "
"following values: `y`, `yes`, `true`, `t`, `on`, `1`, or an "
"empty value, will cause the returned JSON to use the "
"user-specified field names instead of the internal Baserow "
"field names (e.g., field_123)."
),
),
],
@ -707,7 +717,7 @@ class RowView(APIView):
table = TableHandler().get_table(table_id)
TokenHandler().check_table_permissions(request, "read", table, False)
user_field_names = "user_field_names" in request.GET
user_field_names = extract_user_field_names_from_params(request.GET)
model = table.get_model()
row = RowHandler().get_row(request.user, table, row_id, model)
serializer_class = get_row_serializer_class(
@ -736,9 +746,11 @@ class RowView(APIView):
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
description=(
"A flag query parameter which if provided this endpoint will "
"expect and return the user specified field names instead of "
"internal Baserow field names (field_123 etc)."
"A flag query parameter that, if provided with one of the "
"following values: `y`, `yes`, `true`, `t`, `on`, `1`, or an "
"empty value, will cause this endpoint to expect and return the "
"user-specified field names instead of the internal Baserow "
"field names (e.g., field_123)."
),
),
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
@ -802,7 +814,7 @@ class RowView(APIView):
table = TableHandler().get_table(table_id)
TokenHandler().check_table_permissions(request, "update", table, False)
user_field_names = "user_field_names" in request.GET
user_field_names = extract_user_field_names_from_params(request.GET)
field_ids, field_names = None, None
if user_field_names:
@ -923,9 +935,11 @@ class RowMoveView(APIView):
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
description=(
"A flag query parameter which if provided the returned json "
"will use the user specified field names instead of internal "
"Baserow field names (field_123 etc). "
"A flag query parameter that, if provided with one of the "
"following values: `y`, `yes`, `true`, `t`, `on`, `1`, or an "
"empty value, will cause the returned JSON to use the "
"user-specified field names instead of the internal Baserow "
"field names (e.g., field_123)."
),
),
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
@ -967,7 +981,7 @@ class RowMoveView(APIView):
TokenHandler().check_table_permissions(request, "update", table, False)
user_field_names = "user_field_names" in request.GET
user_field_names = extract_user_field_names_from_params(request.GET)
model = table.get_model()
@ -1015,9 +1029,11 @@ class BatchRowsView(APIView):
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
description=(
"A flag query parameter which if provided this endpoint will "
"expect and return the user specified field names instead of "
"internal Baserow field names (field_123 etc)."
"A flag query parameter that, if provided with one of the "
"following values: `y`, `yes`, `true`, `t`, `on`, `1`, or an "
"empty value, will cause this endpoint to expect and return the "
"user-specified field names instead of the internal Baserow "
"field names (e.g., field_123)."
),
),
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
@ -1083,7 +1099,7 @@ class BatchRowsView(APIView):
TokenHandler().check_table_permissions(request, "create", table, False)
model = table.get_model()
user_field_names = "user_field_names" in request.GET
user_field_names = extract_user_field_names_from_params(request.GET)
before_id = query_params.get("before")
before_row = (
RowHandler().get_row(request.user, table, before_id, model)
@ -1130,9 +1146,11 @@ class BatchRowsView(APIView):
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
description=(
"A flag query parameter which if provided this endpoint will "
"expect and return the user specified field names instead of "
"internal Baserow field names (field_123 etc)."
"A flag query parameter that, if provided with one of the "
"following values: `y`, `yes`, `true`, `t`, `on`, `1`, or an "
"empty value, will cause this endpoint to expect and return the "
"user-specified field names instead of the internal Baserow "
"field names (e.g., field_123)."
),
),
CLIENT_SESSION_ID_SCHEMA_PARAMETER,
@ -1198,7 +1216,7 @@ class BatchRowsView(APIView):
TokenHandler().check_table_permissions(request, "update", table, False)
model = table.get_model()
user_field_names = "user_field_names" in request.GET
user_field_names = extract_user_field_names_from_params(request.GET)
row_validation_serializer = get_row_serializer_class(
model,
@ -1326,9 +1344,11 @@ class RowAdjacentView(APIView):
location=OpenApiParameter.QUERY,
type=OpenApiTypes.BOOL,
description=(
"A flag query parameter which if provided the returned json "
"will use the user specified field names instead of internal "
"Baserow field names (field_123 etc). "
"A flag query parameter that, if provided with one of the "
"following values: `y`, `yes`, `true`, `t`, `on`, `1`, or an "
"empty value, will cause the returned JSON to use the "
"user-specified field names instead of the internal Baserow "
"field names (e.g., field_123)."
),
),
OpenApiParameter(

View file

@ -1,3 +1,4 @@
from baserow.config.settings.utils import str_to_bool
from baserow.contrib.database.fields.models import Field
from baserow.contrib.database.fields.utils import get_field_id_from_field_key
from baserow.core.utils import split_comma_separated_string
@ -115,3 +116,20 @@ def extract_field_ids_from_string(value):
ids = [get_field_id_from_field_key(v, False) for v in value.split(",")]
return [_id for _id in ids if _id is not None]
def extract_user_field_names_from_params(query_params):
"""
Extracts the user_field_names parameter from the query_params and returns
boolean value
"""
value = query_params.get("user_field_names", False)
if value is False:
return False
if value is None or value == "":
return True
return str_to_bool(value)

View file

@ -11,6 +11,7 @@ from pyinstrument import Profiler
from baserow.contrib.database.api.utils import (
extract_field_ids_from_string,
extract_user_field_names_from_params,
get_include_exclude_fields,
)
from baserow.contrib.database.rows.exceptions import RowDoesNotExist
@ -39,6 +40,14 @@ def test_extract_field_ids_from_string():
assert extract_field_ids_from_string("is,1,one") == [1]
def test_extract_user_field_names_from_params():
assert extract_user_field_names_from_params({}) is False
assert extract_user_field_names_from_params({"user_field_names": None}) is True
assert extract_user_field_names_from_params({"user_field_names": ""}) is True
assert extract_user_field_names_from_params({"user_field_names": "true"}) is True
assert extract_user_field_names_from_params({"user_field_names": "false"}) is False
@pytest.mark.django_db
def test_get_include_exclude_fields(data_fixture):
table = data_fixture.create_database_table()

View file

@ -0,0 +1,7 @@
{
"type": "bug",
"message": "Properly handle user_field_names in api calls",
"issue_number": 2784,
"bullet_points": [],
"created_at": "2024-07-11"
}

View file

@ -408,7 +408,7 @@
"queryParameters": "Query parameters",
"pathParameters": "Path parameters",
"requestBodySchema": "Request body schema",
"userFieldNamesDescription": "When any value is provided for the `user_field_names` GET param then field names returned by this endpoint will be the actual names of the fields.\n\n If the `user_field_names` GET param is not provided, then all returned field names will be `field_` followed by the id of the field. For example `field_1` refers to the field with an id of `1`.",
"userFieldNamesDescription": "When the `user_field_names` GET parameter is provided and its value is one of the following: `y`, `yes`, `true`, `t`, `on`, `1`, or empty string, the field names returned by this endpoint will be the actual names of the fields.\n\nIf the `user_field_names` GET parameter is not provided, or if it does not match any of the above values, then all returned field names will be `field_` followed by the id of the field. For example `field_1` refers to the field with an id of `1`.",
"singleRow": "Single",
"batchRows": "Batch",
"fileUploads": "File uploads"

View file

@ -23,7 +23,10 @@
</h4>
<ul class="api-docs__parameters">
<APIDocsParameter name="user_field_names" :optional="true" type="any">
<MarkdownIt :content="$t('apiDocs.userFieldNamesDescription')" />
<MarkdownIt
class="api-docs__content"
:content="$t('apiDocs.userFieldNamesDescription')"
/>
</APIDocsParameter>
</ul>
</div>

View file

@ -141,7 +141,7 @@
"description": "To list rows in the *{name}* table a `GET` request has to be made to the *{name}* endpoint. The response is paginated and by default the first page is returned. The correct page can be fetched by providing the `page` and `size` query parameters.",
"page": "Defines which page of rows should be returned.",
"size": "Defines how many rows should be returned per page.",
"userFieldNames": "When any value is provided for the `user_field_names` GET param then field names returned by this endpoint will be the actual names of the fields.\n\n If the `user_field_names` GET param is not provided, then all returned field names will be `field_` followed by the id of the field. For example `field_1` refers to the field with an id of `1`.\n\n Additionally when `user_field_names` is set then the behaviour of the other GET parameters `order_by`, `include` and `exclude` changes. They instead expect comma separated lists of the actual field names instead.",
"userFieldNames": "When the `user_field_names` GET parameter is provided and its value is one of the following: `y`, `yes`, `true`, `t`, `on`, `1`, or empty string, the field names returned by this endpoint will be the actual names of the fields.\n\nIf the `user_field_names` GET parameter is not provided, or if it does not match any of the above values, then all returned field names will be `field_` followed by the id of the field. For example `field_1` refers to the field with an id of `1`.\n\n Additionally when `user_field_names` is set then the behaviour of the other GET parameters `order_by`, `include` and `exclude` changes. They instead expect comma separated lists of the actual field names instead.",
"search": "If provided only rows with data that matches the search query are going to be returned.",
"orderBy": "Optionally the rows can be ordered by fields separated by comma. By default or if prepended with a '+' a field is ordered in ascending (A-Z) order, but by prepending the field with a '-' it can be ordered descending (Z-A).\n\n #### With `user_field_names`:\n\n `order_by` should be a comma separated list of the field names to order by. For example if you provide the following GET parameter `order_by=My Field,-My Field 2` the rows will ordered by the field called `My Field` in ascending order. If some fields have the same value, that subset will be ordered by the field called `My Field 2` in descending order.\n\n Ensure fields with names starting with a `+` or `-` are explicitly prepended with another `+` or `-`. E.g `+-Name`.\n\n The name of fields containing commas must be surrounded by quotes: `\"Name ,\"`. If the field names contain quotes, then they must be escaped using the `\\` character. Eg: `Name \\\"`. \n\n#### Without `user_field_names`:\n\n `order_by` should be a comma separated list of `field_` followed by the id of the field to order by. For example if you provide the following GET parameter `order_by=field_1,-field_2` the rows will ordered by `field_1` in ascending order. If some fields have the same value, that subset will be ordered by `field_2` in descending order.",
"filters": "Rows can optionally be filtered using the same view filters that are available for the views. This parameter accepts a JSON serialized string containing the filter tree to apply to this view. The filter tree is a nested structure containing the filters that need to be applied. \n\n#### With `user_field_names`:\n\nAn example of a valid filter tree is the following: `{\"filter_type\": \"AND\", \"filters\": [{\"field\": \"Name\", \"type\": \"equal\", \"value\": \"test\"}]}`.\n\n#### Without `user_field_names`:\n\nFor example, if you optionally provide the following GET parameter: `{\"filter_type\": \"AND\", \"filters\": [{\"field\": 1, \"type\": \"equal\", \"value\": \"test\"}]}`\n\nPlease note that if this parameter is provided, all other `filter__{field}__{filter}` will be ignored, as well as the filter_type parameter.",