mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-10 15:47:32 +00:00
Merge branch '312-having-many-link-row-fields-causes-the-tool-to-slow-down' into 'develop'
Resolve "Having many link row fields causes the backend to slow down" Closes #312 See merge request bramw/baserow!174
This commit is contained in:
commit
df2c265d9f
6 changed files with 66 additions and 28 deletions
backend
src/baserow/contrib/database
tests/baserow/contrib/database/field
web-frontend/modules/database
|
@ -1,5 +1,4 @@
|
|||
from django.utils.functional import lazy
|
||||
from django.db import models
|
||||
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
|
@ -66,21 +65,6 @@ class UpdateFieldSerializer(serializers.ModelSerializer):
|
|||
}
|
||||
|
||||
|
||||
class LinkRowListSerializer(serializers.ListSerializer):
|
||||
def to_representation(self, data):
|
||||
"""
|
||||
Data that is fetched is always from another Table model and when fetching
|
||||
that data we always need to respect the field enhancements. Otherwise it
|
||||
could for example fail when we want to fetch the related select options that
|
||||
could be in another database and table.
|
||||
"""
|
||||
|
||||
if isinstance(data, models.Manager):
|
||||
data = data.all().enhance_by_fields()
|
||||
|
||||
return super().to_representation(data)
|
||||
|
||||
|
||||
class LinkRowValueSerializer(serializers.Serializer):
|
||||
id = serializers.IntegerField(help_text='The unique identifier of the row in the '
|
||||
'related table.')
|
||||
|
|
|
@ -17,8 +17,8 @@ from rest_framework import serializers
|
|||
from baserow.core.models import UserFile
|
||||
from baserow.core.user_files.exceptions import UserFileDoesNotExist
|
||||
from baserow.contrib.database.api.fields.serializers import (
|
||||
LinkRowListSerializer, LinkRowValueSerializer, FileFieldRequestSerializer,
|
||||
FileFieldResponseSerializer, SelectOptionSerializer
|
||||
LinkRowValueSerializer, FileFieldRequestSerializer, FileFieldResponseSerializer,
|
||||
SelectOptionSerializer
|
||||
)
|
||||
from baserow.contrib.database.api.fields.errors import (
|
||||
ERROR_LINK_ROW_TABLE_NOT_IN_SAME_DATABASE, ERROR_LINK_ROW_TABLE_NOT_PROVIDED,
|
||||
|
@ -314,10 +314,35 @@ class LinkRowFieldType(FieldType):
|
|||
|
||||
def enhance_queryset(self, queryset, field, name):
|
||||
"""
|
||||
Makes sure that the related rows are prefetched by Django.
|
||||
Makes sure that the related rows are prefetched by Django. We also want to
|
||||
enhance the primary field of the related queryset. If for example the primary
|
||||
field is a single select field then the dropdown options need to be
|
||||
prefetched in order to prevent many queries.
|
||||
"""
|
||||
|
||||
return queryset.prefetch_related(name)
|
||||
remote_model = queryset.model._meta.get_field(name).remote_field.model
|
||||
related_queryset = remote_model.objects.all()
|
||||
|
||||
try:
|
||||
primary_field_object = next(
|
||||
object
|
||||
for object in remote_model._field_objects.values()
|
||||
if object['field'].primary
|
||||
)
|
||||
related_queryset = primary_field_object['type'].enhance_queryset(
|
||||
related_queryset,
|
||||
primary_field_object['field'],
|
||||
primary_field_object['name']
|
||||
)
|
||||
except StopIteration:
|
||||
# If the related model does not have a primary field then we also don't
|
||||
# need to enhance the queryset.
|
||||
pass
|
||||
|
||||
return queryset.prefetch_related(models.Prefetch(
|
||||
name,
|
||||
queryset=related_queryset
|
||||
))
|
||||
|
||||
def get_serializer_field(self, instance, **kwargs):
|
||||
"""
|
||||
|
@ -331,9 +356,9 @@ class LinkRowFieldType(FieldType):
|
|||
def get_response_serializer_field(self, instance, **kwargs):
|
||||
"""
|
||||
If a model has already been generated it will be added as a property to the
|
||||
instance. If that case then we can extract the primary field from the model and
|
||||
we can pass the name along to the LinkRowValueSerializer. It will be used to
|
||||
include the primary field's value in the response as a string.
|
||||
instance. If that is the case then we can extract the primary field from the
|
||||
model and we can pass the name along to the LinkRowValueSerializer. It will
|
||||
be used to include the primary field's value in the response as a string.
|
||||
"""
|
||||
|
||||
primary_field_name = None
|
||||
|
@ -348,7 +373,7 @@ class LinkRowFieldType(FieldType):
|
|||
if primary_field:
|
||||
primary_field_name = primary_field['name']
|
||||
|
||||
return LinkRowListSerializer(child=LinkRowValueSerializer(
|
||||
return serializers.ListSerializer(child=LinkRowValueSerializer(
|
||||
value_field_name=primary_field_name, required=False, **kwargs
|
||||
))
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@ from baserow.contrib.database.fields.models import SelectOption, SingleSelectFie
|
|||
from baserow.contrib.database.fields.field_types import SingleSelectFieldType
|
||||
from baserow.contrib.database.rows.handler import RowHandler
|
||||
from baserow.contrib.database.views.handler import ViewHandler
|
||||
from baserow.contrib.database.api.rows.serializers import (
|
||||
get_row_serializer_class, RowSerializer
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
@ -502,7 +505,11 @@ def test_single_select_field_type_get_order(data_fixture):
|
|||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_primary_single_select_field_with_link_row_field(api_client, data_fixture):
|
||||
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.
|
||||
"""
|
||||
|
@ -528,7 +535,8 @@ def test_primary_single_select_field_with_link_row_field(api_client, data_fixtur
|
|||
type_name='single_select',
|
||||
select_options=[
|
||||
{'value': 'Option 1', 'color': 'red'},
|
||||
{'value': 'Option 2', 'color': 'blue'}
|
||||
{'value': 'Option 2', 'color': 'blue'},
|
||||
{'value': 'Option 3', 'color': 'orange'}
|
||||
],
|
||||
primary=True
|
||||
)
|
||||
|
@ -548,6 +556,10 @@ def test_primary_single_select_field_with_link_row_field(api_client, data_fixtur
|
|||
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]}
|
||||
|
@ -556,6 +568,18 @@ def test_primary_single_select_field_with_link_row_field(api_client, data_fixtur
|
|||
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}),
|
||||
|
@ -576,6 +600,10 @@ def test_primary_single_select_field_with_link_row_field(api_client, data_fixtur
|
|||
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'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
* Made it possible to configure SMTP settings via environment variables.
|
||||
* Added field name to the public REST API docs.
|
||||
* Made the public REST API docs compatible with smaller screens.
|
||||
* Reduced the amount of queries when using the link row field.
|
||||
|
||||
## Released (2021-02-04)
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ export class FieldType extends Registerable {
|
|||
* converted to string.
|
||||
*/
|
||||
toHumanReadableString(field, value) {
|
||||
return value
|
||||
return value || ''
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,7 +32,7 @@ export default {
|
|||
.toHumanReadableString(primary, row[`field_${primary.id}`])
|
||||
newValue.push({
|
||||
id: row.id,
|
||||
value: rowValue.toString(),
|
||||
value: rowValue,
|
||||
})
|
||||
this.$emit('update', newValue, value)
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue