Select ORMs to compare
Pick one or more ORMs from the bar above
One-to-Many (ForeignKey)
SQL
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
author_id INTEGER NOT NULL REFERENCES users(id)
);
Django ORM
from django.db import models
class User(models.Model):
name = models.CharField(max_length=255)
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="posts"
)
SQLAlchemy
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy import String, ForeignKey
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(255))
posts: Mapped[list["Post"]] = relationship(back_populates="author")
class Post(Base):
__tablename__ = "posts"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(255))
author_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
author: Mapped["User"] = relationship(back_populates="posts")
SQLModel
from sqlmodel import SQLModel, Field, Relationship
class User(SQLModel, table=True):
__tablename__ = "users"
id: int | None = Field(default=None, primary_key=True)
name: str = Field(max_length=255)
posts: list["Post"] = Relationship(back_populates="author")
class Post(SQLModel, table=True):
__tablename__ = "posts"
id: int | None = Field(default=None, primary_key=True)
title: str = Field(max_length=255)
author_id: int = Field(foreign_key="users.id")
author: User | None = Relationship(back_populates="posts")
Tortoise ORM
from tortoise.models import Model
from tortoise import fields
class User(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=255)
class Post(Model):
id = fields.IntField(pk=True)
title = fields.CharField(max_length=255)
author = fields.ForeignKeyField(
"models.User", related_name="posts"
)
Peewee
from peewee import CharField, ForeignKeyField, Model
class User(Model):
name = CharField(max_length=255)
class Meta:
table_name = "users"
class Post(Model):
title = CharField(max_length=255)
author = ForeignKeyField(User, backref="posts", column_name="author_id")
class Meta:
table_name = "posts"
Prisma
model User {
id Int @id @default(autoincrement())
name String
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
Drizzle
import { pgTable, serial, text, integer } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
});
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
authorId: integer("author_id").notNull().references(() => users.id),
});
Kysely
import { Generated } from 'kysely'
export interface UserTable {
id: Generated<number>
name: string
}
export interface PostTable {
id: Generated<number>
title: string
author_id: number
}
export interface DB {
users: UserTable
posts: PostTable
}
TypeORM
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(() => Post, (post) => post.author)
posts: Post[];
}
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@ManyToOne(() => User, (user) => user.posts)
author: User;
}
MikroORM
import {
Collection,
Entity,
ManyToOne,
OneToMany,
PrimaryKey,
Property,
} from "@mikro-orm/core";
@Entity({ tableName: "users" })
export class User {
@PrimaryKey()
id!: number;
@Property({ length: 255 })
name!: string;
@OneToMany(() => Post, (post) => post.author)
posts = new Collection<Post>(this);
}
@Entity({ tableName: "posts" })
export class Post {
@PrimaryKey()
id!: number;
@Property({ length: 255 })
title!: string;
@ManyToOne(() => User, { fieldName: "author_id" })
author!: User;
}
Sequelize
import {
CreationOptional,
DataTypes,
ForeignKey,
InferAttributes,
InferCreationAttributes,
Model,
NonAttribute,
Sequelize,
} from "sequelize";
const sequelize = new Sequelize(process.env.DATABASE_URL!, {
dialect: "postgres",
});
export class User extends Model<
InferAttributes<User>,
InferCreationAttributes<User>
> {
declare id: CreationOptional<number>;
declare name: string;
declare posts?: NonAttribute<Post[]>;
}
User.init(
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: DataTypes.STRING(255), allowNull: false },
},
{ sequelize, tableName: "users", timestamps: false },
);
export class Post extends Model<
InferAttributes<Post>,
InferCreationAttributes<Post>
> {
declare id: CreationOptional<number>;
declare title: string;
declare authorId: ForeignKey<User["id"]>;
declare author?: NonAttribute<User>;
}
Post.init(
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
title: { type: DataTypes.STRING(255), allowNull: false },
authorId: {
type: DataTypes.INTEGER,
allowNull: false,
field: "author_id",
references: { model: "users", key: "id" },
},
},
{ sequelize, tableName: "posts", timestamps: false },
);
User.hasMany(Post, { as: "posts", foreignKey: "authorId" });
Post.belongsTo(User, { as: "author", foreignKey: "authorId" });Many-to-Many
SQL
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL
);
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE
);
CREATE TABLE post_tags (
post_id INTEGER NOT NULL REFERENCES posts(id),
tag_id INTEGER NOT NULL REFERENCES tags(id),
PRIMARY KEY (post_id, tag_id)
);
Django ORM
from django.db import models
class Tag(models.Model):
name = models.CharField(max_length=100, unique=True)
class Post(models.Model):
title = models.CharField(max_length=255)
tags = models.ManyToManyField(Tag, related_name="posts")
SQLAlchemy
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy import String, ForeignKey, Column, Table
class Base(DeclarativeBase):
pass
post_tags = Table(
"post_tags",
Base.metadata,
Column("post_id", ForeignKey("posts.id"), primary_key=True),
Column("tag_id", ForeignKey("tags.id"), primary_key=True),
)
class Post(Base):
__tablename__ = "posts"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(255))
tags: Mapped[list["Tag"]] = relationship(
secondary=post_tags, back_populates="posts"
)
class Tag(Base):
__tablename__ = "tags"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(100), unique=True)
posts: Mapped[list["Post"]] = relationship(
secondary=post_tags, back_populates="tags"
)
SQLModel
from sqlmodel import SQLModel, Field, Relationship
class PostTagLink(SQLModel, table=True):
__tablename__ = "post_tags"
post_id: int = Field(foreign_key="posts.id", primary_key=True)
tag_id: int = Field(foreign_key="tags.id", primary_key=True)
class Tag(SQLModel, table=True):
__tablename__ = "tags"
id: int | None = Field(default=None, primary_key=True)
name: str = Field(unique=True, max_length=100)
posts: list["Post"] = Relationship(
back_populates="tags",
link_model=PostTagLink,
)
class Post(SQLModel, table=True):
__tablename__ = "posts"
id: int | None = Field(default=None, primary_key=True)
title: str = Field(max_length=255)
tags: list[Tag] = Relationship(
back_populates="posts",
link_model=PostTagLink,
)
Tortoise ORM
from tortoise.models import Model
from tortoise import fields
class Tag(Model):
name = fields.CharField(max_length=100, unique=True)
class Post(Model):
title = fields.CharField(max_length=255)
tags = fields.ManyToManyField(
"models.Tag", related_name="posts"
)
Peewee
from peewee import CharField, ForeignKeyField, Model
class Post(Model):
title = CharField(max_length=255)
class Meta:
table_name = "posts"
class Tag(Model):
name = CharField(max_length=100, unique=True)
class Meta:
table_name = "tags"
class PostTag(Model):
post = ForeignKeyField(Post, backref="post_tags", column_name="post_id")
tag = ForeignKeyField(Tag, backref="tag_posts", column_name="tag_id")
class Meta:
table_name = "post_tags"
indexes = ((('post', 'tag'), True),)
Prisma
model Post {
id Int @id @default(autoincrement())
title String
tags Tag[]
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}
Drizzle
import { pgTable, serial, text, integer, primaryKey } from "drizzle-orm/pg-core";
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
});
export const tags = pgTable("tags", {
id: serial("id").primaryKey(),
name: text("name").notNull().unique(),
});
export const postTags = pgTable("post_tags", {
postId: integer("post_id").notNull().references(() => posts.id),
tagId: integer("tag_id").notNull().references(() => tags.id),
}, (t) => [primaryKey({ columns: [t.postId, t.tagId] })]);
Kysely
import { Generated } from 'kysely'
export interface PostTable {
id: Generated<number>
title: string
}
export interface TagTable {
id: Generated<number>
name: string
}
export interface PostTagTable {
post_id: number
tag_id: number
}
export interface DB {
posts: PostTable
tags: TagTable
post_tags: PostTagTable
}
TypeORM
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable } from "typeorm";
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@ManyToMany(() => Tag)
@JoinTable()
tags: Tag[];
}
@Entity()
export class Tag {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
name: string;
}
MikroORM
import {
Collection,
Entity,
ManyToMany,
PrimaryKey,
Property,
} from "@mikro-orm/core";
@Entity({ tableName: "posts" })
export class Post {
@PrimaryKey()
id!: number;
@Property({ length: 255 })
title!: string;
@ManyToMany(() => Tag, (tag) => tag.posts, {
owner: true,
pivotTable: "post_tags",
joinColumn: "post_id",
inverseJoinColumn: "tag_id",
})
tags = new Collection<Tag>(this);
}
@Entity({ tableName: "tags" })
export class Tag {
@PrimaryKey()
id!: number;
@Property({ length: 100, unique: true })
name!: string;
@ManyToMany(() => Post, (post) => post.tags)
posts = new Collection<Post>(this);
}
Sequelize
import {
CreationOptional,
DataTypes,
InferAttributes,
InferCreationAttributes,
Model,
NonAttribute,
Sequelize,
} from "sequelize";
const sequelize = new Sequelize(process.env.DATABASE_URL!, {
dialect: "postgres",
});
export class Post extends Model<
InferAttributes<Post>,
InferCreationAttributes<Post>
> {
declare id: CreationOptional<number>;
declare title: string;
declare tags?: NonAttribute<Tag[]>;
}
Post.init(
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
title: { type: DataTypes.STRING(255), allowNull: false },
},
{ sequelize, tableName: "posts", timestamps: false },
);
export class Tag extends Model<
InferAttributes<Tag>,
InferCreationAttributes<Tag>
> {
declare id: CreationOptional<number>;
declare name: string;
declare posts?: NonAttribute<Post[]>;
}
Tag.init(
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: DataTypes.STRING(100), allowNull: false, unique: true },
},
{ sequelize, tableName: "tags", timestamps: false },
);
Post.belongsToMany(Tag, {
through: "post_tags",
as: "tags",
foreignKey: "post_id",
otherKey: "tag_id",
});
Tag.belongsToMany(Post, {
through: "post_tags",
as: "posts",
foreignKey: "tag_id",
otherKey: "post_id",
});One-to-One
SQL
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE profiles (
id SERIAL PRIMARY KEY,
bio TEXT NOT NULL,
user_id INTEGER NOT NULL UNIQUE REFERENCES users(id)
);
Django ORM
from django.db import models
class User(models.Model):
name = models.CharField(max_length=255)
class Profile(models.Model):
bio = models.TextField()
user = models.OneToOneField(
User, on_delete=models.CASCADE, related_name="profile"
)
SQLAlchemy
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy import String, Text, ForeignKey
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(255))
profile: Mapped["Profile"] = relationship(back_populates="user")
class Profile(Base):
__tablename__ = "profiles"
id: Mapped[int] = mapped_column(primary_key=True)
bio: Mapped[str] = mapped_column(Text)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), unique=True)
user: Mapped["User"] = relationship(back_populates="profile")
SQLModel
from typing import Optional
from sqlmodel import SQLModel, Field, Relationship
from sqlalchemy import Column, Text
class User(SQLModel, table=True):
__tablename__ = "users"
id: int | None = Field(default=None, primary_key=True)
name: str = Field(max_length=255)
profile: Optional["Profile"] = Relationship(back_populates="user")
class Profile(SQLModel, table=True):
__tablename__ = "profiles"
id: int | None = Field(default=None, primary_key=True)
bio: str = Field(sa_column=Column(Text))
user_id: int = Field(foreign_key="users.id", unique=True)
user: User | None = Relationship(back_populates="profile")
Tortoise ORM
from tortoise.models import Model
from tortoise import fields
class User(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=100)
class Profile(Model):
id = fields.IntField(pk=True)
bio = fields.TextField()
user = fields.OneToOneField(
"models.User", related_name="profile"
)
Peewee
from peewee import CharField, ForeignKeyField, Model, TextField
class User(Model):
name = CharField(max_length=255)
class Meta:
table_name = "users"
class Profile(Model):
bio = TextField()
user = ForeignKeyField(User, unique=True, backref="profile", column_name="user_id")
class Meta:
table_name = "profiles"
Prisma
model User {
id Int @id @default(autoincrement())
name String
profile Profile?
}
model Profile {
id Int @id @default(autoincrement())
bio String
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
Drizzle
import { pgTable, serial, text, integer } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
});
export const profiles = pgTable("profiles", {
id: serial("id").primaryKey(),
bio: text("bio"),
userId: integer("user_id").notNull().unique().references(() => users.id),
});
Kysely
import { Generated } from 'kysely'
export interface UserTable {
id: Generated<number>
name: string
}
export interface ProfileTable {
id: Generated<number>
bio: string
user_id: number // unique in DB schema
}
export interface DB {
users: UserTable
profiles: ProfileTable
}
TypeORM
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToOne(() => Profile, (profile) => profile.user)
profile: Profile;
}
@Entity()
export class Profile {
@PrimaryGeneratedColumn()
id: number;
@Column("text")
bio: string;
@OneToOne(() => User, (user) => user.profile)
@JoinColumn()
user: User;
}
MikroORM
import {
Entity,
OneToOne,
PrimaryKey,
Property,
} from "@mikro-orm/core";
@Entity({ tableName: "users" })
export class User {
@PrimaryKey()
id!: number;
@Property({ length: 255 })
name!: string;
@OneToOne(() => Profile, (profile) => profile.user, {
nullable: true,
})
profile?: Profile;
}
@Entity({ tableName: "profiles" })
export class Profile {
@PrimaryKey()
id!: number;
@Property({ type: "text" })
bio!: string;
@OneToOne(() => User, (user) => user.profile, {
owner: true,
fieldName: "user_id",
unique: true,
})
user!: User;
}
Sequelize
import {
CreationOptional,
DataTypes,
ForeignKey,
InferAttributes,
InferCreationAttributes,
Model,
NonAttribute,
Sequelize,
} from "sequelize";
const sequelize = new Sequelize(process.env.DATABASE_URL!, {
dialect: "postgres",
});
export class User extends Model<
InferAttributes<User>,
InferCreationAttributes<User>
> {
declare id: CreationOptional<number>;
declare name: string;
declare profile?: NonAttribute<Profile>;
}
User.init(
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: DataTypes.STRING(255), allowNull: false },
},
{ sequelize, tableName: "users", timestamps: false },
);
export class Profile extends Model<
InferAttributes<Profile>,
InferCreationAttributes<Profile>
> {
declare id: CreationOptional<number>;
declare bio: string;
declare userId: ForeignKey<User["id"]>;
declare user?: NonAttribute<User>;
}
Profile.init(
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
bio: { type: DataTypes.TEXT, allowNull: false },
userId: {
type: DataTypes.INTEGER,
allowNull: false,
unique: true,
field: "user_id",
references: { model: "users", key: "id" },
},
},
{ sequelize, tableName: "profiles", timestamps: false },
);
User.hasOne(Profile, { as: "profile", foreignKey: "userId" });
Profile.belongsTo(User, { as: "user", foreignKey: "userId" });Self-Referential
SQL
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
manager_id INTEGER REFERENCES employees(id)
);
Django ORM
from django.db import models
class Employee(models.Model):
name = models.CharField(max_length=255)
manager = models.ForeignKey(
"self",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="reports",
)
SQLAlchemy
from typing import Optional
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from sqlalchemy import String, ForeignKey
class Base(DeclarativeBase):
pass
class Employee(Base):
__tablename__ = "employees"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(255))
manager_id: Mapped[Optional[int]] = mapped_column(
ForeignKey("employees.id")
)
manager: Mapped[Optional["Employee"]] = relationship(
back_populates="reports", remote_side=[id]
)
reports: Mapped[list["Employee"]] = relationship(back_populates="manager")
SQLModel
from typing import Optional
from sqlmodel import SQLModel, Field, Relationship
class Employee(SQLModel, table=True):
__tablename__ = "employees"
id: int | None = Field(default=None, primary_key=True)
name: str = Field(max_length=255)
manager_id: int | None = Field(default=None, foreign_key="employees.id")
manager: Optional["Employee"] = Relationship(
back_populates="reports",
sa_relationship_kwargs={"remote_side": "Employee.id"},
)
reports: list["Employee"] = Relationship(back_populates="manager")
Tortoise ORM
from tortoise.models import Model
from tortoise import fields
class Employee(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=100)
manager = fields.ForeignKeyField(
"models.Employee",
related_name="reports",
null=True,
)
Peewee
from peewee import CharField, ForeignKeyField, Model
class Employee(Model):
name = CharField(max_length=255)
manager = ForeignKeyField("self", null=True, backref="reports", column_name="manager_id")
class Meta:
table_name = "employees"
Prisma
model Employee {
id Int @id @default(autoincrement())
name String
manager Employee? @relation("Management", fields: [managerId], references: [id])
managerId Int?
reports Employee[] @relation("Management")
}
Drizzle
import { pgTable, serial, text, integer } from "drizzle-orm/pg-core";
export const employees = pgTable("employees", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
managerId: integer("manager_id"),
});
Kysely
import { Generated } from 'kysely'
export interface EmployeeTable {
id: Generated<number>
name: string
manager_id: number | null
}
export interface DB {
employees: EmployeeTable
}
TypeORM
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
OneToMany,
} from "typeorm";
@Entity()
export class Employee {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@ManyToOne(() => Employee, (e) => e.reports, { nullable: true })
manager: Employee;
@OneToMany(() => Employee, (e) => e.manager)
reports: Employee[];
}
MikroORM
import {
Collection,
Entity,
ManyToOne,
OneToMany,
PrimaryKey,
Property,
} from "@mikro-orm/core";
@Entity({ tableName: "employees" })
export class Employee {
@PrimaryKey()
id!: number;
@Property({ length: 255 })
name!: string;
@ManyToOne(() => Employee, {
nullable: true,
fieldName: "manager_id",
})
manager?: Employee;
@OneToMany(() => Employee, (employee) => employee.manager)
reports = new Collection<Employee>(this);
}
Sequelize
import {
CreationOptional,
DataTypes,
ForeignKey,
InferAttributes,
InferCreationAttributes,
Model,
NonAttribute,
Sequelize,
} from "sequelize";
const sequelize = new Sequelize(process.env.DATABASE_URL!, {
dialect: "postgres",
});
export class Employee extends Model<
InferAttributes<Employee>,
InferCreationAttributes<Employee>
> {
declare id: CreationOptional<number>;
declare name: string;
declare managerId: ForeignKey<Employee["id"]> | null;
declare manager?: NonAttribute<Employee>;
declare reports?: NonAttribute<Employee[]>;
}
Employee.init(
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: DataTypes.STRING(255), allowNull: false },
managerId: {
type: DataTypes.INTEGER,
allowNull: true,
field: "manager_id",
references: { model: "employees", key: "id" },
},
},
{ sequelize, tableName: "employees", timestamps: false },
);
Employee.belongsTo(Employee, { as: "manager", foreignKey: "managerId" });
Employee.hasMany(Employee, { as: "reports", foreignKey: "managerId" });