Orator ORM 的 Seeding 機制

之前寫過一篇〈初探 Orator ORM〉,有介紹了 Orator ORM 的基礎操作,而此篇文章就專門介紹 Orator ORM 的 seeding 機制,本篇的範例也都延續自〈初探 Orator ORM〉,還沒讀過的朋友請趕快手刀點擊閱讀。

阿湯哥手刀狂奔

來源:自由娛樂

Seeding

Seeding 用於在資料庫內建出一堆資料,這些資料可以是用於資料庫初始化的資料,例如郵遞區號,也可以是用於測試的假資料,而 Orator ORM 使用的是 Faker 套件當作假資料產生器。

Orator ORM 的 Seeding 操作

再次回顧一下在〈初探 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 腳本的地方,後面會再提到。

建立 Seeding 腳本

(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 則負責向工廠下單。

定義 Model Factory

建立 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 寫向工廠下單的部份。

呼叫 Model Factory

把 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

若要執行特定的 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