20
Orator ORM 的 Seeding 機制
之前寫過一篇〈初探 Orator ORM〉,有介紹了 Orator ORM 的基礎操作,而此篇文章就專門介紹 Orator ORM 的 seeding 機制,本篇的範例也都延續自〈初探 Orator ORM〉,還沒讀過的朋友請趕快手刀點擊閱讀。
Seeding 用於在資料庫內建出一堆資料,這些資料可以是用於資料庫初始化的資料,例如郵遞區號,也可以是用於測試的假資料,而 Orator ORM 使用的是 Faker 套件當作假資料產生器。
再次回顧一下在〈初探 Orator ORM〉文中的專案結構:
project1
├── app.db
├── oratordemo
│ ├── database.py
│ ├── __init__.py
│ ├── migrations
│ │ └── __init__.p
│ └── models
│ └── __init__.py
├── poetry.lock
└── pyproject.toml
分別有放 migration 腳本檔的 migrations/ 和放 model 腳本檔的 models/,理所當然的 seeding 腳本就會放在 seeds/ 裡面,因此產生過 seeding 腳本之後的目錄結構就會長這樣:
project1
├── app.db
├── oratordemo
│ ├── __init__.py
│ ├── database.py
│ ├── config
│ │ └── __init__.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models
│ │ └── __init__.py
│ └── seeds
│ └── __init__.py
├── poetry.lock
└── pyproject.toml
與 migrations/ 和 models/ 一樣,這個 seeds/ 不用手動建立,Orator ORM 會替我們代勞。
另一個新的 config/ 則是放 model factory 腳本的地方,後面會再提到。
(porject1) ~/project1/oratordemo> orator make:seed user_table_seeder
database_seeder created successfully.
user_table_seeder created successfully.
進去 seeds/ 看一下,注意到除了預期的 user_table_seeder.py 外,Orator ORM 還另外建了個 database_seeder.py,這個 database_seeder.py 只會建立一次,關於這個檔案的作用我們後面會再提到,先關心 user_table_seeder.py:
from orator.seeds import Seeder
class UserTableSeeder(Seeder):
def run(self):
"""
Run the database seeds.
"""
pass
裡面只有一個空的 run()
函式,這個函式就是在跑 seeding 時會被呼叫的函式,函式內放的就是建立 User 資料的敘述,但架構上我們會把實際產生假資料的邏輯抽離,在這邊我們引入 model factory 的概念,顧名思義,model factory 就是實際產生假資料的「工廠」,而 xxx_seeder.py 則負責向工廠下單。
建立 oratordemo/config/factories.py 檔案:
from orator.orm import Factory
from oratordemo.models.user import User
factory = Factory()
@factory.define(User)
def users_factory(faker):
return {
'name': faker.name(),
'age': faker.random_int(min=1, max=115),
'birthday': faker.date_of_birth(minimum_age=0, maximum_age=115).strftime('%Y-%m-%d')
}
這邊會帶入一個 Orator ORM 自行產生的 Faker 物件,而 Faker 的用法也頗直覺。Faker 可以產出的假資料有許多種類,可以閱讀 Faker 的文件了解。
有了 model factory,我們回到 seeder 寫向工廠下單的部份。
把 user_table_seeder.py 加上呼叫 model factory 的部份:
from orator.seeds import Seeder
from oratordemo.config.factories import factory
from oratordemo.models.user import User
class UserTableSeeder(Seeder):
factory = factory
def run(self):
"""
Run the database seeds.
"""
self.factory(User, 50).create()
從上面可以看出 seeder 和 model factory 的分工,model factory 負責實際的生產邏輯,而 seeder 負責向 model factory 下訂數量。
若要執行特定的 seeder,執行這樣的命令:
(project1) ~/project1> env PYTHONPATH='/home/leon/project1' orator db:seed --config=oratordemo/database.py --path=oratordemo/seeds/ --seeder=user_table_seeder
Are you sure you want to seed the database?: (yes/no) [no] yes
Database seeded!
會這麼落落長是有原因的,Orator ORM 在跑 seeder 內如果有 import
敘述會引發 ModuleNotFoundError
這個問題,因此必須多加一個環境變數讓 Python 能找到我們要 import 的模組。
若要執行一連串的 seeder,則可以把要跑的 seeder 定義在 database_seeder.py 內,這個檔案在初次建立 seeder 時會一併建立,目前還是空的,把它加上 call()
敘述來加入要跑的 seeder 們:
from orator.seeds import Seeder
from oratordemo.seeds.user_table_seeder import UserTableSeeder
class DatabaseSeeder(Seeder):
def run(self):
"""
Run the database seeds.
"""
self.call(UserTableSeeder)
如果前面跑 seeder 的命令,把指定特定 seeder 的參數拿掉,按照約定它就會去跑這支 database_seeder.py:
(project1) ~/project1> env PYTHONPATH='/home/leon/project1' orator db:seed --config=oratordemo/database.py --path=oratordemo/seeds/
Are you sure you want to seed the database?: (yes/no) [no] yes
Seeded: UserTableSeeder
Database seeded!
跑完可以去看一下資料庫,應該可以看到確實有 seeding 的資料在內。
Orator ORM 沿襲了 ActiveRecord 的約定大於配置的風格,因此和 Laravel 的 Eloquent ORM 也有著類似的使用體驗,雖然在 Python 的世界 SQLAlchemy 由於歷史因素還是有較高的使用率,但對新專案來說 Orator ORM 是一個可以被認真考慮的選項。
覺得這篇文章對您有幫助請幫我們一鍵三連,拍手、訂閱、分享,小小的鼓勵是支撐我們分享的原動力,感謝讀完本文的您,也歡迎留下意見與我們交流。
20