v/vlib/orm
2024-12-23 16:19:27 +02:00
..
orm.v orm: support plain @[serial] attribute for marking struct fields (#22814) 2024-11-09 17:26:22 +02:00
orm_create_and_drop_test.v fmt: fix alignment of struct init fields (#22025) 2024-08-11 09:11:24 +03:00
orm_custom_operators_test.v fmt: fix alignment of struct init fields (#22025) 2024-08-11 09:11:24 +03:00
orm_fk_test.v fmt: fix formating a file in an oscillating manner (fix #22223, fix #22026) (#22232) 2024-09-17 09:47:38 +03:00
orm_fn_calls_test.v fmt: fix alignment of struct init fields (#22025) 2024-08-11 09:11:24 +03:00
orm_fn_test.v fmt: fix alignment of struct init fields (#22025) 2024-08-11 09:11:24 +03:00
orm_insert_reserved_name_test.v fmt: fix and simplify align of struct fields (#21995) 2024-08-05 20:23:39 +03:00
orm_insert_test.v veb: translations via %translation_key 2024-10-20 21:35:27 +03:00
orm_interface_test.v fmt: fix and simplify align of struct fields (#21995) 2024-08-05 20:23:39 +03:00
orm_last_id_test.v fmt: fix and simplify align of struct fields (#21995) 2024-08-05 20:23:39 +03:00
orm_mut_connection_test.v orm: fix orm.Connection only allowing immutable Connections (#22684) 2024-10-28 22:29:47 +02:00
orm_mut_db_test.v fmt: fix and simplify align of struct fields (#21995) 2024-08-05 20:23:39 +03:00
orm_null_test.v all: replace fn name '@xxx' with 'xxx' (#22506) 2024-10-12 22:17:02 +03:00
orm_option_array_test.v fmt: fix alignment of struct init fields (#22025) 2024-08-11 09:11:24 +03:00
orm_option_time_test.v fmt: fix alignment of struct init fields (#22025) 2024-08-11 09:11:24 +03:00
orm_order_by_custom_field_test.v orm: fix order by with custom column name (#22813) 2024-11-09 08:24:59 +03:00
orm_references_test.v fmt: fix and simplify align of struct fields (#21995) 2024-08-05 20:23:39 +03:00
orm_result_test.v fmt: fix and simplify align of struct fields (#21995) 2024-08-05 20:23:39 +03:00
orm_serial_attribute_test.v orm: support plain @[serial] attribute for marking struct fields (#22814) 2024-11-09 17:26:22 +02:00
orm_sql_or_blocks_test.v ci: mark all orm tests as flaky (mainly to reduce the false positives on windows+MSVC, due to 'cannot open file' sqlite3.obj, when it is rebuilt in parallel) 2023-12-12 12:11:19 +02:00
orm_string_interpolation_in_where_test.v fmt: fix and simplify align of struct fields (#21995) 2024-08-05 20:23:39 +03:00
orm_sum_type_insert_test.v cgen, checker: allow using smartcasted sumtype variant values in the ORM queries (fix #23239) (#23241) 2024-12-23 16:19:27 +02:00
orm_test.v fmt: fix alignment of struct init fields (#22025) 2024-08-11 09:11:24 +03:00
README.md orm: fix list generation and escape loose backtick (#23039) 2024-12-02 00:26:16 +02:00

ORM

V has a powerful, concise ORM baked in! Create tables, insert records, manage relationships, all regardless of the DB driver you decide to use.

Nullable

For a nullable column, use an option field. If the field is non-option, the column will be defined with NOT NULL at table creation.

struct Foo {
    notnull  string
    nullable ?string
}

Attributes

Structs

  • [table: 'name'] explicitly sets the name of the table for the struct

Fields

  • [primary] sets the field as the primary key

  • [unique] gives the field a UNIQUE constraint

  • [unique: 'foo'] adds the field to a UNIQUE group

  • [skip] or [sql: '-'] field will be skipped

  • [sql: type] where type is a V type such as int or f64

  • [serial] or [sql: serial] lets the DB backend choose a column type for an auto-increment field

  • [sql: 'name'] sets a custom column name for the field

  • [sql_type: 'SQL TYPE'] explicitly sets the type in SQL

  • [default: 'raw_sql'] inserts raw_sql verbatim in a "DEFAULT" clause when creating a new table, allowing for SQL functions like CURRENT_TIME. For raw strings, surround raw_sql with backticks (`).

  • [fkey: 'parent_id'] sets foreign key for an field which holds an array

Usage

Here are a couple example structs showing most of the features outlined above.

import time

@[table: 'foos']
struct Foo {
    id          int         @[primary; sql: serial]
    name        string
    created_at  time.Time   @[default: 'CURRENT_TIME']
    updated_at  ?string     @[sql_type: 'TIMESTAMP']
    deleted_at  ?time.Time
    children    []Child     @[fkey: 'parent_id']
}

struct Child {
    id        int    @[primary; sql: serial]
    parent_id int
    name      string
}

To use the ORM, there is a special interface that lets you use the structs and V itself in queries. This interface takes the database instance as an argument.

import db.sqlite

db := sqlite.connect(':memory:')!

sql db {
    // query; see below
}!

When you need to reference the table, simply pass the struct itself.

import models.Foo

struct Bar {
    id int @[primary; sql: serial]
}

sql db {
    create table models.Foo
    create table Bar
}!

Create & Drop Tables

You can create and drop tables by passing the struct to create table and drop table.

import models.Foo

struct Bar {
    id int @[primary; sql: serial]
}

sql db {
    create table models.Foo
    drop table Bar
}!

Insert Records

To insert a record, create a struct and pass the variable to the query. Again, reference the struct as the table.

foo := Foo{
    name:       'abc'
    created_at: time.now()
    // updated_at defaults to none
    // deleted_at defaults to none
    children: [
        Child{
            name: 'abc'
        },
        Child{
            name: 'def'
        },
    ]
}

foo_id := sql db {
    insert foo into Foo
}!

If the id field is marked as sql: serial and primary, the insert expression returns the database ID of the newly added object. Getting an ID of a newly added DB row is often useful.

When inserting, [sql: serial] fields, and fields with a [default: 'raw_sql'] attribute, are not sent to the database when the value being sent is the default for the V struct field (e.g., 0 int, or an empty string). This allows the database to insert default values for auto-increment fields and where you have specified a default.

Select

You can select rows from the database by passing the struct as the table, and use V syntax and functions for expressions. Selecting returns an array of the results.

result := sql db {
    select from Foo where id == 1
}!

foo := result.first()
result := sql db {
    select from Foo where id > 1 && name != 'lasanha' limit 5
}!
result := sql db {
    select from Foo where id > 1 order by id
}!

Update

You can update fields in a row using V syntax and functions. Again, pass the struct as the table.

sql db {
    update Foo set updated_at = time.now() where name == 'abc' && updated_at is none
}!

Note that is none and !is none can be used to select for NULL fields.

Delete

You can delete rows using V syntax and functions. Again, pass the struct as the table.

sql db {
    delete from Foo where id > 10
}!

time.Time Fields

It's definitely useful to cast a field as time.Time so you can use V's built-in time functions; however, this is handled a bit differently than expected in the ORM. time.Time fields are created as integer columns in the database. Because of this, the usual time functions (current_timestamp, NOW(), etc) in SQL do not work as defaults.

Example

import db.pg

struct Member {
	id         string @[default: 'gen_random_uuid()'; primary; sql_type: 'uuid']
	name       string
	created_at string @[default: 'CURRENT_TIMESTAMP'; sql_type: 'TIMESTAMP']
}

fn main() {
	db := pg.connect(pg.Config{
		host: 'localhost'
		port: 5432
		user: 'user'
		password: 'password'
		dbname: 'dbname'
	})!

	defer {
		db.close()
	}

	sql db {
		create table Member
	}!

	new_member := Member{
		name: 'John Doe'
	}

	sql db {
		insert new_member into Member
	}!

	selected_members := sql db {
		select from Member where name == 'John Doe' limit 1
	}!
	john_doe := selected_members.first()

	sql db {
		update Member set name = 'Hitalo' where id == john_doe.id
	}!
}