TornadoでPeewee ORMを使用する方法

インストール

pip install peewee  

GitHubリポジトリ

https://github.com/coleifer/peewee 

公式ドキュメント

http://docs.peewee-orm.com/en/latest/

Peeweeでモデルクラスを作成する

データベースインスタンスdbをインスタンス化し、Metaで指定します。

models/model.py

from datetime import datetime

from peewee import *
from peewee import Model

db = MySQLDatabase('inventory_db', host="localhost", port=3306, user="admin", password="secure_password")


class BaseModel(Model):
    created_at = DateTimeField(default=datetime.now, verbose_name="作成日時")
    class Meta:
        database = db

class Vendor(BaseModel):
    company_name = CharField(max_length=100, verbose_name="会社名", index=True)
    location = CharField(max_length=100, verbose_name="所在地")
    contact_number = CharField(max_length=11, verbose_name="連絡先")
    class Meta:
        database = db
        table_name = "vendor"

class Product(BaseModel):
    vendor = ForeignKeyField(Vendor, verbose_name="販売元", backref="products")
    product_name = CharField(max_length=100, verbose_name="製品名", index=True)
    view_count = IntegerField(default=0, verbose_name="閲覧数")
    stock_quantity = IntegerField(default=0, verbose_name="在庫数")
    unit_price = FloatField(default=0.0, verbose_name="単価")
    description = TextField(verbose_name="製品説明")

    class Meta:
        table_name = "product"


def initialize_database():
    db.create_tables([Product, Vendor])
    
if __name__ == "__main__":
    initialize_database()

このスクリプトを実行すると、データベースに2つのテーブルが作成されます。

追加するデータ data.py

vendor_data = [
    {
        "company_name":"楽天市場",
        "location":"東京都",
        "contact_number":"09011112222"
    },
    {
        "company_name":"ヤフーショッピング",
        "location":"大阪府",
        "contact_number":"09033334444"
    },
    {
        "company_name":"Amazon",
        "location":"神奈川県",
        "contact_number":"09055556666"
    }
]

product_data = [
    {
        "vendor":1,
        "product_name": "キリン 一番搾り 350ml缶 24本",
        "view_count": 250,
        "stock_quantity": 120,
        "unit_price": 4500,
        "description": "キリンビールの定番商品。新鮮な原料を使用した爽やかな味わいが特徴です。"
    },
    {
        "vendor":2,
        "product_name": "サントリー ザ・プレミアム・モルツ 350ml缶 24本",
        "view_count": 180,
        "stock_quantity": 80,
        "unit_price": 5800,
        "description": "サントリーの高級ビール。豊かな香りとまろやかな味わいが魅力です。"
    },
    {
        "vendor":3,
        "product_name": "アサヒ スーパードライ 350ml缶 24本",
        "view_count": 320,
        "stock_quantity": 150,
        "unit_price": 4200,
        "description": "アサヒビールの人気商品。クリーンな味わいと爽快感が特徴です。"
    },
    {
        "vendor":1,
        "product_name": "キリン 淡麗グリーンラベル 350ml缶 24本",
        "view_count": 95,
        "stock_quantity": 60,
        "unit_price": 3800,
        "description": "キリンビールのスタンダード商品。バランスの取れた味わいが魅力です。"
    },
    {
        "vendor":2,
        "product_name": "サントリー ウイスキー 角瓶 700ml",
        "view_count": 75,
        "stock_quantity": 30,
        "unit_price": 8500,
        "description": "サントリーのウイスキー。豊かな香りと深い味わいが特徴です。"
    }
]

コードを表示データベースへのデータ挿入 (peewee_test.py)

from models.model import Vendor, Product
from data import vendor_data, product_data

def save_data():
    for vendor_info in vendor_data:
        vendor = Vendor()
        vendor.company_name = vendor_info["company_name"]
        vendor.location = vendor_info["location"]
        vendor.contact_number = vendor_info["contact_number"]

        vendor.save()

    for product_info in product_data:
        product = Product(**product_info)
        product.save()

if __name__ == "__main__":
    save_data()

スクリプトを実行し、データベースを確認します。

データのクエリ (peewee_test.py)

