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 #2784 See merge request baserow/baserow!2523
This commit is contained in:
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
8
backend/src/baserow/contrib/database/api/rows/fields.py
Normal file
8
backend/src/baserow/contrib/database/api/rows/fields.py
Normal 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})
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.",
|
||||
|
|
Loading…
Add table
Reference in a new issue