16 KiB
Executable File
Models
A model represents a collection of structured data, usually corresponding to a single table or collection in a database.
Migrations allow you to evolve your database schema over time. Rather than write schema modifications in pure SQL, migrations allow you to describe changes to your tables. You can think of each migration as being a new "version" of the database. A schema starts off with nothing in it, and each migration modifies it to add or remove tables, columns, or entries. Strapi knows how to update your schema along this timeline, bringing it from whatever point it is in the history to the latest version.
!!! note Do not worry about how to write migrations-- Strapi does the job for you based on your model definitions. Learn more about migrations.
Basic information
Connection
The connection
key sets the the connection to use. If no connection specified, Strapi will use the defaultConnection
registered in your configuration.
In the model:
{
"connection": "default"
}
Table name
The tableName
key sets the table name to use in your database.
!!! important Never pluralize a table name-- it's a SQL convention.
In the model:
{
"tableName": "user"
}
Create a new table in a migration file:
connection.schema.createTableIfNotExists('user', function (table) {
// Some schema building here...
})
Drop an existing table in a migration file:
connection.schema.dropTableIfExists('user');
Rename an existing table in a migration file:
connection.schema.renameTable('user', 'old_user')
Options
Increments
The table.increments()
method adds an auto incrementing column, in PostgreSQL this is a serial. This will be used as the primary key for the column. Also available is a "bigIncrement" if you wish to add a "bigint" incrementing number (in PostgreSQL "bigserial").
In the model:
{
"options": {
"increments": true
}
}
In a migration file, this generates:
table.increments();
Timestamps
Adds a created_at
and updated_at
column on the database, setting these each to dateTime
types.
In the model:
{
"options": {
"timestamps": true
}
}
In a migration file, this generates:
table.timestamps();
Comment
Sets the comment for a table.
In the model:
{
"options": {
"comment": "This is my awesome table"
}
}
In a migration file, this generates:
table.comment('This is my awesome table');
Primary
Creates a compound primary key with an array of column names.
In the model:
{
"options": {
"primary": ["id", "username", "uid"]
}
}
In a migration file, this generates:
table.primary(['id', 'username', 'uid']);
Attributes
Types
For every type of column, the code to drop a column is:
table.dropColumn('columnName');
The code to rename an existing column is:
connection.schema.renameColumn('uid', 'old_uid')
String
string
column, with optional length
defaulting to 255
.
In the model:
{
"attributes": {
"username": {
"type": "string",
"length": 160
}
}
}
In a migration file, this generates:
table.string('username', 160);
Text
text
column, with optional textType
for MySQL text datatype preference.
textType
may be mediumtext
or longtext
, otherwise defaults to text
.
In the model:
{
"attributes": {
"biography": {
"type": "text",
"textType": "longtext"
}
}
}
In a migration file, this generates:
table.text('biography', 'longtext');
Integer and big integer
integer
(or bigInteger
if needed for MySQL and PostgreSQL) column.
In the model:
{
"attributes": {
"age": {
"type": "bigInteger"
}
}
}
In a migration file, this generates:
table.bigInteger('age');
If you want to have an integer even in MySQL and PostgreSQL simply use integer
instead of bigInteger
:
table.integer('age');
Float and decimal
float
or decimal
column.
decimal
and float
are both used to store numerical values. They pretty are the same but they have the following main differences:
float
is approximate-number data type, which means that not all values in the data type range can be represented exactly.decimal
is fixed-Precision data type, which means that all the values in the data type range can be represented exactly with "precision" and "scale".
Converting from decimal
to float
can cause some loss of precision.
The table.decimal()
method creates a new "decimal" column.
The table.float()
method creates a new "float" column.
They both come with an optional "precision" (defaults to 8
) and "scale" (defaults to 2
).
In the model:
{
"attributes": {
"price": {
"type": "float",
"precision": 3,
"scale": 2
}
}
}
Using the table.decimal()
method:
connection.schema.table('products', function (table) {
table.decimal('price', 2, 2);
})
Using the table.float()
method:
connection.schema.table('car', function (table) {
table.float('speed', 8, 2);
})
Date, time, date time and timestamp
date
, time
, datetime
or timestamp
column.
date
, time
, datetime
and timestamp
can be pretty confusing sometimes.
Here are the formats:
date
format: YYYY-MM-DDtime
format: HH:MI:SSdatetime
format: YYYY-MM-DD HH:MI:SStimestamp
format: YYYY-MM-DD HH:MI:SS
In the model:
{
"attributes": {
"birth": {
"type": "date"
}
}
}
Using the table.date()
method:
table.date('birth');
Using the table.datetime()
method:
connection.schema.table('articles', function (table) {
table.datetime('written_on');
})
Using the table.time()
method:
connection.schema.table('flights', function (table) {
table.time('arrived_at');
})
Using the table.timestamp()
method with a default value to now:
connection.schema.table('articles', function (table) {
table.timestamp('written_at').defaultTo(connection.fn.now());
})
Boolean
boolean
column.
In the model:
{
"attributes": {
"isActive": {
"type": "boolean",
"defaultTo": false
}
}
}
In a migration file, this generates:
table.boolean('isActive').defaultTo(false);
Binary
binary
column, with optional length
argument for MySQL.
In the model:
{
"attributes": {
"picture": {
"type": "binary",
"length": 200
}
}
}
In a migration file, this generates:
table.binary('picture', 200);
UUID
uuid
column. This uses the built-in uuid
type in PostgreSQL, and falling back to a "char(36)" in other databases.
In the model:
{
"attributes": {
"license": {
"type": "uuid"
}
}
}
In a migration file, this generates:
connection.schema.table('cars', function (table) {
table.uuid('license');
})
Enumeration
enum
column. values
and a defaultTo
value can be passed.
The table.enu()
method creates a new "enum" column (aliased to enu
, as enum
is a reserved word in JavaScript).
An "enum" is a "string" object with a value chosen from a list of permitted values that are enumerated explicitly in the column specification at table creation time.
In the model:
{
"attributes": {
"status": {
"type": "enum",
"values": [
"Open",
"Closed"
],
"defaultTo": "Open"
}
}
}
In a migration file, this generates:
connection.schema.table('articles', function (table) {
table.enu('status', ['Open', 'Closed']).defaultTo('Open');
})
JSON
json
column or uses native jsonb
type if possible, using the built-in json
type in PostgreSQL defaulting to a text
column in older versions of PostgreSQL or in unsupported databases.
In the model:
{
"attributes": {
"maps": {
"type": "json"
}
}
}
In a migration file, this generates:
connection.schema.table('maps', function (table) {
table.jsonb('location');
})
Note that when setting an "array" (or a value that could be an array) as the value, you should use JSON.stringify()
to convert your value to a "string" prior to passing it to the query builder:
strapi.connections.default.table('maps')
.where({id: 1})
.update({
location: JSON.stringify(mightBeAnArray)
});
This is because PostgreSQL has a native "array" type which uses a syntax incompatible with JSON; Strapi has no way of knowing which syntax to use, and calling JSON.stringify()
forces JSON-style syntax.
Specific type
Sets a specific type for the column creation, if you'd like to add a column type that isn't supported here.
In the model:
{
"attributes": {
"iDontHaveAnyExampleHere": {
"type": "helloUnknownType"
}
}
}
In a migration file, this generates:
connection.schema.table('articles', function (table) {
table.specificType('iDontHaveAnyExampleHere', 'helloUnknownType');
})
Validations
The following methods may be chained on the schema building methods, as modifiers to the column.
Uniqueness
Sets the column as unique.
In the model:
{
"attributes": {
"username": {
"type": "string",
"unique": true
}
}
}
In a migration file, this generates:
table.string('username').unique();
As primary key
Sets the field as the primary key for the table. To create a compound primary key, pass an array of column names in the table options
.
In the model:
{
"attributes": {
"uid": {
"type": "bigInteger",
"primary": true
}
}
}
In a migration file, this generates:
table.bigInteger('uid').primary();
Default value
Sets the default value for the column on an insert.
In the model:
{
"attributes": {
"country": {
"type": "string",
"defaultTo": "France"
}
}
}
In a migration file, this generates:
table.string('country').defaultTo('France');
Associations
Strapi handles "one-way", "one-to-one", "one-to-many", and "many-to-many" associations by defining relationships on models.
One-way
A "one-way" association is where a model is associated with another model. You could query that model and populate to get the associated model. You can't however query the associated model and populate to get the associating model.
This kind of relation is also known as "belongs to".
In this example, we are associating a User
with a Article
but not a Article
with a User
. Because we have only formed an association on one of the models, an Article
has no restrictions on the number of User
models it can belong to. If we wanted to, we could change this and associate the Article
with exactly one User
and the User
with exactly one Article
.
The User
model:
{
"tableName": "user",
"attributes": {
"article": {
"model": "article"
}
}
}
Nothing is needed in the Article
model regarding this relationship.
The migration looks like this:
connection.schema.createTableIfNotExists('user', function (table) {
table.increments();
table.integer('article').references('article.id');
})
One-to-one
A "one-to-one" association states that a model may only be associated with one other model.
This kind of relation is also known as "has one".
In this example A User
can only have one Article
and an Article
can only have one author
(User
).
The User
model:
{
"tableName": "user",
"attributes": {
"article": {
"model": "article",
"via": "author"
}
}
}
The Article
model:
{
"tableName": "article",
"attributes": {
"author": {
"model": "user"
}
}
}
The migration looks like this:
connection.schema.createTableIfNotExists('article', function (table) {
table.increments();
table.integer('author').unsigned().unique().references('user.id');
}).catch(function (err) {
console.log('Impossible to create the `article` table.');
console.log(err);
}),
connection.schema.createTableIfNotExists('user', function (table) {
table.increments();
table.integer('article').unsigned().unique().references('article.id');
}).catch(function (err) {
console.log('Impossible to create the `user` table.');
console.log(err);
})
One-to-many
A "one-to-many" association states that a model can be associated with many other models. To build this association a virtual attribute is added to a model using the collection
property. In a "one-to-many" association one side must have a collection
attribute and the other side must contain a model
attribute. This allows the many side to know which records it needs to get.
Because you may want a model to have multiple "one-to-many" associations on another model a via
key is needed on the collection
attribute. This states which model attribute on the one side of the association is used to populate the records.
This kind of relation is also known as "has many".
In this example an Article
can only have one author
(User
) and a User
can have more than one Article
.
The User
model:
{
"tableName": "user",
"attributes": {
"articles": {
"collection": "article",
"via": "author"
}
}
}
The Article
model:
{
"tableName": "article",
"attributes": {
"author": {
"model": "user"
}
}
}
The migration looks like this:
connection.schema.createTableIfNotExists('article', function (table) {
table.increments();
table.integer('author').references('user.id');
}).catch(function (err) {
console.log('Impossible to create the `article` table.');
console.log(err);
})
Many-to-one
"One-to-many" and "many-to-one" relationships are the same. We just change our point of view from the User
model or the Article
model in our examples.
This kind of relation is also known as "belongs to one".
In this example a User
can have more than one Article
and an Article
can only have one author
(User
).
The Article
model:
{
"tableName": "article",
"attributes": {
"author": {
"model": "user"
}
}
}
The User
model:
{
"tableName": "user",
"attributes": {
"articles": {
"collection": "article",
"via": "author"
}
}
}
The migration looks like this:
connection.schema.createTableIfNotExists('article', function (table) {
table.increments();
table.integer('author').references('user.id');
}).catch(function (err) {
console.log('Impossible to create the `article` table.');
console.log(err);
})
Many-to-many
A "many-to-many" association states that a model can be associated with many other models and vice-versa. Because both models can have many related models a new join table will need to be created to keep track of these relations.
Strapi will look at your models and if it finds that two models both have collection
attributes that point to each other, it will automatically build up a join table for you.
Because you may want a model to have multiple "many-to-many" associations on another model a via
key is needed on the collection attribute. This states which model attribute on the one side of the association is used to populate the records.
This kind of relation is also known as "has and belongs to many".
Using the User
and Article
example lets look at how to build a schema where a User
may have many Article
records and a Article
may have multiple owners.
The User
model:
{
"tableName": "user",
"attributes": {
"articles": {
"collection": "article",
"via": "authors"
}
}
}
The Article
model:
{
"tableName": "article",
"attributes": {
"authors": {
"collection": "user",
"via": "articles"
}
}
}
The migration looks like this:
connection.schema.createTableIfNotExists('articles_authors__users_articles', function (table) {
table.integer('user_id').unsigned().references('id').inTable('user');
table.integer('article_id').unsigned().references('id').inTable('article');
}).catch(function (err) {
console.log('Impossible to create the `articles_authors__users_articles` relation table.');
console.log(err)
})