mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-17 18:32:35 +00:00
Merge branch '1836-table-collection-element-02-models' into 'develop'
[backend] Add table element See merge request baserow/baserow!1699
This commit is contained in:
commit
afd26c5a82
14 changed files with 551 additions and 16 deletions
backend
Makefile
src/baserow
contrib
core/services
test_utils/fixtures
tests/baserow/contrib/builder
|
@ -18,6 +18,8 @@ lint-python: lint
|
|||
format:
|
||||
black . ../premium/backend ../enterprise/backend --extend-exclude='/generated/' || exit;
|
||||
|
||||
fix: sort format
|
||||
|
||||
sort:
|
||||
isort --skip generated src tests ../premium/backend ../enterprise/backend || exit;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from drf_spectacular.types import OpenApiTypes
|
|||
from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
|
||||
from baserow.contrib.builder.elements.models import Element
|
||||
from baserow.contrib.builder.elements.models import CollectionElementField, Element
|
||||
from baserow.contrib.builder.elements.registries import element_type_registry
|
||||
from baserow.core.formula.serializers import FormulaSerializerField
|
||||
|
||||
|
@ -112,3 +112,15 @@ class MoveElementSerializer(serializers.Serializer):
|
|||
class PageParameterValueSerializer(serializers.Serializer):
|
||||
name = serializers.CharField()
|
||||
value = FormulaSerializerField(allow_blank=True)
|
||||
|
||||
|
||||
class CollectionElementFieldSerializer(serializers.ModelSerializer):
|
||||
value = FormulaSerializerField(allow_blank=True)
|
||||
|
||||
class Meta:
|
||||
model = CollectionElementField
|
||||
fields = (
|
||||
"id",
|
||||
"name",
|
||||
"value",
|
||||
)
|
||||
|
|
|
@ -287,6 +287,9 @@ class BuilderApplicationType(ApplicationType):
|
|||
if "builder_page_elements" not in id_mapping:
|
||||
id_mapping["builder_page_elements"] = {}
|
||||
|
||||
if "builder_data_sources" not in id_mapping:
|
||||
id_mapping["builder_data_sources"] = {}
|
||||
|
||||
if "workspace_id" not in id_mapping and builder.workspace is not None:
|
||||
id_mapping["workspace_id"] = builder.workspace.id
|
||||
|
||||
|
@ -309,17 +312,6 @@ class BuilderApplicationType(ApplicationType):
|
|||
imported_pages.append(page_instance)
|
||||
progress.increment(state=IMPORT_SERIALIZED_IMPORTING)
|
||||
|
||||
# Then we create all the element instances.
|
||||
for serialized_page in serialized_pages:
|
||||
for serialized_element in serialized_page["elements"]:
|
||||
self.import_element(
|
||||
serialized_element,
|
||||
serialized_page,
|
||||
id_mapping,
|
||||
)
|
||||
|
||||
progress.increment(state=IMPORT_SERIALIZED_IMPORTING)
|
||||
|
||||
# Then we create all the datasource instances.
|
||||
for serialized_page in serialized_pages:
|
||||
for serialized_data_source in serialized_page["data_sources"]:
|
||||
|
@ -349,10 +341,25 @@ class BuilderApplicationType(ApplicationType):
|
|||
name=serialized_data_source["name"],
|
||||
)
|
||||
|
||||
id_mapping["builder_data_sources"][
|
||||
serialized_data_source["id"]
|
||||
] = data_source.id
|
||||
|
||||
serialized_page["_data_source_objects"].append(data_source)
|
||||
|
||||
progress.increment(state=IMPORT_SERIALIZED_IMPORTING)
|
||||
|
||||
# Then we create all the element instances.
|
||||
for serialized_page in serialized_pages:
|
||||
for serialized_element in serialized_page["elements"]:
|
||||
self.import_element(
|
||||
serialized_element,
|
||||
serialized_page,
|
||||
id_mapping,
|
||||
)
|
||||
|
||||
progress.increment(state=IMPORT_SERIALIZED_IMPORTING)
|
||||
|
||||
return imported_pages
|
||||
|
||||
def import_element(
|
||||
|
|
|
@ -142,6 +142,7 @@ class BuilderConfig(AppConfig):
|
|||
InputTextElementType,
|
||||
LinkElementType,
|
||||
ParagraphElementType,
|
||||
TableElementType,
|
||||
)
|
||||
from .elements.registries import element_type_registry
|
||||
|
||||
|
@ -152,6 +153,7 @@ class BuilderConfig(AppConfig):
|
|||
element_type_registry.register(InputTextElementType())
|
||||
element_type_registry.register(ColumnElementType())
|
||||
element_type_registry.register(ButtonElementType())
|
||||
element_type_registry.register(TableElementType())
|
||||
|
||||
from .domains.domain_types import CustomDomainType, SubDomainType
|
||||
from .domains.registries import domain_type_registry
|
||||
|
|
|
@ -8,10 +8,12 @@ from django.db.models.functions import Cast
|
|||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from baserow.contrib.builder.data_sources.handler import DataSourceHandler
|
||||
from baserow.contrib.builder.elements.handler import ElementHandler
|
||||
from baserow.contrib.builder.elements.models import (
|
||||
WIDTHS,
|
||||
ButtonElement,
|
||||
CollectionElementField,
|
||||
ColumnElement,
|
||||
ContainerElement,
|
||||
Element,
|
||||
|
@ -21,6 +23,7 @@ from baserow.contrib.builder.elements.models import (
|
|||
InputTextElement,
|
||||
LinkElement,
|
||||
ParagraphElement,
|
||||
TableElement,
|
||||
VerticalAlignments,
|
||||
)
|
||||
from baserow.contrib.builder.elements.registries import ElementType
|
||||
|
@ -101,6 +104,110 @@ class ContainerElementType(ElementType, ABC):
|
|||
pass
|
||||
|
||||
|
||||
class CollectionElementType(ElementType, ABC):
|
||||
allowed_fields = ["data_source", "data_source_id"]
|
||||
serializer_field_names = ["data_source_id", "fields"]
|
||||
|
||||
class SerializedDict(ElementDict):
|
||||
data_source_id: int
|
||||
fields: List[Dict]
|
||||
|
||||
@property
|
||||
def serializer_field_overrides(self):
|
||||
from baserow.contrib.builder.api.elements.serializers import (
|
||||
CollectionElementFieldSerializer,
|
||||
)
|
||||
|
||||
overrides = {
|
||||
"data_source_id": serializers.IntegerField(
|
||||
allow_null=True,
|
||||
default=None,
|
||||
help_text=TableElement._meta.get_field("data_source").help_text,
|
||||
required=False,
|
||||
),
|
||||
"fields": CollectionElementFieldSerializer(
|
||||
many=True, required=False, help_text="The fields to show in the table."
|
||||
),
|
||||
}
|
||||
|
||||
return overrides
|
||||
|
||||
def prepare_value_for_db(
|
||||
self, values: Dict, instance: Optional[LinkElement] = None
|
||||
):
|
||||
if "data_source_id" in values:
|
||||
data_source_id = values.pop("data_source_id")
|
||||
if data_source_id is not None:
|
||||
data_source = DataSourceHandler().get_data_source(data_source_id)
|
||||
if (
|
||||
not data_source.service
|
||||
or not data_source.service.specific.get_type().returns_list
|
||||
):
|
||||
raise ValidationError(
|
||||
f"The data source with ID {data_source_id} doesn't return a "
|
||||
"list."
|
||||
)
|
||||
values["data_source"] = data_source
|
||||
else:
|
||||
values["data_source"] = None
|
||||
|
||||
return super().prepare_value_for_db(values, instance)
|
||||
|
||||
def after_create(self, instance, values):
|
||||
if "fields" in values:
|
||||
created_fields = CollectionElementField.objects.bulk_create(
|
||||
[
|
||||
CollectionElementField(**field, order=index)
|
||||
for index, field in enumerate(values["fields"])
|
||||
]
|
||||
)
|
||||
instance.fields.add(*created_fields)
|
||||
|
||||
def after_update(self, instance, values):
|
||||
if "fields" in values:
|
||||
# Remove previous fields
|
||||
instance.fields.clear()
|
||||
|
||||
self.after_create(instance, values)
|
||||
|
||||
def before_delete(self, instance):
|
||||
instance.fields.all().delete()
|
||||
|
||||
def get_property_for_serialization(self, element: Element, prop_name: str):
|
||||
"""
|
||||
You can customize the behavior of the serialization of a property with this
|
||||
hook.
|
||||
"""
|
||||
|
||||
if prop_name == "fields":
|
||||
return [{"name": f.name, "value": f.value} for f in element.fields.all()]
|
||||
|
||||
return super().get_property_for_serialization(element, prop_name)
|
||||
|
||||
def import_serialized(self, page, serialized_values, id_mapping):
|
||||
serialized_copy = serialized_values.copy()
|
||||
if serialized_copy["data_source_id"]:
|
||||
serialized_copy["data_source_id"] = id_mapping["builder_data_sources"][
|
||||
serialized_copy["data_source_id"]
|
||||
]
|
||||
|
||||
fields = serialized_copy.pop("fields", [])
|
||||
|
||||
instance = super().import_serialized(page, serialized_copy, id_mapping)
|
||||
|
||||
# Create fields
|
||||
created_fields = CollectionElementField.objects.bulk_create(
|
||||
[
|
||||
CollectionElementField(**field, order=index)
|
||||
for index, field in enumerate(fields)
|
||||
]
|
||||
)
|
||||
|
||||
instance.fields.add(*created_fields)
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class ColumnElementType(ContainerElementType):
|
||||
"""
|
||||
A column element is a container element that can be used to display other elements
|
||||
|
@ -290,7 +397,7 @@ class LinkElementType(ElementType):
|
|||
class SerializedDict(ElementDict):
|
||||
value: BaserowFormula
|
||||
navigation_type: str
|
||||
navigate_to_page_id: Page
|
||||
navigate_to_page_id: int
|
||||
page_parameters: List
|
||||
navigate_to_url: BaserowFormula
|
||||
variant: str
|
||||
|
@ -586,3 +693,11 @@ class ButtonElementType(ElementType):
|
|||
|
||||
def get_sample_params(self) -> Dict[str, Any]:
|
||||
return {"value": "Some value"}
|
||||
|
||||
|
||||
class TableElementType(CollectionElementType):
|
||||
type = "table"
|
||||
model_class = TableElement
|
||||
|
||||
def get_sample_params(self) -> Dict[str, Any]:
|
||||
return {"data_source_id": None}
|
||||
|
|
|
@ -150,6 +150,8 @@ class ElementHandler:
|
|||
element = model_class(page=page, order=order, **allowed_values)
|
||||
element.save()
|
||||
|
||||
element_type.after_create(element, kwargs)
|
||||
|
||||
return element
|
||||
|
||||
def delete_element(self, element: Element):
|
||||
|
@ -159,6 +161,8 @@ class ElementHandler:
|
|||
:param element: The to-be-deleted element.
|
||||
"""
|
||||
|
||||
element.get_type().before_delete(element)
|
||||
|
||||
element.delete()
|
||||
|
||||
def update_element(self, element: ElementForUpdate, **kwargs) -> Element:
|
||||
|
@ -184,6 +188,8 @@ class ElementHandler:
|
|||
|
||||
element.save()
|
||||
|
||||
element.get_type().after_update(element, kwargs)
|
||||
|
||||
return element
|
||||
|
||||
def move_element(
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Optional
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import QuerySet
|
||||
from django.db.models import SET_NULL, QuerySet
|
||||
|
||||
from baserow.contrib.builder.pages.models import Page
|
||||
from baserow.core.formula.field import FormulaField
|
||||
|
@ -414,3 +414,42 @@ class ButtonElement(Element):
|
|||
max_length=10,
|
||||
default=HorizontalAlignments.LEFT,
|
||||
)
|
||||
|
||||
|
||||
class CollectionElementField(models.Model):
|
||||
"""
|
||||
A field of a Collection element
|
||||
"""
|
||||
|
||||
order = models.PositiveIntegerField()
|
||||
name = models.CharField(
|
||||
max_length=225,
|
||||
help_text="The name of the field.",
|
||||
)
|
||||
value = FormulaField(default="", help_text="The value of the field.")
|
||||
|
||||
class Meta:
|
||||
ordering = ("order", "id")
|
||||
|
||||
|
||||
class CollectionElement(Element):
|
||||
data_source = models.ForeignKey(
|
||||
"builder.DataSource",
|
||||
null=True,
|
||||
on_delete=SET_NULL,
|
||||
help_text="The data source we want to show in the element for. "
|
||||
"Only data_sources that return list are allowed.",
|
||||
)
|
||||
|
||||
fields = models.ManyToManyField(
|
||||
CollectionElementField, help_text="Fields of the collection element."
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class TableElement(CollectionElement):
|
||||
"""
|
||||
A table element
|
||||
"""
|
||||
|
|
|
@ -57,6 +57,31 @@ class ElementType(
|
|||
|
||||
return values
|
||||
|
||||
def after_create(self, instance: ElementSubClass, values: Dict):
|
||||
"""
|
||||
This hook is called right after the element has been created.
|
||||
|
||||
:param instance: The created element instance.
|
||||
:param values: The values that were passed when creating the field
|
||||
instance.
|
||||
"""
|
||||
|
||||
def after_update(self, instance: ElementSubClass, values: Dict):
|
||||
"""
|
||||
This hook is called right after the element has been updated.
|
||||
|
||||
:param instance: The updated element instance.
|
||||
:param values: The values that were passed when creating the field
|
||||
instance.
|
||||
"""
|
||||
|
||||
def before_delete(self, instance: ElementSubClass):
|
||||
"""
|
||||
This hook is called just before the element will be deleted.
|
||||
|
||||
:param instance: The to be deleted element instance.
|
||||
"""
|
||||
|
||||
def get_property_for_serialization(self, element: Element, prop_name: str):
|
||||
"""
|
||||
You can customize the behavior of the serialization of a property with this
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
# Generated by Django 3.2.21 on 2023-09-22 12:04
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import baserow.core.formula.field
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("builder", "0021_alter_domain_content_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="CollectionElementField",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("order", models.PositiveIntegerField()),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
help_text="The name of the field.", max_length=225
|
||||
),
|
||||
),
|
||||
(
|
||||
"value",
|
||||
baserow.core.formula.field.FormulaField(
|
||||
default="", help_text="The value of the field."
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ("order", "id"),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="TableElement",
|
||||
fields=[
|
||||
(
|
||||
"element_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="builder.element",
|
||||
),
|
||||
),
|
||||
(
|
||||
"data_source",
|
||||
models.ForeignKey(
|
||||
help_text="The data source we want to show in the element for. Only data_sources that return list are allowed.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="builder.datasource",
|
||||
),
|
||||
),
|
||||
(
|
||||
"fields",
|
||||
models.ManyToManyField(
|
||||
help_text="Fields of the collection element.",
|
||||
to="builder.CollectionElementField",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("builder.element",),
|
||||
),
|
||||
]
|
|
@ -52,6 +52,7 @@ class LocalBaserowListRowsUserServiceType(
|
|||
type = "local_baserow_list_rows"
|
||||
model_class = LocalBaserowListRows
|
||||
max_result_limit = 200
|
||||
returns_list = True
|
||||
|
||||
class SerializedDict(ServiceDict):
|
||||
table_id: int
|
||||
|
|
|
@ -41,6 +41,9 @@ class ServiceType(
|
|||
# unless instructed otherwise by a user.
|
||||
default_result_limit = max_result_limit
|
||||
|
||||
# Does this service return a list of record?
|
||||
returns_list = False
|
||||
|
||||
def prepare_values(
|
||||
self, values: Dict[str, Any], user: AbstractUser
|
||||
) -> Dict[str, Any]:
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
from copy import deepcopy
|
||||
|
||||
from baserow.contrib.builder.elements.models import (
|
||||
CollectionElementField,
|
||||
ColumnElement,
|
||||
HeadingElement,
|
||||
ImageElement,
|
||||
LinkElement,
|
||||
ParagraphElement,
|
||||
TableElement,
|
||||
)
|
||||
|
||||
|
||||
|
@ -28,6 +32,36 @@ class ElementFixtures:
|
|||
element = self.create_builder_element(LinkElement, user, page, **kwargs)
|
||||
return element
|
||||
|
||||
def create_builder_table_element(self, user=None, page=None, **kwargs):
|
||||
fields = kwargs.pop(
|
||||
"fields",
|
||||
deepcopy(
|
||||
[
|
||||
{"name": "Field 1", "value": "get('test1')"},
|
||||
{"name": "Field 2", "value": "get('test2')"},
|
||||
{"name": "Field 3", "value": "get('test3')"},
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
if "data_source" not in kwargs:
|
||||
kwargs[
|
||||
"data_source"
|
||||
] = self.create_builder_local_baserow_list_rows_data_source(page=page)
|
||||
|
||||
element = self.create_builder_element(TableElement, user, page, **kwargs)
|
||||
|
||||
if fields:
|
||||
created_fields = CollectionElementField.objects.bulk_create(
|
||||
[
|
||||
CollectionElementField(**field, order=index)
|
||||
for index, field in enumerate(fields)
|
||||
]
|
||||
)
|
||||
element.fields.add(*created_fields)
|
||||
|
||||
return element
|
||||
|
||||
def create_builder_element(self, model_class, user=None, page=None, **kwargs):
|
||||
if user is None:
|
||||
user = self.create_user()
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
import pytest
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from baserow.contrib.builder.elements.models import CollectionElementField, Element
|
||||
from baserow.contrib.builder.elements.registries import element_type_registry
|
||||
from baserow.contrib.builder.elements.service import ElementService
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_table_element_without_fields(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
page = data_fixture.create_builder_page(user=user)
|
||||
data_source1 = data_fixture.create_builder_local_baserow_list_rows_data_source(
|
||||
page=page
|
||||
)
|
||||
|
||||
ElementService().create_element(
|
||||
user,
|
||||
element_type_registry.get("table"),
|
||||
page=page,
|
||||
data_source_id=data_source1.id,
|
||||
fields=[],
|
||||
)
|
||||
|
||||
created_element = Element.objects.last().specific
|
||||
|
||||
assert created_element.data_source.id == data_source1.id
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_table_element_with_fields(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
page = data_fixture.create_builder_page(user=user)
|
||||
data_source1 = data_fixture.create_builder_local_baserow_list_rows_data_source(
|
||||
page=page
|
||||
)
|
||||
|
||||
ElementService().create_element(
|
||||
user,
|
||||
element_type_registry.get("table"),
|
||||
page=page,
|
||||
data_source_id=data_source1.id,
|
||||
fields=[
|
||||
{"name": "Field 1", "value": "get('test')"},
|
||||
{"name": "Field 2", "value": "get('test')"},
|
||||
],
|
||||
)
|
||||
|
||||
created_element = Element.objects.last().specific
|
||||
|
||||
assert created_element.data_source.id == data_source1.id
|
||||
|
||||
fields = list(created_element.fields.all())
|
||||
|
||||
assert len(fields) == 2
|
||||
|
||||
fields[0].name == "Field 1"
|
||||
fields[1].name == "Field 2"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_table_element_with_non_collection_data_source(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
page = data_fixture.create_builder_page(user=user)
|
||||
data_source1 = data_fixture.create_builder_local_baserow_get_row_data_source(
|
||||
page=page
|
||||
)
|
||||
data_source2 = data_fixture.create_builder_data_source(page=page)
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
ElementService().create_element(
|
||||
user,
|
||||
element_type_registry.get("table"),
|
||||
page=page,
|
||||
data_source_id=data_source1.id,
|
||||
fields=[],
|
||||
)
|
||||
|
||||
assert data_source2.service is None
|
||||
with pytest.raises(ValidationError):
|
||||
ElementService().create_element(
|
||||
user,
|
||||
element_type_registry.get("table"),
|
||||
page=page,
|
||||
data_source_id=data_source2.id,
|
||||
fields=[],
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_table_element_without_fields(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
page = data_fixture.create_builder_page(user=user)
|
||||
data_source1 = data_fixture.create_builder_local_baserow_list_rows_data_source(
|
||||
page=page
|
||||
)
|
||||
table_element = data_fixture.create_builder_table_element(page=page)
|
||||
|
||||
ElementService().update_element(
|
||||
user,
|
||||
table_element,
|
||||
data_source_id=data_source1.id,
|
||||
)
|
||||
|
||||
table_element.refresh_from_db()
|
||||
|
||||
assert table_element.data_source.id == data_source1.id
|
||||
|
||||
fields = list(table_element.fields.all())
|
||||
|
||||
assert len(fields) == 3
|
||||
|
||||
fields[0].name == "Field 1"
|
||||
fields[1].name == "Field 2"
|
||||
fields[2].name == "Field 3"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_table_element_without_bad_data_source_type(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
page = data_fixture.create_builder_page(user=user)
|
||||
data_source1 = data_fixture.create_builder_local_baserow_get_row_data_source(
|
||||
page=page
|
||||
)
|
||||
table_element = data_fixture.create_builder_table_element(page=page)
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
ElementService().update_element(
|
||||
user,
|
||||
table_element,
|
||||
data_source_id=data_source1.id,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_table_element_with_fields(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
page = data_fixture.create_builder_page(user=user)
|
||||
table_element = data_fixture.create_builder_table_element(page=page)
|
||||
|
||||
ElementService().update_element(
|
||||
user,
|
||||
table_element,
|
||||
fields=[
|
||||
{"name": "New field 1", "value": "get('test')"},
|
||||
{"name": "New field 2", "value": "get('test')"},
|
||||
],
|
||||
)
|
||||
|
||||
table_element.refresh_from_db()
|
||||
|
||||
fields = list(table_element.fields.all())
|
||||
|
||||
assert len(fields) == 2
|
||||
|
||||
fields[0].name == "New field 1"
|
||||
fields[1].name == "New field 2"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_delete_table_element_remove_fields(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
page = data_fixture.create_builder_page(user=user)
|
||||
|
||||
table_element = data_fixture.create_builder_table_element(page=page)
|
||||
|
||||
assert CollectionElementField.objects.count() == 3
|
||||
|
||||
ElementService().delete_element(user, table_element)
|
||||
|
||||
assert CollectionElementField.objects.count() == 0
|
|
@ -6,6 +6,7 @@ from baserow.contrib.builder.elements.models import (
|
|||
Element,
|
||||
HeadingElement,
|
||||
ParagraphElement,
|
||||
TableElement,
|
||||
)
|
||||
from baserow.contrib.builder.elements.registries import element_type_registry
|
||||
from baserow.contrib.builder.models import Builder
|
||||
|
@ -61,11 +62,15 @@ def test_builder_application_export(data_fixture):
|
|||
page=page2, user=user, name="source 3", integration=integration
|
||||
)
|
||||
|
||||
element4 = data_fixture.create_builder_table_element(
|
||||
page=page2, data_source=datasource3
|
||||
)
|
||||
|
||||
serialized = BuilderApplicationType().export_serialized(
|
||||
builder, ImportExportConfig(include_permission_data=True)
|
||||
)
|
||||
|
||||
assert serialized == {
|
||||
reference = {
|
||||
"pages": [
|
||||
{
|
||||
"id": page1.id,
|
||||
|
@ -182,6 +187,20 @@ def test_builder_application_export(data_fixture):
|
|||
"value": element3.value,
|
||||
"level": element3.level,
|
||||
},
|
||||
{
|
||||
"id": element4.id,
|
||||
"type": "table",
|
||||
"order": str(element4.order),
|
||||
"parent_element_id": None,
|
||||
"place_in_container": None,
|
||||
"style_padding_top": 10,
|
||||
"style_padding_bottom": 10,
|
||||
"data_source_id": element4.data_source.id,
|
||||
"fields": [
|
||||
{"name": f.name, "value": f.value}
|
||||
for f in element4.fields.all()
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -210,6 +229,8 @@ def test_builder_application_export(data_fixture):
|
|||
"type": "builder",
|
||||
}
|
||||
|
||||
assert serialized == reference
|
||||
|
||||
|
||||
IMPORT_REFERENCE = {
|
||||
"pages": [
|
||||
|
@ -237,6 +258,18 @@ IMPORT_REFERENCE = {
|
|||
"order": 2,
|
||||
"value": "",
|
||||
},
|
||||
{
|
||||
"id": 1000,
|
||||
"type": "table",
|
||||
"parent_element_id": None,
|
||||
"place_in_container": None,
|
||||
"order": 2.5,
|
||||
"data_source_id": 5,
|
||||
"fields": [
|
||||
{"name": "F 1", "value": "get('test1')"},
|
||||
{"name": "F 2", "value": "get('test2')"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": 500,
|
||||
"type": "column",
|
||||
|
@ -375,7 +408,7 @@ def test_builder_application_import(data_fixture):
|
|||
|
||||
[page1, page2] = builder.page_set.all()
|
||||
|
||||
assert page1.element_set.count() == 4
|
||||
assert page1.element_set.count() == 5
|
||||
assert page2.element_set.count() == 1
|
||||
|
||||
assert page1.datasource_set.count() == 2
|
||||
|
@ -399,12 +432,16 @@ def test_builder_application_import(data_fixture):
|
|||
element1,
|
||||
element_inside_container,
|
||||
element2,
|
||||
table_element,
|
||||
container_element,
|
||||
] = specific_iterator(page1.element_set.all())
|
||||
|
||||
assert isinstance(element1, HeadingElement)
|
||||
assert isinstance(element2, ParagraphElement)
|
||||
assert isinstance(container_element, ColumnElement)
|
||||
assert isinstance(table_element, TableElement)
|
||||
|
||||
assert table_element.fields.count() == 2
|
||||
|
||||
assert element1.order == 1
|
||||
assert element1.level == 2
|
||||
|
|
Loading…
Add table
Reference in a new issue