from models.model import Vendor, Product
from data import vendor_data, product_data
def query_data():
    #単一データの取得
    #product = Product.get(Product.id==1)
    product = Product.get_by_id(1)
    product = Product[1]

    #selectはModelSelectオブジェクトを返す
    #すべてのデータを取得
    # select price from product
    products = Product.select(Product.product_name, Product.unit_price) # 遅延読み込み、イテレーション時にのみデータベースをクエリ

    # select * from product where price > 1000
    products = Product.select().where(Product.unit_price>1000)

    #select * from product where price>1000 and view_count>200
    products = Product.select().where((Product.unit_price>1000)|(Product.view_count>200))

    #select * from product where product_name like "%ラベル"
    products = Product.select().where(Product.product_name.contains("ラベル"))

    products = Product.select().where(Product.id<<[1,3])
    products = Product.select().where((Product.id==1)|(Product.id==3))
    products = Product.select().where((Product.id.in_([1,3])))


    #select * from product where price>view_count
    products = Product.select().where(Product.unit_price>Product.view_count)

    #ソート select * from product order by price desc
    products = Product.select().order_by(Product.unit_price.asc())
    products = Product.select().order_by(Product.unit_price)

    #ページネーション
    products = Product.select().order_by(Product.unit_price).paginate(2, 2)
    for product in products:
        print(product.unit_price)


if __name__ == "__main__":
    query_data()<br></br>

データの更新 (peewee_test.py)

更新時にはexecuteを呼び出して遅延読み込みを即時実行する必要があります。

from models.model import Vendor, Product
from data import vendor_data, product_data

from peewee import ModelUpdate
def update_data():
    try:
        # update view_count=100 where id =1
        Product.update(view_count=Product.view_count+1).where(Product.id==1).execute()

        product = Product.get_by_id(1)
        product.view_count += 1
        product.save()

    except Product.DoesNotExist:
        pass


if __name__ == "__main__":
    update_data()

データの削除 (peewee_test.py)

更新時と同様に、executeを呼び出して遅延読み込みを即時実行する必要があります。

from models.model import Vendor, Product
from data import vendor_data, product_data

from peewee import ModelUpdate
def delete_data():
        # 削除
        product = Product.get_by_id(1)
        product.delete_instance()
        # delete from product where price>5000
        Product.delete().where(Product.unit_price > 5000).execute()


if __name__ == "__main__":
    delete_data()

peewee-asyncをTornadoに統合する

peewee-asyncを統合する理由は、peeweeに多くの同期メソッド(例:更新、保存、削除時のexecute)が含まれているためです。これらを非同期メソッドにする必要があります。peewee-asyncはasyncioが提供するpeewee ORMの非同期インターフェースで、Tornadoで直接使用できます。

peewee-asyncのインストール

pip install --pre peewee-async 

GitHubリポジトリ

https://github.com/05bit/peewee-async

公式ドキュメントに基づいてmodels/model.pyを改写

from datetime import datetime

from peewee import *
from peewee import Model
import peewee_async

database = peewee_async.MySQLDatabase(
    'inventory_db', host="localhost", port=3306, user="admin", password="secure_password"
)

objects = peewee_async.Manager(database)

# 同期は不要になりました!


database.set_allow_sync(False)


class BaseModel(Model):
    created_at = DateTimeField(default=datetime.now, verbose_name="作成日時")
    class Meta:
        database = database

class Vendor(BaseModel):
    company_name = CharField(max_length=100, verbose_name="会社名", index=True)
    location = CharField(max_length=100, verbose_name="所在地")
    contact_number = CharField(max_length=11, verbose_name="連絡先")
    class Meta:
        database = database
        table_name = "vendor"

class Product(BaseModel):
    vendor = ForeignKeyField(Vendor, verbose_name="販売元", backref="products")
    product_name = CharField(max_length=100, verbose_name="製品名", index=True)
    view_count = IntegerField(default=0, verbose_name="閲覧数")
    stock_quantity = IntegerField(default=0, verbose_name="在庫数")
    unit_price = FloatField(default=0.0, verbose_name="単価")
    description = TextField(verbose_name="製品説明")

    class Meta:
        table_name = "product"


def initialize_database():
    db.create_tables([Product, Vendor])
if __name__ == "__main__":
    initialize_database()

peewee_async_test.py

import asyncio

from models.model import Product
from models.model import objects


async def async_handler():
    await objects.create(Product, vendor_id=1, product_name="サントリー ウイスキー 角瓶 700ml",
                         view_count=15, stock_quantity=50, unit_price=8500, description="サントリーのウイスキー。豊かな香りと深い味わいが特徴です。")
    products = await objects.execute(Product.select())
    for product in products:
        print(product.product_name)

loop = asyncio.get_event_loop()
loop.run_until_complete(async_handler())

実行結果は以下の通りです。

タグ: peewee Tornado ORM Python async

6月21日 21:08 投稿