Define a schema
You’ll write the schema the other guides use: authors, posts and comments with relations both ways, an enum, an array column, an index and a check constraint.
Prerequisites
Section titled “Prerequisites”- A project set up as in Set up a project.
1. Start with plain columns
Section titled “1. Start with plain columns”lib/models/blog.gisila.yaml:
Author: columns: name: type: varchar is_null: false email: type: varchar is_null: false is_unique: true allow_blank: false created_at: type: timestamp is_null: false default: now- Columns are nullable unless you say
is_null: false. - There’s no
id: one is added asBIGSERIAL PRIMARY KEY. default: nowbecomesDEFAULT NOW(), so inserts can leavecreated_atout.allow_blank: falseadds an empty-string check to the generatedvalidate().
Flow style (name: { type: varchar, is_null: false }) is equivalent; the rest
of this guide uses it to save space.
2. Add an enum and a model with relations
Section titled “2. Add an enum and a model with relations”enums: PostStatus: [draft, published, archived]
Post: columns: title: { type: varchar, is_null: false, max_length: 200 } body: { type: text } status: { type: PostStatus, is_null: false, default: draft } published_at: { type: timestamp } tags: { type: varchar[], is_null: false, default: '{}' } author: type: Author is_null: false on_delete: CASCADE reverse_name: postsauthor: { type: Author }is a foreign key. It’s stored asauthor_id BIGINT NOT NULLreferencingauthors(id), and the Dart field isauthorId.on_delete: CASCADEdeletes an author’s posts with the author. The default isSET NULL, which requires the column to be nullable.reverse_name: postsgeneratesAuthor.posts. Without it, you can only navigate from post to author.
3. Add comments
Section titled “3. Add comments”Comment: columns: body: { type: text, is_null: false } post: type: Post is_null: false on_delete: CASCADE reverse_name: comments author: type: Author reverse_name: comments4. Add an index and a check
Section titled “4. Add an index and a check”Add to Post:
Post: columns: # ...as above... indexes: idx_posts_author_published: columns: [author, published_at] checks: published_has_date: expression: "status <> 'published' OR published_at IS NOT NULL"Index columns use the YAML names; author becomes author_id in the SQL.
5. Generate
Section titled “5. Generate”dart run gisila_orm:generateIf the file has mistakes, nothing is written and you get every problem at once:
error[unknown_reference]: "Comment.post" references unknown model "Psot" --> lib/models/blog.gisila.yaml:31:13 |31 | type: Psot | ^^^^ did you mean "Post"?6. Look at what you got
Section titled “6. Look at what you got”import 'package:blog/models/blog.gisila.g.dart';
// ModelsPost(title: 'Hi', status: PostStatus.draft, tags: const [], authorId: 1);
// Typed columnsPostTable.status; // ColumnRef<PostStatus>PostTable.tags; // ColumnRef<List<String>>PostTable.authorId; // ColumnRef<int>
// RelationsPost.author; // BelongsToRelation<Post, Author>Author.posts; // HasManyRelation<Author, Post>Post.comments; // HasManyRelation<Post, Comment>Comment.author; // BelongsToRelation<Comment, Author>Author.comments; // HasManyRelation<Author, Comment>
// Query accessorsauthors(); posts(); comments();Splitting the schema across files
Section titled “Splitting the schema across files”As the schema grows, split it: lib/models/author.gisila.yaml,
lib/models/post.gisila.yaml and so on. Models can reference models in other
files. With more than one file the output becomes
lib/models/schema.gisila.g.dart, so update your imports.
Verify it works
Section titled “Verify it works”dart analyzedart run gisila_orm:migrate upmigrate up applies the generated migration; check the tables:
docker compose exec db psql -U postgres -d blog -c '\d posts'You should see author_id bigint not null, the status column of type
post_status, the foreign key and the published_has_date check.
Next steps
Section titled “Next steps”- Run migrations
- Work with relations
- Schema YAML format for every option.