Skip to main content

Relations

Ratel supports four relation types, all with type-safe eager loading.

OneToMany

Parent has many children. Example: User has many Orders.

message User {
option (ratel.table) = { generate: true, table_name: "users" };
int64 id = 1 [(ratel.column) = { constraints: { primary_key: true } }];

repeated Order orders = 10 [(ratel.relation) = {
one_to_many: {
ref_name: "user_id"
on_delete_cascade: true
}
}];
}
OptionDescription
ref_nameFK column name in the child table
on_delete_cascadeAdd ON DELETE CASCADE to the FK
existed_fieldFK field already exists in child message

HasOne

Parent has exactly one child. Example: User has one Profile.

message User {
Profile profile = 11 [(ratel.relation) = {
has_one: {
ref_name: "user_id"
on_delete_cascade: true
}
}];
}

Same options as OneToMany, but loads a single record instead of a slice.

BelongsTo

Child references its parent. Example: Order belongs to User.

message Order {
option (ratel.table) = { generate: true, table_name: "orders" };
int64 user_id = 2;

User user = 10 [(ratel.relation) = {
belongs_to: {
foreign_key: "user_id"
owner_key: "id"
}
}];
}
OptionDescription
foreign_keyFK column in the current table
owner_keyPK column in the parent table (default: id)

ManyToMany

Both sides have many. Ratel auto-generates a pivot table.

message Product {
option (ratel.table) = { generate: true, table_name: "products" };

repeated Category categories = 10 [(ratel.relation) = {
many_to_many: {
ref_on_delete_cascade: true
back_ref_on_delete_cascade: true
}
}];
}
OptionDescription
ref_on_delete_cascadeCASCADE on the FK to this table
back_ref_on_delete_cascadeCASCADE on the FK to the related table
pivot_table.table_nameCustom pivot table name

Loading Relations

Relations are loaded eagerly when querying:

users, err := Users.Query(ctx, db,
Users.SelectAll().Where(Users.IsActive.Eq(true)),
)
// users[0].Orders is populated
// users[0].Profile is populated

Each scanner struct has a Relations() method that returns configured relation loaders. The executor uses these to automatically load related records after the main query.

Relation Direction Summary

RelationFK LocationResult Type
OneToManyChild table[]*ChildScanner
HasOneChild table*ChildScanner
BelongsToCurrent table*ParentScanner
ManyToManyPivot table[]*RelatedScanner