A field is an abstraction that defines how table data is stored per column. More information can be found on the database plugin page. This is a tutorial about how to create your own custom table field type for Baserow via a plugin. We are going to create a integer field which displays as “hello world”. Of course a number field with the more features already exists, this is just for example purposes. In the end the user can create an integer field that only shows a hello world message. We expect that you are using the plugin boilerplate.
We are going to start by creating an IntegerField
model that extends the Field
model. It will have a property to indicate if a negative number is allowed which can be
set for each field that is created.
Create plugins/my_baserow_plugin/backend/src/my_baserow_plugin/models.py
from django.db import models
from baserow.contrib.database.fields.models import Field
class IntegerField(Field):
integer_negative = models.BooleanField(default=False)
Depending on the field model instance a model and serializer field must be returned. The
model field is used when generating the table’s model that is used to select and update
the data. The serializer field is used when exposing the data via the REST API to the
web-frontend. For more information about the properties and methods related to the field
type you can check the
backend/src/baserow/contrib/database/fields/registries.py::FieldType
class in the
Baserow repository.
Create plugins/my_baserow_plugin/backend/src/my_baserow_plugin/field_types.py
from django.db import models
from django.core.exceptions import ValidationError
from rest_framework import serializers
from baserow.contrib.database.fields.registries import FieldType
from .models import IntegerField
class IntegerFieldType(FieldType):
type = 'integer'
model_class = IntegerField
allowed_fields = ['integer_negative']
serializer_field_names = ['integer_negative']
def prepare_value_for_db(self, instance, value):
if value and not instance.integer_negative and value < 0:
raise ValidationError(f'The value for field {instance.id} cannot be '
f'negative.')
return value
def get_serializer_field(self, instance, **kwargs):
return serializers.IntegerField(required=False, allow_null=True)
def get_model_field(self, instance, **kwargs):
return models.IntegerField(null=True, blank=True)
Create plugins/my_baserow_plugin/backend/src/my_baserow_plugin/config.py
from django.apps import AppConfig
from baserow.core.registries import plugin_registry
from baserow.contrib.database.fields.registries import field_type_registry
class PluginNameConfig(AppConfig):
name = 'my_baserow_plugin'
def ready(self):
from .plugins import PluginNamePlugin
from .field_types import IntegerFieldType
plugin_registry.register(PluginNamePlugin())
field_type_registry.register(IntegerFieldType())
Finally, lets start the dev environment and use it to apply the migrations because we have created a new model.
# Set these env vars to make sure mounting your source code into the container uses
# the correct user and permissions.
export PLUGIN_BUILD_UID=$(id -u)
export PLUGIN_BUILD_GID=$(id -g)
docker-compose run my-baserow-plugin /baserow.sh backend-cmd manage makemigrations
docker-compose run my-baserow-plugin /baserow.sh backend-cmd manage migrate
Because the backend and web-frontend are two separate applications that only communicate
via a REST API with each other, the web-frontend does not yet know about the existence
of the integer
field type. We can add this in a similar way. Add/modify the following
files in the web-frontend part of the plugin.
plugins/my_baserow_plugin/web-frontend/fieldTypes.js
import { FieldType } from "@baserow/modules/database/fieldTypes";
import SubFormIntegerField from "@my-baserow-plugin/components/SubFormIntegerField";
import GridViewIntegerField from "@my-baserow-plugin/components/GridViewIntegerField";
import RowEditIntegerField from "@my-baserow-plugin/components/RowEditIntegerField";
export class IntegerFieldType extends FieldType {
static getType() {
return "integer";
}
getIconClass() {
return "iconoir-numbered-list-left";
}
getName() {
return "Integer";
}
getFormComponent() {
return SubFormIntegerField;
}
getGridViewFieldComponent() {
return GridViewIntegerField;
}
getRowEditFieldComponent(field) {
return RowEditIntegerField;
}
}
plugins/my_baserow_plugin/web-frontend/plugin.js
import { PluginNamePlugin } from "@my-baserow-plugin/plugins";
import { IntegerFieldType } from "@my-baserow-plugin/fieldTypes";
export default (context) => {
const { app } = context;
app.$registry.register("plugin", new PluginNamePlugin(context));
app.$registry.register("field", new IntegerFieldType(context));
};
The GridViewIntegerField component is returned by the getGridViewFieldComponent
method of the IntegerFieldType
class which means that this component is added for each
data row field that has the integer
type. For now we only add “Hello World” for
example purposes so it doesn’t actually display the number, but there are plenty of
examples in the Baserow repository in the directory
web-frontend/modules/database/components/view/grid
.
plugins/my_baserow_plugin/web-frontend/components/GridViewIntegerField.vue
<template>
<div class="grid-view__cell" :class="{ active: selected }">
<div>Hello World</div>
</div>
</template>
<script>
import gridField from "@baserow/modules/database/mixins/gridField";
export default {
mixins: [gridField],
};
</script>
The RowEditIntegerField
component is returned by the getRowEditFieldComponent
method of the IntegerFieldType
class which means that this component is added in the
popup row form. This form is shown when the expand icon on the left side of row has been
clicked by the user. For now, we only add “Hello World” for example purposes, but there
are plenty of examples in the Baserow repository in the
directory web-frontend/modules/database/components/row
.
plugins/my_baserow_plugin/web-frontend/components/RowEditIntegerField.vue
<template>
<div class="control__elements">Hello World</div>
</template>
<script>
import rowEditField from "@baserow/modules/database/mixins/rowEditField";
export default {
mixins: [rowEditField],
};
</script>
The SubFormIntegerField
component will be added to the context menu form when creating
or editing a field. If there are extra properties defined in the related model in the
backend then they can be added to the form using this component.
plugins/my_baserow_plugin/web-frontend/components/SubFormIntegerField.vue
<template>
<div>
<div class="control">
<div class="control__elements">
<Checkbox v-model="values.integer_negative"
>Allow negative</Checkbox
>
</div>
</div>
</div>
</template>
<script>
import form from "@baserow/modules/core/mixins/form";
export default {
name: "SubFormIntegerField",
mixins: [form],
data() {
return {
allowedValues: ["integer_negative"],
values: { integer_negative: false },
};
},
methods: {
isFormValid() {
return true;
},
},
};
</script>
After adding all the files you should be able to create a field with the newly created integer field type.