mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-14 09:08:32 +00:00
Resolve "Created by field type"
This commit is contained in:
parent
df49e52393
commit
aa9a793dbe
37 changed files with 1368 additions and 42 deletions
backend
pytest.ini
src/baserow
contrib/database
test_utils
templates
tests/baserow/contrib
database
api
export
field
table
view
integrations/local_baserow
changelog/entries/unreleased/feature
premium/backend/tests/baserow_premium_tests/export
web-frontend
locales
modules/database
test/unit/database
|
@ -21,6 +21,7 @@ markers =
|
|||
field_boolean: All tests related to boolean field
|
||||
field_date: All tests related to date field
|
||||
field_created_on: All tests related to created on field
|
||||
field_created_by: All tests related to created by field
|
||||
field_last_modified: All tests related to last modified field
|
||||
field_email: All tests related to email field
|
||||
field_phone_number: All tests related to phone number field
|
||||
|
|
|
@ -108,6 +108,8 @@ class DatabaseApplicationType(ApplicationType):
|
|||
model = table.get_model(fields=fields, add_dependencies=False)
|
||||
serialized_rows = []
|
||||
row_queryset = model.objects.all()
|
||||
if table.created_by_column_added:
|
||||
row_queryset = row_queryset.select_related("created_by")
|
||||
if table.last_modified_by_column_added:
|
||||
row_queryset = row_queryset.select_related("last_modified_by")
|
||||
for row in row_queryset:
|
||||
|
@ -116,6 +118,7 @@ class DatabaseApplicationType(ApplicationType):
|
|||
order=str(row.order),
|
||||
created_on=row.created_on.isoformat(),
|
||||
updated_on=row.updated_on.isoformat(),
|
||||
created_by=getattr(row, "created_by", None),
|
||||
last_modified_by=getattr(row, "last_modified_by", None),
|
||||
)
|
||||
for field_object in model._field_objects.values():
|
||||
|
@ -474,16 +477,27 @@ class DatabaseApplicationType(ApplicationType):
|
|||
else:
|
||||
updated_on = timezone.now()
|
||||
|
||||
modified_by_email = serialized_row.get("last_modified_by", None)
|
||||
created_by_email = serialized_row.get("created_by", None)
|
||||
created_by = (
|
||||
user_email_mapping.get(created_by_email, None)
|
||||
if created_by_email
|
||||
else None
|
||||
)
|
||||
|
||||
last_modified_by_email = serialized_row.get("last_modified_by", None)
|
||||
last_modified_by = (
|
||||
user_email_mapping.get(last_modified_by_email, None)
|
||||
if last_modified_by_email
|
||||
else None
|
||||
)
|
||||
|
||||
row_instance = table_model(
|
||||
id=serialized_row["id"],
|
||||
order=serialized_row["order"],
|
||||
created_on=created_on,
|
||||
updated_on=updated_on,
|
||||
last_modified_by=user_email_mapping.get(modified_by_email, None)
|
||||
if modified_by_email
|
||||
else None,
|
||||
created_by=created_by,
|
||||
last_modified_by=last_modified_by,
|
||||
)
|
||||
|
||||
for serialized_field in serialized_table["fields"]:
|
||||
|
|
|
@ -200,6 +200,7 @@ class DatabaseConfig(AppConfig):
|
|||
from .fields.field_types import (
|
||||
BooleanFieldType,
|
||||
CountFieldType,
|
||||
CreatedByFieldType,
|
||||
CreatedOnFieldType,
|
||||
DateFieldType,
|
||||
EmailFieldType,
|
||||
|
@ -233,6 +234,7 @@ class DatabaseConfig(AppConfig):
|
|||
field_type_registry.register(LastModifiedFieldType())
|
||||
field_type_registry.register(LastModifiedByFieldType())
|
||||
field_type_registry.register(CreatedOnFieldType())
|
||||
field_type_registry.register(CreatedByFieldType())
|
||||
field_type_registry.register(LinkRowFieldType())
|
||||
field_type_registry.register(FileFieldType())
|
||||
field_type_registry.register(SingleSelectFieldType())
|
||||
|
|
|
@ -15,9 +15,12 @@ class DatabaseExportSerializedStructure:
|
|||
}
|
||||
|
||||
@staticmethod
|
||||
def row(id, order, created_on, updated_on, last_modified_by=None):
|
||||
def row(id, order, created_on, updated_on, created_by=None, last_modified_by=None):
|
||||
optional = {}
|
||||
|
||||
if created_by:
|
||||
optional["created_by"] = created_by.email
|
||||
|
||||
if last_modified_by:
|
||||
optional["last_modified_by"] = last_modified_by.email
|
||||
|
||||
|
|
|
@ -131,6 +131,11 @@ def construct_all_possible_field_kwargs(
|
|||
"name": "last_modified_by",
|
||||
}
|
||||
],
|
||||
"created_by": [
|
||||
{
|
||||
"name": "created_by",
|
||||
}
|
||||
],
|
||||
"link_row": [
|
||||
{"name": "link_row", "link_row_table": link_table},
|
||||
{
|
||||
|
|
|
@ -65,8 +65,6 @@ from baserow.contrib.database.api.fields.serializers import (
|
|||
)
|
||||
from baserow.contrib.database.db.functions import RandomUUID
|
||||
from baserow.contrib.database.export_serialized import DatabaseExportSerializedStructure
|
||||
from baserow.contrib.database.fields.field_cache import FieldCache
|
||||
from baserow.contrib.database.fields.fields import SyncedLastModifiedByForeignKeyField
|
||||
from baserow.contrib.database.formula import (
|
||||
BASEROW_FORMULA_TYPE_ALLOWED_FIELDS,
|
||||
BaserowExpression,
|
||||
|
@ -131,6 +129,7 @@ from .exceptions import (
|
|||
SelfReferencingLinkRowCannotHaveRelatedField,
|
||||
)
|
||||
from .expressions import extract_jsonb_array_values_to_single_string
|
||||
from .field_cache import FieldCache
|
||||
from .field_filters import (
|
||||
AnnotatedQ,
|
||||
contains_filter,
|
||||
|
@ -143,12 +142,14 @@ from .fields import (
|
|||
BaserowLastModifiedField,
|
||||
MultipleSelectManyToManyField,
|
||||
SingleSelectForeignKey,
|
||||
SyncedUserForeignKeyField,
|
||||
)
|
||||
from .handler import FieldHandler
|
||||
from .models import (
|
||||
AbstractSelectOption,
|
||||
BooleanField,
|
||||
CountField,
|
||||
CreatedByField,
|
||||
CreatedOnField,
|
||||
DateField,
|
||||
EmailField,
|
||||
|
@ -1246,7 +1247,7 @@ class LastModifiedByFieldType(ReadOnlyFieldType):
|
|||
kwargs["null"] = True
|
||||
kwargs["blank"] = True
|
||||
kwargs.update(self.model_field_kwargs)
|
||||
return SyncedLastModifiedByForeignKeyField(
|
||||
return SyncedUserForeignKeyField(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="+",
|
||||
|
@ -1268,7 +1269,9 @@ class LastModifiedByFieldType(ReadOnlyFieldType):
|
|||
|
||||
if not table.last_modified_by_column_added:
|
||||
table_to_update = TableHandler().get_table_for_update(table.id)
|
||||
TableHandler().create_last_modified_by_field(table_to_update)
|
||||
TableHandler().create_created_by_and_last_modified_by_fields(
|
||||
table_to_update
|
||||
)
|
||||
table.refresh_from_db()
|
||||
|
||||
def after_create(self, field, model, user, connection, before, field_kwargs):
|
||||
|
@ -1437,6 +1440,212 @@ class LastModifiedByFieldType(ReadOnlyFieldType):
|
|||
)
|
||||
|
||||
|
||||
class CreatedByFieldType(ReadOnlyFieldType):
|
||||
type = "created_by"
|
||||
model_class = CreatedByField
|
||||
can_be_in_form_view = False
|
||||
keep_data_on_duplication = True
|
||||
|
||||
source_field_name = "created_by"
|
||||
model_field_kwargs = {"sync_with_add": "created_by"}
|
||||
|
||||
def get_model_field(self, instance, **kwargs):
|
||||
kwargs["null"] = True
|
||||
kwargs["blank"] = True
|
||||
kwargs.update(self.model_field_kwargs)
|
||||
return SyncedUserForeignKeyField(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="+",
|
||||
related_query_name="+",
|
||||
db_constraint=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def get_serializer_field(self, instance, **kwargs):
|
||||
return CollaboratorSerializer(required=False, **kwargs)
|
||||
|
||||
def before_create(
|
||||
self, table, primary, allowed_field_values, order, user, field_kwargs
|
||||
):
|
||||
"""
|
||||
If created_by column is still not present on the table,
|
||||
we need to create it first.
|
||||
"""
|
||||
|
||||
if not table.created_by_column_added:
|
||||
table_to_update = TableHandler().get_table_for_update(table.id)
|
||||
TableHandler().create_created_by_and_last_modified_by_fields(
|
||||
table_to_update
|
||||
)
|
||||
table.refresh_from_db()
|
||||
|
||||
def after_create(self, field, model, user, connection, before, field_kwargs):
|
||||
"""
|
||||
Immediately after the field has been created, we need to populate the values
|
||||
with the already existing source_field_name column.
|
||||
"""
|
||||
|
||||
model.objects.all().update(
|
||||
**{f"{field.db_column}": models.F(self.source_field_name)}
|
||||
)
|
||||
|
||||
def after_update(
|
||||
self,
|
||||
from_field,
|
||||
to_field,
|
||||
from_model,
|
||||
to_model,
|
||||
user,
|
||||
connection,
|
||||
altered_column,
|
||||
before,
|
||||
to_field_kwargs,
|
||||
):
|
||||
"""
|
||||
If the field type has changed, we need to update the values from
|
||||
the source_field_name column.
|
||||
"""
|
||||
|
||||
if not isinstance(from_field, self.model_class):
|
||||
to_model.objects.all().update(
|
||||
**{f"{to_field.db_column}": models.F(self.source_field_name)}
|
||||
)
|
||||
|
||||
def enhance_queryset(self, queryset, field, name):
|
||||
return queryset.select_related(name)
|
||||
|
||||
def should_backup_field_data_for_same_type_update(
|
||||
self, old_field, new_field_attrs: Dict[str, Any]
|
||||
) -> bool:
|
||||
return False
|
||||
|
||||
def random_value(self, instance, fake, cache):
|
||||
return None
|
||||
|
||||
def get_export_serialized_value(
|
||||
self,
|
||||
row: "GeneratedTableModel",
|
||||
field_name: str,
|
||||
cache: Dict[str, Any],
|
||||
files_zip: Optional[ZipFile] = None,
|
||||
storage: Optional[Storage] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Exported value will be the user's email address.
|
||||
"""
|
||||
|
||||
user = self.get_internal_value_from_db(row, field_name)
|
||||
return user.email if user else None
|
||||
|
||||
def set_import_serialized_value(
|
||||
self,
|
||||
row: "GeneratedTableModel",
|
||||
field_name: str,
|
||||
value: Any,
|
||||
id_mapping: Dict[str, Any],
|
||||
cache: Dict[str, Any],
|
||||
files_zip: Optional[ZipFile] = None,
|
||||
storage: Optional[Storage] = None,
|
||||
):
|
||||
"""
|
||||
Importing will use the value from source_field_name column.
|
||||
"""
|
||||
|
||||
value = getattr(row, self.source_field_name)
|
||||
setattr(row, field_name, value)
|
||||
|
||||
def get_internal_value_from_db(
|
||||
self, row: "GeneratedTableModel", field_name: str
|
||||
) -> Any:
|
||||
return getattr(row, field_name)
|
||||
|
||||
def get_export_value(
|
||||
self, value: Any, field_object: "FieldObject", rich_value: bool = False
|
||||
) -> Any:
|
||||
"""
|
||||
Exported value will be the user's email address.
|
||||
"""
|
||||
|
||||
user = value
|
||||
return user.email if user else None
|
||||
|
||||
def get_order(
|
||||
self, field, field_name, order_direction
|
||||
) -> OptionallyAnnotatedOrderBy:
|
||||
"""
|
||||
If the user wants to sort the results they expect them to be ordered
|
||||
alphabetically based on the user's name.
|
||||
"""
|
||||
|
||||
name = f"{field_name}__first_name"
|
||||
order = collate_expression(F(name))
|
||||
|
||||
if order_direction == "ASC":
|
||||
order = order.asc(nulls_first=True)
|
||||
else:
|
||||
order = order.desc(nulls_last=True)
|
||||
return OptionallyAnnotatedOrderBy(order=order)
|
||||
|
||||
def get_value_for_filter(self, row: "GeneratedTableModel", field: Field) -> any:
|
||||
value = getattr(row, field.db_column)
|
||||
return value
|
||||
|
||||
def get_search_expression(self, field: Field, queryset: QuerySet) -> Expression:
|
||||
return Subquery(
|
||||
queryset.filter(pk=OuterRef("pk")).values(f"{field.db_column}__first_name")[
|
||||
:1
|
||||
]
|
||||
)
|
||||
|
||||
def is_searchable(self, field: Field) -> bool:
|
||||
return True
|
||||
|
||||
def contains_query(self, field_name, value, model_field, field):
|
||||
value = value.strip()
|
||||
if value == "":
|
||||
return Q()
|
||||
return Q(**{f"{field_name}__first_name__icontains": value})
|
||||
|
||||
def get_alter_column_prepare_new_value(self, connection, from_field, to_field):
|
||||
"""
|
||||
When converting to created by field type we won't preserve any
|
||||
values.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
sql = (
|
||||
f"""
|
||||
p_in = NULL;
|
||||
""" # nosec b608
|
||||
)
|
||||
# fmt: on
|
||||
return sql, {}
|
||||
|
||||
def get_alter_column_prepare_old_value(self, connection, from_field, to_field):
|
||||
"""
|
||||
When converting to another field type we won't preserve any values.
|
||||
"""
|
||||
|
||||
to_field_type = field_type_registry.get_by_model(to_field)
|
||||
if to_field_type.type != self.type and connection.vendor == "postgresql":
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SET CONSTRAINTS ALL IMMEDIATE")
|
||||
|
||||
# fmt: off
|
||||
sql = (
|
||||
f"""
|
||||
p_in = NULL;
|
||||
""" # nosec b608
|
||||
)
|
||||
# fmt: on
|
||||
return sql, {}
|
||||
|
||||
return super().get_alter_column_prepare_old_value(
|
||||
connection, from_field, to_field
|
||||
)
|
||||
|
||||
|
||||
class LinkRowFieldType(FieldType):
|
||||
"""
|
||||
The link row field can be used to link a field to a row of another table. Because
|
||||
|
|
|
@ -318,10 +318,9 @@ class DurationFieldUsingPostgresFormatting(models.DurationField):
|
|||
return sql + "::text", params
|
||||
|
||||
|
||||
class SyncedLastModifiedByForeignKeyField(IgnoreMissingForeignKey):
|
||||
requires_refresh_after_update = True
|
||||
|
||||
class SyncedUserForeignKeyField(IgnoreMissingForeignKey):
|
||||
def __init__(self, to, sync_with=None, sync_with_add=None, *args, **kwargs):
|
||||
self.requires_refresh_after_update = sync_with is not None
|
||||
self.sync_with = sync_with
|
||||
self.sync_with_add = sync_with_add
|
||||
if sync_with or sync_with_add:
|
||||
|
|
|
@ -342,6 +342,10 @@ class CreatedOnField(Field, BaseDateMixin):
|
|||
pass
|
||||
|
||||
|
||||
class CreatedByField(Field):
|
||||
pass
|
||||
|
||||
|
||||
class LinkRowField(Field):
|
||||
THROUGH_DATABASE_TABLE_PREFIX = LINK_ROW_THROUGH_TABLE_PREFIX
|
||||
link_row_table = models.ForeignKey(
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# Generated by Django 3.2.21 on 2023-12-01 13:46
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("database", "0142_add_multiple_select_to_formulafield"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="CreatedByField",
|
||||
fields=[
|
||||
(
|
||||
"field_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="database.field",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("database.field",),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="table",
|
||||
name="created_by_column_added",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
null=True,
|
||||
help_text="Indicates whether the table has had the created_by column added.",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 3.2.21 on 2023-12-01 13:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("database", "0143_add_table_created_by_column_added"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="table",
|
||||
name="created_by_column_added",
|
||||
field=models.BooleanField(
|
||||
default=True,
|
||||
help_text="Indicates whether the table has had the created_by column added.",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -59,6 +59,7 @@ from baserow.core.utils import Progress, get_non_unique_values, grouper
|
|||
|
||||
from ..search.handler import SearchHandler
|
||||
from ..table.constants import (
|
||||
CREATED_BY_COLUMN_NAME,
|
||||
LAST_MODIFIED_BY_COLUMN_NAME,
|
||||
ROW_NEEDS_BACKGROUND_UPDATE_COLUMN_NAME,
|
||||
)
|
||||
|
@ -768,10 +769,15 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
prepared_values, model
|
||||
)
|
||||
row_values["order"] = self.get_unique_orders_before_row(before, model)[0]
|
||||
|
||||
if getattr(model, CREATED_BY_COLUMN_NAME, None):
|
||||
row_values[CREATED_BY_COLUMN_NAME] = user if user and user.id else None
|
||||
|
||||
if getattr(model, LAST_MODIFIED_BY_COLUMN_NAME, None):
|
||||
row_values[LAST_MODIFIED_BY_COLUMN_NAME] = (
|
||||
user if user and user.id else None
|
||||
)
|
||||
|
||||
instance = model.objects.create(**row_values)
|
||||
rows_created_counter.add(1)
|
||||
|
||||
|
@ -1100,8 +1106,13 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
):
|
||||
row_values, manytomany_values = self.extract_manytomany_values(row, model)
|
||||
row_values["order"] = unique_orders[index]
|
||||
|
||||
if getattr(model, CREATED_BY_COLUMN_NAME, None):
|
||||
row_values[CREATED_BY_COLUMN_NAME] = user if user.id else None
|
||||
|
||||
if getattr(model, LAST_MODIFIED_BY_COLUMN_NAME, None):
|
||||
row_values[LAST_MODIFIED_BY_COLUMN_NAME] = user if user.id else None
|
||||
|
||||
instance = model(**row_values)
|
||||
|
||||
relations = {
|
||||
|
|
|
@ -19,3 +19,4 @@ ROW_NEEDS_BACKGROUND_UPDATE_COLUMN_NAME = "needs_background_update"
|
|||
TSV_FIELD_PREFIX = "tsv_field"
|
||||
|
||||
LAST_MODIFIED_BY_COLUMN_NAME = "last_modified_by"
|
||||
CREATED_BY_COLUMN_NAME = "created_by"
|
||||
|
|
|
@ -39,6 +39,7 @@ from baserow.core.trash.handler import TrashHandler
|
|||
from baserow.core.utils import ChildProgressBuilder, Progress, find_unused_name
|
||||
|
||||
from .constants import (
|
||||
CREATED_BY_COLUMN_NAME,
|
||||
LAST_MODIFIED_BY_COLUMN_NAME,
|
||||
ROW_NEEDS_BACKGROUND_UPDATE_COLUMN_NAME,
|
||||
TABLE_CREATION,
|
||||
|
@ -738,22 +739,35 @@ class TableHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
|
||||
table.save(update_fields=("needs_background_update_column_added",))
|
||||
|
||||
def create_last_modified_by_field(self, table: "Table") -> None:
|
||||
def create_created_by_and_last_modified_by_fields(self, table: "Table") -> None:
|
||||
"""
|
||||
Creates last_modified_by field for the provided table if
|
||||
it has not yet been created.
|
||||
Creates the created_by and last_modified_by fields for the provided
|
||||
table if they have not yet been created.
|
||||
|
||||
:param table: Table that should have last_modified_by field.
|
||||
:param table: Table that should have created_by and last_modified_by
|
||||
fields.
|
||||
"""
|
||||
|
||||
if table.last_modified_by_column_added:
|
||||
last_modified_by_column_added = table.last_modified_by_column_added
|
||||
created_by_column_added = table.created_by_column_added
|
||||
if last_modified_by_column_added and created_by_column_added:
|
||||
return
|
||||
|
||||
table.last_modified_by_column_added = True
|
||||
model = table.get_model(use_cache=False)
|
||||
table.created_by_column_added = True
|
||||
model = table.get_model(use_cache=False, field_ids=[])
|
||||
|
||||
with safe_django_schema_editor(atomic=False) as schema_editor:
|
||||
last_modified_by_field = model._meta.get_field(LAST_MODIFIED_BY_COLUMN_NAME)
|
||||
schema_editor.add_field(model, last_modified_by_field)
|
||||
if not last_modified_by_column_added:
|
||||
last_modified_by_field = model._meta.get_field(
|
||||
LAST_MODIFIED_BY_COLUMN_NAME
|
||||
)
|
||||
schema_editor.add_field(model, last_modified_by_field)
|
||||
|
||||
table.save(update_fields=["last_modified_by_column_added"])
|
||||
if not created_by_column_added:
|
||||
created_by_field = model._meta.get_field(CREATED_BY_COLUMN_NAME)
|
||||
schema_editor.add_field(model, created_by_field)
|
||||
|
||||
table.save(
|
||||
update_fields=["created_by_column_added", "last_modified_by_column_added"]
|
||||
)
|
||||
|
|
|
@ -41,6 +41,7 @@ from baserow.contrib.database.table.cache import (
|
|||
set_cached_model_field_attrs,
|
||||
)
|
||||
from baserow.contrib.database.table.constants import (
|
||||
CREATED_BY_COLUMN_NAME,
|
||||
LAST_MODIFIED_BY_COLUMN_NAME,
|
||||
ROW_NEEDS_BACKGROUND_UPDATE_COLUMN_NAME,
|
||||
TSV_FIELD_PREFIX,
|
||||
|
@ -761,6 +762,11 @@ class Table(
|
|||
help_text="Indicates whether the table has had the last_modified_by "
|
||||
"column added.",
|
||||
)
|
||||
created_by_column_added = models.BooleanField(
|
||||
default=True,
|
||||
null=True,
|
||||
help_text="Indicates whether the table has had the created_by column added.",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ("order",)
|
||||
|
@ -977,6 +983,9 @@ class Table(
|
|||
if self.needs_background_update_column_added:
|
||||
self._add_needs_background_update_column(field_attrs, indexes)
|
||||
|
||||
if self.created_by_column_added:
|
||||
self._add_created_by(field_attrs, indexes)
|
||||
|
||||
if self.last_modified_by_column_added:
|
||||
self._add_last_modified_by(field_attrs, indexes)
|
||||
|
||||
|
@ -1024,6 +1033,17 @@ class Table(
|
|||
|
||||
indexes.append(get_row_needs_background_update_index(self))
|
||||
|
||||
def _add_created_by(self, field_attrs, indexes):
|
||||
field_attrs[CREATED_BY_COLUMN_NAME] = IgnoreMissingForeignKey(
|
||||
User,
|
||||
null=True,
|
||||
related_name="+",
|
||||
related_query_name="+",
|
||||
db_constraint=False,
|
||||
on_delete=models.SET_NULL,
|
||||
help_text="Stores information about the user that created the row.",
|
||||
)
|
||||
|
||||
def _add_last_modified_by(self, field_attrs, indexes):
|
||||
field_attrs[LAST_MODIFIED_BY_COLUMN_NAME] = IgnoreMissingForeignKey(
|
||||
User,
|
||||
|
|
|
@ -187,13 +187,10 @@ def setup_new_background_update_and_search_columns(self, table_id: int):
|
|||
logger.debug(f"Postgres full-text search is disabled.")
|
||||
|
||||
|
||||
@app.task(
|
||||
bind=True,
|
||||
queue="export",
|
||||
)
|
||||
def setup_last_modified_by_column(self, table_id: int):
|
||||
@app.task(bind=True, queue="export")
|
||||
def setup_created_by_and_last_modified_by_column(self, table_id: int):
|
||||
from baserow.contrib.database.table.handler import TableHandler
|
||||
|
||||
with transaction.atomic():
|
||||
table = TableHandler().get_table_for_update(table_id)
|
||||
TableHandler().create_last_modified_by_field(table)
|
||||
TableHandler().create_created_by_and_last_modified_by_fields(table)
|
||||
|
|
|
@ -79,9 +79,13 @@ def update_view_index_if_view_group_by_changes(sender, view_group_by, **kwargs):
|
|||
|
||||
@receiver(view_loaded)
|
||||
def view_loaded_create_indexes_and_columns(sender, view, table_model, **kwargs):
|
||||
from baserow.contrib.database.table.tasks import setup_last_modified_by_column
|
||||
from baserow.contrib.database.table.tasks import (
|
||||
setup_created_by_and_last_modified_by_column,
|
||||
)
|
||||
from baserow.contrib.database.views.handler import ViewIndexingHandler
|
||||
|
||||
ViewIndexingHandler.schedule_index_creation_if_needed(view, table_model)
|
||||
if not view.table.last_modified_by_column_added:
|
||||
setup_last_modified_by_column.delay(table_id=view.table.id)
|
||||
|
||||
table = view.table
|
||||
if not table.last_modified_by_column_added or not table.created_by_column_added:
|
||||
setup_created_by_and_last_modified_by_column.delay(table_id=view.table.id)
|
||||
|
|
|
@ -21,6 +21,7 @@ from baserow.contrib.database.fields.field_filters import (
|
|||
)
|
||||
from baserow.contrib.database.fields.field_types import (
|
||||
BooleanFieldType,
|
||||
CreatedByFieldType,
|
||||
CreatedOnFieldType,
|
||||
DateFieldType,
|
||||
EmailFieldType,
|
||||
|
@ -1305,7 +1306,7 @@ class UserIsViewFilterType(ViewFilterType):
|
|||
"""
|
||||
|
||||
type = "user_is"
|
||||
compatible_field_types = [LastModifiedByFieldType.type]
|
||||
compatible_field_types = [CreatedByFieldType.type, LastModifiedByFieldType.type]
|
||||
|
||||
USER_KEY = f"users"
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from baserow.contrib.database.fields.dependencies.handler import FieldDependency
|
|||
from baserow.contrib.database.fields.field_cache import FieldCache
|
||||
from baserow.contrib.database.fields.models import (
|
||||
BooleanField,
|
||||
CreatedByField,
|
||||
CreatedOnField,
|
||||
DateField,
|
||||
EmailField,
|
||||
|
@ -260,6 +261,16 @@ class FieldFixtures:
|
|||
|
||||
return field
|
||||
|
||||
def create_created_by_field(self, user=None, create_field=True, **kwargs):
|
||||
self.set_test_field_kwarg_defaults(user, kwargs)
|
||||
|
||||
field = CreatedByField.objects.create(**kwargs)
|
||||
|
||||
if create_field:
|
||||
self.create_model_field(kwargs["table"], field)
|
||||
|
||||
return field
|
||||
|
||||
def create_created_on_field(self, user=None, create_field=True, **kwargs):
|
||||
if "date_include_time" not in kwargs:
|
||||
kwargs["date_include_time"] = False
|
||||
|
|
|
@ -194,6 +194,7 @@ def setup_interesting_test_table(
|
|||
"created_on_date_eu": None,
|
||||
"created_on_datetime_eu_tzone": None,
|
||||
"last_modified_by": None,
|
||||
"created_by": None,
|
||||
# We will setup link rows manually later
|
||||
"link_row": None,
|
||||
"self_link_row": None,
|
||||
|
|
|
@ -385,6 +385,20 @@
|
|||
"primary": false,
|
||||
"number_decimal_places": 2,
|
||||
"number_negative": false
|
||||
},
|
||||
{
|
||||
"id": 4698,
|
||||
"type": "last_modified_by",
|
||||
"name": "Last modified by",
|
||||
"order": 27,
|
||||
"primary": false
|
||||
},
|
||||
{
|
||||
"id": 4699,
|
||||
"type": "created_by",
|
||||
"name": "Created by",
|
||||
"order": 28,
|
||||
"primary": false
|
||||
}
|
||||
],
|
||||
"views": [
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
from django.shortcuts import reverse
|
||||
|
||||
import pytest
|
||||
from rest_framework.status import HTTP_200_OK, HTTP_400_BAD_REQUEST
|
||||
|
||||
from baserow.contrib.database.fields.handler import FieldHandler
|
||||
from baserow.contrib.database.fields.models import CreatedByField
|
||||
from baserow.test_utils.helpers import is_dict_subset
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_api_create_created_by_field_type(api_client, data_fixture):
|
||||
user, token = data_fixture.create_user_and_token(
|
||||
email="test@test.nl", password="password", first_name="Test1"
|
||||
)
|
||||
database = data_fixture.create_database_application(user=user, name="Placeholder")
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
|
||||
response = api_client.post(
|
||||
reverse("api:database:fields:list", kwargs={"table_id": table.id}),
|
||||
{
|
||||
"name": "created by",
|
||||
"type": "created_by",
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response_json["name"] == "created by"
|
||||
assert response_json["type"] == "created_by"
|
||||
assert CreatedByField.objects.all().count() == 1
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_api_update_created_by_field_type(api_client, data_fixture):
|
||||
workspace = data_fixture.create_workspace()
|
||||
user, token = data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
workspace=workspace,
|
||||
)
|
||||
database = data_fixture.create_database_application(
|
||||
user=user, name="Placeholder", workspace=workspace
|
||||
)
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created by", primary=True
|
||||
)
|
||||
|
||||
response = api_client.patch(
|
||||
reverse("api:database:fields:item", kwargs={"field_id": field.id}),
|
||||
{"name": "created by renamed"},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response_json["name"] == "created by renamed"
|
||||
assert response_json["type"] == "created_by"
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_api_delete_created_by_field_type(api_client, data_fixture):
|
||||
user, token = data_fixture.create_user_and_token(
|
||||
email="test@test.nl", password="password", first_name="Test1"
|
||||
)
|
||||
database = data_fixture.create_database_application(user=user, name="Placeholder")
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table,
|
||||
order=1,
|
||||
name="created by",
|
||||
)
|
||||
|
||||
response = api_client.delete(
|
||||
reverse("api:database:fields:item", kwargs={"field_id": field.id}),
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
field.refresh_from_db()
|
||||
assert field.trashed is True
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.api_rows
|
||||
@pytest.mark.django_db
|
||||
def test_api_create_created_by_field_type_row(api_client, data_fixture):
|
||||
user, token = data_fixture.create_user_and_token()
|
||||
database = data_fixture.create_database_application(user=user, name="Placeholder")
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
text_field = data_fixture.create_text_field(table=table)
|
||||
field_handler = FieldHandler()
|
||||
field = field_handler.create_field(
|
||||
user=user,
|
||||
table=table,
|
||||
type_name="created_by",
|
||||
name="created by",
|
||||
)
|
||||
|
||||
# can't set the field value directly
|
||||
response = api_client.post(
|
||||
reverse("api:database:rows:list", kwargs={"table_id": table.id}),
|
||||
{f"field_{field.id}": user.id},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||
assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
||||
|
||||
# value will be set when other field value is set
|
||||
response = api_client.post(
|
||||
reverse("api:database:rows:list", kwargs={"table_id": table.id}),
|
||||
{f"field_{text_field.id}": "text"},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response_json[f"field_{field.id}"] == {
|
||||
"name": user.first_name,
|
||||
"id": user.id,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.api_rows
|
||||
@pytest.mark.django_db
|
||||
def test_update_row_dont_change_created_by_fields_values(api_client, data_fixture):
|
||||
creator = data_fixture.create_user()
|
||||
user, token = data_fixture.create_user_and_token()
|
||||
database = data_fixture.create_database_application(user=user, name="Placeholder")
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
text_field = data_fixture.create_text_field(table=table)
|
||||
field_handler = FieldHandler()
|
||||
field = field_handler.create_field(
|
||||
user=user,
|
||||
table=table,
|
||||
type_name="created_by",
|
||||
name="created by",
|
||||
)
|
||||
|
||||
model = table.get_model()
|
||||
row = model.objects.create(**{f"field_{text_field.id}": "text"}, created_by=creator)
|
||||
|
||||
# can't set the field value directly
|
||||
response = api_client.patch(
|
||||
reverse(
|
||||
"api:database:rows:item", kwargs={"table_id": table.id, "row_id": row.id}
|
||||
),
|
||||
{f"field_{field.id}": user.id},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||
assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
||||
|
||||
# value will not change updating the row
|
||||
response = api_client.patch(
|
||||
reverse(
|
||||
"api:database:rows:item", kwargs={"table_id": table.id, "row_id": row.id}
|
||||
),
|
||||
{f"field_{text_field.id}": "updated text"},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response_json[f"field_{field.id}"] == {
|
||||
"id": creator.id,
|
||||
"name": creator.first_name,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.api_rows
|
||||
@pytest.mark.django_db
|
||||
def test_created_by_field_type_batch_insert_rows(api_client, data_fixture):
|
||||
workspace = data_fixture.create_workspace()
|
||||
user, token = data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
workspace=workspace,
|
||||
)
|
||||
database = data_fixture.create_database_application(
|
||||
user=user, workspace=workspace, name="Placeholder"
|
||||
)
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created by", primary=True
|
||||
)
|
||||
text_field = data_fixture.create_text_field(table=table, order=2, name="Text")
|
||||
|
||||
# can't set the field value directly
|
||||
response = api_client.post(
|
||||
reverse("api:database:rows:batch", kwargs={"table_id": table.id}),
|
||||
{
|
||||
"items": [
|
||||
{f"field_{field.id}": user.id},
|
||||
]
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||
assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
||||
|
||||
# value will be set when other field value is set
|
||||
response = api_client.post(
|
||||
reverse("api:database:rows:batch", kwargs={"table_id": table.id}),
|
||||
{
|
||||
"items": [
|
||||
{f"field_{text_field.id}": "text"},
|
||||
]
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert is_dict_subset(
|
||||
{"items": [{f"field_{field.id}": {"id": user.id, "name": user.first_name}}]},
|
||||
response.json(),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.api_rows
|
||||
@pytest.mark.django_db
|
||||
def test_created_by_field_type_dont_change_when_batch_update_rows(
|
||||
api_client, data_fixture
|
||||
):
|
||||
workspace = data_fixture.create_workspace()
|
||||
user, token = data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
workspace=workspace,
|
||||
)
|
||||
user2 = data_fixture.create_user(workspace=workspace)
|
||||
user3 = data_fixture.create_user(workspace=workspace)
|
||||
database = data_fixture.create_database_application(
|
||||
user=user, workspace=workspace, name="Placeholder"
|
||||
)
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created by", primary=True
|
||||
)
|
||||
text_field = data_fixture.create_text_field(table=table, order=2, name="Text")
|
||||
model = table.get_model()
|
||||
row1 = model.objects.create(created_by=user2)
|
||||
row2 = model.objects.create(created_by=user3)
|
||||
row3 = model.objects.create(created_by=user3)
|
||||
|
||||
# can't set the field value directly
|
||||
response = api_client.patch(
|
||||
reverse("api:database:rows:batch", kwargs={"table_id": table.id}),
|
||||
{
|
||||
"items": [
|
||||
{"id": row1.id, f"field_{text_field.id}": "new text"},
|
||||
{"id": row2.id, f"field_{field.id}": user.id},
|
||||
]
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
response_json = response.json()
|
||||
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||
assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
||||
|
||||
# value will be set when other field value is set
|
||||
response = api_client.patch(
|
||||
reverse("api:database:rows:batch", kwargs={"table_id": table.id}),
|
||||
{
|
||||
"items": [
|
||||
{"id": row1.id, f"field_{text_field.id}": "new text"},
|
||||
{"id": row2.id, f"field_{text_field.id}": "new text"},
|
||||
{"id": row3.id, f"field_{text_field.id}": "new text"},
|
||||
]
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
response_json = response.json()
|
||||
|
||||
assert response_json["items"][0][f"field_{field.id}"]["id"] == user2.id
|
||||
assert response_json["items"][1][f"field_{field.id}"]["id"] == user3.id
|
||||
assert response_json["items"][2][f"field_{field.id}"]["id"] == user3.id
|
|
@ -296,5 +296,5 @@ def test_last_modified_by_field_type_batch_update_rows(api_client, data_fixture)
|
|||
|
||||
# assert response_json == {}
|
||||
assert response_json["items"][0][f"field_{field.id}"]["id"] == user.id
|
||||
assert response_json["items"][0][f"field_{field.id}"]["id"] == user.id
|
||||
assert response_json["items"][1][f"field_{field.id}"]["id"] == user.id
|
||||
assert getattr(row3, f"field_{field.id}") == user3
|
||||
|
|
|
@ -233,6 +233,7 @@ def test_get_row_serializer_with_user_field_names(data_fixture):
|
|||
"last_modified_datetime_eu": "2021-01-02T12:00:00Z",
|
||||
"last_modified_datetime_us": "2021-01-02T12:00:00Z",
|
||||
"last_modified_datetime_eu_tzone": "2021-01-02T12:00:00Z",
|
||||
"created_by": {"id": user.id, "name": user.first_name},
|
||||
"last_modified_by": {"id": user.id, "name": user.first_name},
|
||||
"created_on_date_eu": "2021-01-02",
|
||||
"created_on_date_us": "2021-01-02",
|
||||
|
|
|
@ -226,21 +226,21 @@ def test_can_export_every_interesting_different_field_to_csv(
|
|||
"last_modified_datetime_us,last_modified_date_us,last_modified_datetime_eu,"
|
||||
"last_modified_date_eu,last_modified_datetime_eu_tzone,created_on_datetime_us,"
|
||||
"created_on_date_us,created_on_datetime_eu,created_on_date_eu,created_on_datetime_eu_tzone,"
|
||||
"last_modified_by,link_row,self_link_row,link_row_without_related,decimal_link_row,"
|
||||
"last_modified_by,created_by,link_row,self_link_row,link_row_without_related,decimal_link_row,"
|
||||
"file_link_row,file,single_select,multiple_select,multiple_collaborators,"
|
||||
"phone_number,formula_text,formula_int,formula_bool,formula_decimal,formula_dateinterval,"
|
||||
"formula_date,formula_singleselect,formula_email,formula_link_with_label,"
|
||||
"formula_link_url_only,formula_multipleselect,count,rollup,lookup,uuid\r\n"
|
||||
"1,,,,,,,,,0,False,,,,,,,01/02/2021 12:00,01/02/2021,02/01/2021 12:00,02/01/2021,"
|
||||
"02/01/2021 13:00,01/02/2021 12:00,01/02/2021,02/01/2021 12:00,02/01/2021,"
|
||||
"02/01/2021 13:00,user@example.com,,,,,,,,,,,test FORMULA,1,True,33.3333333333,1 day,"
|
||||
"2020-01-01,,,label (https://google.com),https://google.com,,0,0.000,,"
|
||||
"02/01/2021 13:00,user@example.com,user@example.com,,,,,,,,,,,test FORMULA,1,True,33.3333333333,"
|
||||
"1 day,2020-01-01,,,label (https://google.com),https://google.com,,0,0.000,,"
|
||||
"00000000-0000-4000-8000-000000000001\r\n"
|
||||
"2,text,long_text,https://www.google.com,test@example.com,-1,1,-1.2,1.2,3,True,"
|
||||
"02/01/2020 01:23,02/01/2020,01/02/2020 01:23,01/02/2020,01/02/2020 02:23,"
|
||||
"01/02/2020 02:23,01/02/2021 12:00,01/02/2021,02/01/2021 12:00,02/01/2021,"
|
||||
"02/01/2021 13:00,01/02/2021 12:00,01/02/2021,02/01/2021 12:00,02/01/2021,"
|
||||
'02/01/2021 13:00,user@example.com,"linked_row_1,linked_row_2,unnamed row 3",unnamed row 1,'
|
||||
'02/01/2021 13:00,user@example.com,user@example.com,"linked_row_1,linked_row_2,unnamed row 3",unnamed row 1,'
|
||||
'"linked_row_1,linked_row_2","1.234,-123.456,unnamed row 3",'
|
||||
'"name.txt (http://localhost:8000/media/user_files/test_hash.txt),unnamed row 2",'
|
||||
'"a.txt (http://localhost:8000/media/user_files/hashed_name.txt),'
|
||||
|
|
|
@ -0,0 +1,468 @@
|
|||
from io import BytesIO
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
import pytest
|
||||
|
||||
from baserow.contrib.database.fields.deferred_field_fk_updater import (
|
||||
DeferredFieldFkUpdater,
|
||||
)
|
||||
from baserow.contrib.database.fields.handler import FieldHandler
|
||||
from baserow.contrib.database.fields.registries import field_type_registry
|
||||
from baserow.contrib.database.rows.handler import RowHandler
|
||||
from baserow.contrib.database.views.handler import ViewHandler
|
||||
from baserow.core.handler import CoreHandler
|
||||
from baserow.core.registries import ImportExportConfig
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_create_created_by_field(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
text_field = data_fixture.create_text_field(
|
||||
table=table, order=1, name="name", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
row_handler.create_row(
|
||||
user=user, table=table, values={f"field_{text_field.id}": "Row 1"}, model=model
|
||||
)
|
||||
row_handler.create_row(
|
||||
user=user, table=table, values={f"field_{text_field.id}": "Row 2"}, model=model
|
||||
)
|
||||
row_handler.create_row(
|
||||
user=user, table=table, values={f"field_{text_field.id}": "Row 3"}, model=model
|
||||
)
|
||||
|
||||
field_handler = FieldHandler()
|
||||
field = field_handler.create_field(
|
||||
user=user,
|
||||
table=table,
|
||||
type_name="created_by",
|
||||
name="created by",
|
||||
)
|
||||
|
||||
model = table.get_model()
|
||||
rows = list(model.objects.all())
|
||||
assert getattr(rows[0], f"field_{field.id}") == user
|
||||
assert getattr(rows[1], f"field_{field.id}") == user
|
||||
assert getattr(rows[2], f"field_{field.id}") == user
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_create_created_by_field_force_create_created_by_column(
|
||||
data_fixture,
|
||||
):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user, created_by_column_added=False)
|
||||
text_field = data_fixture.create_text_field(
|
||||
table=table, order=1, name="name", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
row_handler.create_row(
|
||||
user=user, table=table, values={f"field_{text_field.id}": "Row 1"}, model=model
|
||||
)
|
||||
|
||||
field_handler = FieldHandler()
|
||||
field = field_handler.create_field(
|
||||
user=user,
|
||||
table=table,
|
||||
type_name="created_by",
|
||||
name="created by",
|
||||
)
|
||||
|
||||
model = table.get_model()
|
||||
rows = list(model.objects.all())
|
||||
assert getattr(rows[0], f"field_{field.id}") is None
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_create_row_created_by(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created_by_field", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
row_1 = row_handler.create_row(user=user, table=table, values={}, model=model)
|
||||
|
||||
assert getattr(row_1, f"field_{field.id}") == user
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_prevent_create_row_created_by(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created_by_field", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
row_handler.create_row(
|
||||
user=user,
|
||||
table=table,
|
||||
values={f"field_{field.id}": user.id},
|
||||
model=model,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_create_rows_created_by(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created_by_field", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
rows = row_handler.create_rows(
|
||||
user=user, table=table, rows_values=[{}, {}], model=model
|
||||
)
|
||||
|
||||
assert getattr(rows[0], f"field_{field.id}") == user
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_prevent_create_rows_created_by(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created_by_field", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
row_handler.create_rows(
|
||||
user=user,
|
||||
table=table,
|
||||
rows_values=[{f"field_{field.id}": user.id}, {}],
|
||||
model=model,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_update_row_dont_update_created_by(data_fixture):
|
||||
creator = data_fixture.create_user()
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
text_field = data_fixture.create_text_field(table=table, order=2, name="text")
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created_by_field", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
row_1 = model.objects.create(
|
||||
**{f"field_{text_field.id}": "text"}, created_by=creator
|
||||
)
|
||||
|
||||
assert getattr(row_1, f"field_{field.id}") == creator
|
||||
|
||||
updated_row = row_handler.update_row(
|
||||
user=user, table=table, row=row_1, values={"text": "test"}, model=model
|
||||
)
|
||||
|
||||
assert getattr(updated_row, f"field_{field.id}") == creator
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_prevent_update_row_created_by(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
text_field = data_fixture.create_text_field(table=table, order=2, name="text")
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created_by_field", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
row_1 = model.objects.create(**{f"field_{text_field.id}": "text"})
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
row_handler.update_row(
|
||||
user=user,
|
||||
table=table,
|
||||
row=row_1,
|
||||
values={f"field_{field.id}": user.id},
|
||||
model=model,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_update_rows_dont_update_created_by(data_fixture):
|
||||
creator = data_fixture.create_user()
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
text_field = data_fixture.create_text_field(table=table, order=2, name="text")
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created_by_field", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
row_1 = model.objects.create(
|
||||
**{f"field_{text_field.id}": "text"}, created_by=creator
|
||||
)
|
||||
|
||||
assert getattr(row_1, f"field_{field.id}") == creator
|
||||
|
||||
row_handler.update_rows(
|
||||
user=user,
|
||||
table=table,
|
||||
rows_values=[{"id": row_1.id, f"field_{text_field.id}": "changed"}],
|
||||
model=model,
|
||||
)
|
||||
|
||||
row_1.refresh_from_db()
|
||||
assert getattr(row_1, f"field_{field.id}") == creator
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_prevent_update_rows_created_by(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
text_field = data_fixture.create_text_field(table=table, order=2, name="text")
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created_by_field", primary=True
|
||||
)
|
||||
|
||||
row_handler = RowHandler()
|
||||
model = table.get_model()
|
||||
|
||||
row_1 = model.objects.create(**{f"field_{text_field.id}": "text"})
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
row_handler.update_rows(
|
||||
user=user,
|
||||
table=table,
|
||||
rows_values=[{"id": row_1.id, f"field_{field.id}": user.id}],
|
||||
model=model,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_import_export_created_by_field(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
field_handler = FieldHandler()
|
||||
field = field_handler.create_field(
|
||||
user=user,
|
||||
table=table,
|
||||
type_name="created_by",
|
||||
name="modified by",
|
||||
)
|
||||
field_type = field_type_registry.get_by_model(field)
|
||||
field_serialized = field_type.export_serialized(field)
|
||||
id_mapping = {}
|
||||
field_imported = field_type.import_serialized(
|
||||
table,
|
||||
field_serialized,
|
||||
ImportExportConfig(include_permission_data=True),
|
||||
id_mapping,
|
||||
DeferredFieldFkUpdater(),
|
||||
)
|
||||
|
||||
assert field_imported.id != field.id
|
||||
assert field_serialized == {
|
||||
"id": field.id,
|
||||
"name": "modified by",
|
||||
"order": field.order,
|
||||
"primary": False,
|
||||
"type": "created_by",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_get_set_export_serialized_value_created_by_field(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
workspace = data_fixture.create_workspace(user=user)
|
||||
imported_workspace = data_fixture.create_workspace(user=user)
|
||||
database = data_fixture.create_database_application(workspace=workspace)
|
||||
table = data_fixture.create_database_table(database=database)
|
||||
field = data_fixture.create_created_by_field(table=table)
|
||||
|
||||
core_handler = CoreHandler()
|
||||
|
||||
model = table.get_model()
|
||||
row_1 = model.objects.create(created_by=user)
|
||||
row_2 = model.objects.create()
|
||||
row_3 = model.objects.create(created_by=user)
|
||||
|
||||
config = ImportExportConfig(include_permission_data=False)
|
||||
exported_applications = core_handler.export_workspace_applications(
|
||||
workspace, BytesIO(), config
|
||||
)
|
||||
imported_applications, _ = core_handler.import_applications_to_workspace(
|
||||
imported_workspace, exported_applications, BytesIO(), config, None
|
||||
)
|
||||
imported_database = imported_applications[0]
|
||||
imported_table = imported_database.table_set.all()[0]
|
||||
imported_field = imported_table.field_set.all().first().specific
|
||||
|
||||
assert imported_table.id != table.id
|
||||
assert imported_field.id != field.id
|
||||
|
||||
imported_model = imported_table.get_model()
|
||||
all = imported_model.objects.all()
|
||||
assert len(all) == 3
|
||||
imported_row_1 = all[0]
|
||||
imported_row_2 = all[1]
|
||||
imported_row_3 = all[2]
|
||||
|
||||
assert getattr(imported_row_1, f"field_{imported_field.id}") == user
|
||||
assert getattr(imported_row_2, f"field_{imported_field.id}") is None
|
||||
assert getattr(imported_row_3, f"field_{imported_field.id}") == user
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_duplicate_created_by_field(data_fixture):
|
||||
workspace = data_fixture.create_workspace()
|
||||
user, token = data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
workspace=workspace,
|
||||
)
|
||||
user2 = data_fixture.create_user(workspace=workspace)
|
||||
user3 = data_fixture.create_user(workspace=workspace)
|
||||
database = data_fixture.create_database_application(
|
||||
user=user, workspace=workspace, name="Placeholder"
|
||||
)
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created by", primary=True
|
||||
)
|
||||
model = table.get_model()
|
||||
row1 = model.objects.create(created_by=user)
|
||||
row2 = model.objects.create(created_by=user2)
|
||||
row3 = model.objects.create(created_by=user3)
|
||||
|
||||
new_field, _ = FieldHandler().duplicate_field(user, field)
|
||||
|
||||
model = table.get_model()
|
||||
rows = model.objects.all()
|
||||
assert getattr(rows[0], f"field_{new_field.id}") == user
|
||||
assert getattr(rows[1], f"field_{new_field.id}") == user2
|
||||
assert getattr(rows[2], f"field_{new_field.id}") == user3
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_trash_restore_created_by_field(data_fixture):
|
||||
workspace = data_fixture.create_workspace()
|
||||
user = data_fixture.create_user(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
workspace=workspace,
|
||||
)
|
||||
user2 = data_fixture.create_user(workspace=workspace)
|
||||
user3 = data_fixture.create_user(workspace=workspace)
|
||||
database = data_fixture.create_database_application(
|
||||
user=user, workspace=workspace, name="Placeholder"
|
||||
)
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
field = data_fixture.create_created_by_field(
|
||||
table=table, order=1, name="created by"
|
||||
)
|
||||
text_field = data_fixture.create_text_field(table=table, order=2, name="Text")
|
||||
model = table.get_model()
|
||||
row1 = model.objects.create(created_by=user2)
|
||||
row2 = model.objects.create(created_by=user2)
|
||||
row3 = model.objects.create(created_by=user3)
|
||||
row4 = model.objects.create(created_by=user3)
|
||||
|
||||
FieldHandler().delete_field(user, field)
|
||||
|
||||
# last_updated_by in the table will be changed by making
|
||||
# changes to rows. This needs to be reflected after the
|
||||
# field is restored
|
||||
|
||||
RowHandler().update_row_by_id(
|
||||
user, table, row_id=row1.id, values={f"field_{text_field.id}": "new text"}
|
||||
)
|
||||
|
||||
RowHandler().update_rows(
|
||||
user=user,
|
||||
table=table,
|
||||
rows_values=[
|
||||
{"id": row2.id, f"field_{text_field.id}": "updated"},
|
||||
{"id": row3.id, f"field_{text_field.id}": "updated"},
|
||||
],
|
||||
)
|
||||
|
||||
FieldHandler().restore_field(field)
|
||||
|
||||
model = table.get_model()
|
||||
rows = model.objects.all()
|
||||
assert getattr(rows[0], f"field_{field.id}") == user2
|
||||
assert getattr(rows[1], f"field_{field.id}") == user2
|
||||
assert getattr(rows[2], f"field_{field.id}") == user3
|
||||
assert getattr(rows[3], f"field_{field.id}") == user3
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.django_db
|
||||
def test_created_by_field_type_sorting(data_fixture):
|
||||
user_a = data_fixture.create_user(email="user1@baserow.io", first_name="User a")
|
||||
user_b = data_fixture.create_user(email="user2@baserow.io", first_name="User b")
|
||||
user_c = data_fixture.create_user(email="user3@baserow.io", first_name="User c")
|
||||
|
||||
database = data_fixture.create_database_application(user=user_a, name="Placeholder")
|
||||
data_fixture.create_user_workspace(workspace=database.workspace, user=user_b)
|
||||
data_fixture.create_user_workspace(workspace=database.workspace, user=user_c)
|
||||
table = data_fixture.create_database_table(name="Example", database=database)
|
||||
grid_view = data_fixture.create_grid_view(table=table)
|
||||
field = data_fixture.create_created_by_field(
|
||||
user=user_a, table=table, name="created by"
|
||||
)
|
||||
model = table.get_model()
|
||||
view_handler = ViewHandler()
|
||||
|
||||
row1 = model.objects.create(created_by=user_c)
|
||||
row2 = model.objects.create(created_by=user_b)
|
||||
row3 = model.objects.create(created_by=user_a)
|
||||
row4 = model.objects.create(created_by=user_c)
|
||||
row5 = model.objects.create(created_by=None)
|
||||
|
||||
sort = data_fixture.create_view_sort(view=grid_view, field=field, order="ASC")
|
||||
rows = view_handler.apply_sorting(grid_view, model.objects.all())
|
||||
row_ids = [row.id for row in rows]
|
||||
assert row_ids == [row5.id, row3.id, row2.id, row1.id, row4.id]
|
||||
|
||||
sort.order = "DESC"
|
||||
sort.save()
|
||||
|
||||
rows = view_handler.apply_sorting(grid_view, model.objects.all())
|
||||
row_ids = [row.id for row in rows]
|
||||
assert row_ids == [row1.id, row4.id, row2.id, row3.id, row5.id]
|
|
@ -28,6 +28,7 @@ from baserow.contrib.database.fields.field_helpers import (
|
|||
from baserow.contrib.database.fields.field_types import (
|
||||
BooleanFieldType,
|
||||
CountFieldType,
|
||||
CreatedByFieldType,
|
||||
CreatedOnFieldType,
|
||||
DateFieldType,
|
||||
EmailFieldType,
|
||||
|
@ -337,6 +338,13 @@ def test_field_conversion_last_modified_by(data_fixture):
|
|||
_test_can_convert_between_fields(data_fixture, LastModifiedByFieldType.type)
|
||||
|
||||
|
||||
@pytest.mark.field_created_by
|
||||
@pytest.mark.disabled_in_ci
|
||||
@pytest.mark.django_db
|
||||
def test_field_conversion_created_by(data_fixture):
|
||||
_test_can_convert_between_fields(data_fixture, CreatedByFieldType.type)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_field(data_fixture):
|
||||
user = data_fixture.create_user()
|
||||
|
|
|
@ -754,7 +754,7 @@ def test_create_last_modified_by_field(data_fixture):
|
|||
with pytest.raises(FieldDoesNotExist):
|
||||
model._meta.get_field(LAST_MODIFIED_BY_COLUMN_NAME)
|
||||
|
||||
TableHandler().create_last_modified_by_field(table)
|
||||
TableHandler().create_created_by_and_last_modified_by_fields(table)
|
||||
|
||||
table.refresh_from_db()
|
||||
assert table.last_modified_by_column_added
|
||||
|
|
|
@ -50,7 +50,7 @@ def test_workspace_user_get_next_order(data_fixture):
|
|||
|
||||
@pytest.mark.django_db
|
||||
def test_get_table_model(data_fixture):
|
||||
default_model_fields_count = 6
|
||||
default_model_fields_count = 7
|
||||
table = data_fixture.create_database_table(name="Cars")
|
||||
text_field = data_fixture.create_text_field(
|
||||
table=table, order=0, name="Color", text_default="white"
|
||||
|
@ -186,6 +186,7 @@ def test_get_table_model_with_fulltext_search_enabled(data_fixture):
|
|||
"updated_on",
|
||||
"trashed",
|
||||
"order",
|
||||
"created_by",
|
||||
"last_modified_by",
|
||||
]
|
||||
added_fields = [
|
||||
|
|
|
@ -19,7 +19,7 @@ def test_view_loaded_creates_last_modified_by_column(indexing_handler, data_fixt
|
|||
|
||||
# won't schedule column creation if already added
|
||||
with patch(
|
||||
"baserow.contrib.database.table.tasks.setup_last_modified_by_column"
|
||||
"baserow.contrib.database.table.tasks.setup_created_by_and_last_modified_by_column"
|
||||
) as setup:
|
||||
view_loaded_create_indexes_and_columns(
|
||||
None, view, table_model, table=table, user=user
|
||||
|
@ -30,7 +30,7 @@ def test_view_loaded_creates_last_modified_by_column(indexing_handler, data_fixt
|
|||
table.last_modified_by_column_added = False
|
||||
table.save()
|
||||
with patch(
|
||||
"baserow.contrib.database.table.tasks.setup_last_modified_by_column"
|
||||
"baserow.contrib.database.table.tasks.setup_created_by_and_last_modified_by_column"
|
||||
) as setup:
|
||||
view_loaded_create_indexes_and_columns(
|
||||
None, view, table_model, table=table, user=user
|
||||
|
|
|
@ -1172,6 +1172,17 @@ def test_local_baserow_table_service_generate_schema_with_interesting_test_table
|
|||
"title": "last_modified_by",
|
||||
"type": "object",
|
||||
},
|
||||
field_db_column_by_name["created_by"]: {
|
||||
"default": None,
|
||||
"metadata": {},
|
||||
"original_type": "created_by",
|
||||
"properties": {
|
||||
"id": {"title": "id", "type": "number"},
|
||||
"name": {"title": "name", "type": "string"},
|
||||
},
|
||||
"title": "created_by",
|
||||
"type": "object",
|
||||
},
|
||||
field_db_column_by_name["link_row"]: {
|
||||
"title": "link_row",
|
||||
"default": None,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"message": "Add the created_by field type.",
|
||||
"issue_number": 624,
|
||||
"bullet_points": [],
|
||||
"created_at": "2023-12-04"
|
||||
}
|
|
@ -65,6 +65,7 @@ def test_can_export_every_interesting_different_field_to_json(
|
|||
"created_on_date_eu": "02/01/2021",
|
||||
"created_on_datetime_eu_tzone": "02/01/2021 13:00",
|
||||
"last_modified_by": "user@example.com",
|
||||
"created_by": "user@example.com",
|
||||
"link_row": [],
|
||||
"self_link_row": [],
|
||||
"link_row_without_related": [],
|
||||
|
@ -125,6 +126,7 @@ def test_can_export_every_interesting_different_field_to_json(
|
|||
"created_on_date_eu": "02/01/2021",
|
||||
"created_on_datetime_eu_tzone": "02/01/2021 13:00",
|
||||
"last_modified_by": "user@example.com",
|
||||
"created_by": "user@example.com",
|
||||
"link_row": [
|
||||
"linked_row_1",
|
||||
"linked_row_2",
|
||||
|
@ -308,6 +310,7 @@ def test_can_export_every_interesting_different_field_to_xml(
|
|||
<created-on-date-eu>02/01/2021</created-on-date-eu>
|
||||
<created-on-datetime-eu-tzone>02/01/2021 13:00</created-on-datetime-eu-tzone>
|
||||
<last-modified-by>user@example.com</last-modified-by>
|
||||
<created-by>user@example.com</created-by>
|
||||
<link-row/>
|
||||
<self-link-row/>
|
||||
<link-row-without-related/>
|
||||
|
@ -368,6 +371,7 @@ def test_can_export_every_interesting_different_field_to_xml(
|
|||
<created-on-date-eu>02/01/2021</created-on-date-eu>
|
||||
<created-on-datetime-eu-tzone>02/01/2021 13:00</created-on-datetime-eu-tzone>
|
||||
<last-modified-by>user@example.com</last-modified-by>
|
||||
<created-by>user@example.com</created-by>
|
||||
<link-row>
|
||||
<item>linked_row_1</item>
|
||||
<item>linked_row_2</item>
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
"lastModified": "Last modified",
|
||||
"lastModifiedBy": "Last modified by",
|
||||
"createdOn": "Created on",
|
||||
"createdBy": "Created by",
|
||||
"url": "URL",
|
||||
"email": "Email",
|
||||
"file": "File",
|
||||
|
@ -137,6 +138,7 @@
|
|||
"lastModifiedReadOnly": "The last modified field is a read only field.",
|
||||
"lastModifiedBy": "The last modified by field is a read only field.",
|
||||
"createdOnReadOnly": "The created on field is a read only field.",
|
||||
"createdBy": "The created by field is a read only field showing the user that created the row.",
|
||||
"url": "Accepts a string that must be a URL.",
|
||||
"email": "Accepts a string that must be an email address.",
|
||||
"file": "Accepts an array of objects containing at least the name of the user file. You can use the \"File uploads\" endpoints to upload the file. The response of those calls can be provided directly as object here. The endpoints can be found in the left sidebar.",
|
||||
|
|
|
@ -2021,6 +2021,144 @@ export class LastModifiedByFieldType extends FieldType {
|
|||
}
|
||||
}
|
||||
|
||||
export class CreatedByFieldType extends FieldType {
|
||||
static getType() {
|
||||
return 'created_by'
|
||||
}
|
||||
|
||||
getIconClass() {
|
||||
return 'iconoir-user'
|
||||
}
|
||||
|
||||
getName() {
|
||||
const { i18n } = this.app
|
||||
return i18n.t('fieldType.createdBy')
|
||||
}
|
||||
|
||||
getFormViewFieldComponents(field) {
|
||||
return {}
|
||||
}
|
||||
|
||||
getIsReadOnly() {
|
||||
return true
|
||||
}
|
||||
|
||||
shouldFetchDataWhenAdded() {
|
||||
return true
|
||||
}
|
||||
|
||||
getGridViewFieldComponent() {
|
||||
return GridViewFieldLastModifiedBy
|
||||
}
|
||||
|
||||
getFunctionalGridViewFieldComponent() {
|
||||
return FunctionalGridViewFieldLastModifiedBy
|
||||
}
|
||||
|
||||
getRowEditFieldComponent(field) {
|
||||
return RowEditFieldLastModifiedBy
|
||||
}
|
||||
|
||||
getCardComponent() {
|
||||
return RowCardFieldLastModifiedBy
|
||||
}
|
||||
|
||||
getCanSortInView(field) {
|
||||
return true
|
||||
}
|
||||
|
||||
getSort(name, order) {
|
||||
return (a, b) => {
|
||||
let userNameA = a[name] === null ? '' : a[name].name
|
||||
let userNameB = b[name] === null ? '' : b[name].name
|
||||
|
||||
const workspaces = this.app.store.getters['workspace/getAll']
|
||||
const workspaceAvailable = workspaces.length > 0
|
||||
if (workspaceAvailable) {
|
||||
if (a[name] !== null) {
|
||||
const workspaceUserA = this.app.store.getters[
|
||||
'workspace/getUserById'
|
||||
](a[name].id)
|
||||
userNameA = workspaceUserA ? workspaceUserA.name : userNameA
|
||||
}
|
||||
|
||||
if (b[name] !== null) {
|
||||
const workspaceUserB = this.app.store.getters[
|
||||
'workspace/getUserById'
|
||||
](b[name].id)
|
||||
userNameB = workspaceUserB ? workspaceUserB.name : userNameB
|
||||
}
|
||||
}
|
||||
|
||||
return collatedStringCompare(userNameA, userNameB, order)
|
||||
}
|
||||
}
|
||||
|
||||
canBeReferencedByFormulaField() {
|
||||
return false
|
||||
}
|
||||
|
||||
_getCurrentUserValue() {
|
||||
return {
|
||||
id: this.app.store.getters['auth/getUserId'],
|
||||
name: this.app.store.getters['auth/getName'],
|
||||
}
|
||||
}
|
||||
|
||||
getNewRowValue() {
|
||||
return this._getCurrentUserValue()
|
||||
}
|
||||
|
||||
onRowChange(row, currentField, currentFieldValue) {
|
||||
return currentFieldValue
|
||||
}
|
||||
|
||||
prepareValueForCopy(field, value) {
|
||||
if (value === undefined || value === null) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const name = value.name
|
||||
|
||||
const workspaces = this.app.store.getters['workspace/getAll']
|
||||
if (workspaces.length > 0) {
|
||||
const workspaceUser = this.app.store.getters['workspace/getUserById'](
|
||||
value.id
|
||||
)
|
||||
return workspaceUser ? workspaceUser.name : name
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
toHumanReadableString(field, value, delimiter = ', ') {
|
||||
return this.prepareValueForCopy(field, value)
|
||||
}
|
||||
|
||||
toSearchableString(field, value, delimiter = ', ') {
|
||||
return this.toHumanReadableString(field, value, delimiter)
|
||||
}
|
||||
|
||||
getContainsFilterFunction() {
|
||||
return genericContainsFilter
|
||||
}
|
||||
|
||||
getDocsDataType(field) {
|
||||
return 'object'
|
||||
}
|
||||
|
||||
getDocsDescription(field) {
|
||||
return this.app.i18n.t('fieldDocs.createdBy')
|
||||
}
|
||||
|
||||
getDocsRequestExample() {
|
||||
return {
|
||||
id: 1,
|
||||
name: 'John',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class URLFieldType extends FieldType {
|
||||
static getType() {
|
||||
return 'url'
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
MultipleSelectFieldType,
|
||||
PhoneNumberFieldType,
|
||||
CreatedOnFieldType,
|
||||
CreatedByFieldType,
|
||||
FormulaFieldType,
|
||||
CountFieldType,
|
||||
RollupFieldType,
|
||||
|
@ -443,6 +444,7 @@ export default (context) => {
|
|||
app.$registry.register('field', new LastModifiedFieldType(context))
|
||||
app.$registry.register('field', new LastModifiedByFieldType(context))
|
||||
app.$registry.register('field', new CreatedOnFieldType(context))
|
||||
app.$registry.register('field', new CreatedByFieldType(context))
|
||||
app.$registry.register('field', new URLFieldType(context))
|
||||
app.$registry.register('field', new EmailFieldType(context))
|
||||
app.$registry.register('field', new FileFieldType(context))
|
||||
|
|
|
@ -1614,7 +1614,7 @@ export class UserIsFilterType extends ViewFilterType {
|
|||
}
|
||||
|
||||
getCompatibleFieldTypes() {
|
||||
return ['last_modified_by']
|
||||
return ['created_by', 'last_modified_by']
|
||||
}
|
||||
|
||||
isAllowedInPublicViews() {
|
||||
|
@ -1650,7 +1650,7 @@ export class UserIsNotFilterType extends ViewFilterType {
|
|||
}
|
||||
|
||||
getCompatibleFieldTypes() {
|
||||
return ['last_modified_by']
|
||||
return ['created_by', 'last_modified_by']
|
||||
}
|
||||
|
||||
isAllowedInPublicViews() {
|
||||
|
|
|
@ -206,6 +206,14 @@ const mockedFields = {
|
|||
table_id: 42,
|
||||
type: 'last_modified_by',
|
||||
},
|
||||
created_by: {
|
||||
id: 22,
|
||||
name: 'created_by',
|
||||
order: 22,
|
||||
primary: false,
|
||||
table_id: 42,
|
||||
type: 'created_by',
|
||||
},
|
||||
}
|
||||
|
||||
const valuesToCall = [null, undefined]
|
||||
|
|
Loading…
Add table
Reference in a new issue