33
loading...
This website collects cookies to deliver better user experience
orator
可以產出 migration 與 seeding 的空白模板檔,因為有 migration 腳本,所以資料庫 schema 的變更也都可以納入版控管理。project1
├── app.db
├── oratordemo
│ ├── database.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.p
│ └── models
│ └── __init__.py
├── poetry.lock
└── pyproject.toml
cd
指令的部份。(project1) ~/project1> poetry add orator
orator
這個 CLI 程式可以用,先跑一下認識它:(porject1) ~/project1> orator
Orator 0.9.9
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose[=VERBOSE] Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
help Displays help for a command
list Lists commands
migrate Run the database migrations.
db
db:seed Seed the database with records.
make
make:migration Create a new migration file.
make:model Creates a new Model class.
make:seed Create a new seeder file.
migrate
migrate:install Create the migration repository.
migrate:refresh Reset and re-run all migrations.
migrate:reset Rollback all database migrations.
migrate:rollback Rollback the last database migration.
migrate:status Show a list of migrations up/down.
from orator import DatabaseManager, Model
DATABASES = {
'sqlite': {
'driver': 'sqlite',
'database': 'app.db',
},
}
db = DatabaseManager(DATABASES)
Model.set_connection_resolver(db)
class BaseModel(Model):
pass
(project1) ~/project1/oratordemo> orator make:model --help
Usage:
make:model [options] [--] <name>
Arguments:
name The name of the model to create.
Options:
-m, --migration Create a new migration file for the model.
-p, --path=PATH Path to models directory
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose[=VERBOSE] Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Help:
Creates a new Model class.
(project1) ~/project1/oratordemo> orator make:model User --migration
from orator import Model
class User(Model):
pass
--migration
的參數,所以會一併產生 users table 的 migration 檔,在 project1/oratordemo/migrations/yyyy_mm_dd_xxxxxx_create_users_table.py:from orator.migrations import Migration
class CreateUsersTable(Migration):
def up(self):
"""
Run the migrations.
"""
with self.schema.create('users') as table:
table.increments('id')
table.timestamps()
def down(self):
"""
Revert the migrations.
"""
self.schema.drop('users')
up()
和 down()
分別是 migration 檔案在建立與撤銷時會被呼叫的函式,這是 ORM migration 的重要特性,因為每個 migration 檔名都帶有時間戳,因此 ORM 是可以根據時間戳對資料庫的 schema 做建立與撤銷的,ORM 也會在資料庫內創建一個特殊的表格用於記錄整個資料庫的 migration 紀錄,而且這一切都是不用人為介入的。id
,第二行 table.timestamps()
會產生兩個欄位,分別是記錄創建時間的 created_at
和紀錄更新時間的 updated_at
,這三個欄位也是 ORM 的特性,也是約定優於配置的表現之一,表格間透過 id
來建構關聯性,時間戳則是在程式邏輯內操作 model 物件時會自動打上那兩個時間戳。from orator.migrations import Migration
class CreateUsersTable(Migration):
def up(self):
"""
Run the migrations.
"""
with self.schema.create('users') as table:
table.increments('id')
table.timestamps()
table.string('name')
table.integer('age').unsigned().nullable()
table.date('birthday').nullable()
def down(self):
"""
Revert the migrations.
"""
self.schema.drop('users')
(project1) ~/project1> orator migrate --config=oratordemo/database.py --path=oratordemo/migrations/
Are you sure you want to proceed with the migration? (yes/no) [no] yes
Migration table created successfully
[OK] Migrated 2021_01_28_093757_create_users_table
orator
預設會去讀取我們指定的 database.py 內的 DATABASE
,並將其認定為與資料庫的連線方式。table.date('birthday')
,而 SQLite 並不支援日期這樣的 datatype,ORM 就會自動幫我們轉換成 TEXT 的 datatype。(project1) ~/project1/oratordemo> orator make:model Item --migration
from orator.migrations import Migration
class CreateItemsTable(Migration):
def up(self):
"""
Run the migrations.
"""
with self.schema.create('items') as table:
table.increments('id')
table.timestamps()
table.integer('user_id').unsigned()
table.foreign('user_id').references('id').on('users')
table.string('title')
table.string('description').nullable()
def down(self):
"""
Revert the migrations.
"""
self.schema.drop('items')
user_id
與 users table 的 id
欄位之間的關聯性。(project1) ~/project1> orator migrate --config=oratordemo/database.py --path=oratordemo/migrations/
from orator.orm import has_many
from oratordemo.database import BaseModel
class User(BaseModel):
@has_many
def items(self):
from oratordemo.models.item import Item
return Item
has_many
,用於定義 model 間一對多的關聯性。BaseModel
, 我們定義的所有 model 都會繼承自 BaseModel
。User
類,裡面定義了與 Item
間一對多的關聯性,值得注意的是在 model 腳本內,我們只需要定義關聯性,不需要去定義其他的屬性,Orator ORM 會自己處理,這點是 Orator ORM 與其他 ORM 較不同的地方。from orator.orm import belongs_to
from oratordemo.database import BaseModel
class Item(BaseModel):
@belongs_to
def user(self):
from oratordemo.models.user import User
return User
belongs_to
,一樣用於指示 Item 與 User 間的關係。from oratordemo.models.item import Item
,不放在外面的原因是放在外面會導致循環引用的問題,所以得放在裡面。(project1) project1> python
Python 3.9.0 (default)
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from oratordemo.models.user import User
>>> from oratordemo.models.item import Item
>>> user1 = User
>>> user1.name = "John"
>>> user1.save()
true
>>> user1.id
1
user1
加個 item:>>> item1 = Item()
>>> item1.title = "Banana"
>>> user1.items().save(item1)
<oratordemo.models.item.Item object at 0x7fa94c828e20>
user1
就可以讀到剛加入的 item:>>> user1 = User.find(1)
>>> user1.items.first().title
'Banana'