Peewee ― Python史上最高に美しいORM

[![Group241.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/d7bebb11-aac1-4e1e-8b5e-5a70b7a9afa6.png)](https://leapcell.io/)


**[Leapcell: 最高のサーバーレス Web ホスティング](https://leapcell.io/)**

# Peewee Query Tutorial: 軽量 ORM を用いた効率的なデータ操作

Python のデータベース開発において、オブジェクトリレーションマッピング（ORM）ライブラリは開発者にとって欠かせないツールです。軽量 ORM である Peewee は、そのシンプルさと使いやすさで際立っています。このチュートリアルでは、Peewee の照会機能を掘り下げ、Leapcell と同様のプラットフォームに展開されるユーザーサービスのコンテキストでのサンプルを提供し、SQLAlchemy と比較し、Peewee の利点を分析します。

## I. Peewee の基本的な照会操作

### (1) レコードの作成
Leapcell クラウドプラットフォームと同様のユーザーサービスで、`User` モデルを使ってユーザー情報（`username`、`email`、`plan_type`（サブスクリプションプラン）を格納するとします。Peewee を使って新しいユーザーレコードを作成するのは簡単です：

```python
from peewee import *

# デモ用に SQLite を使用；Leapcell のデプロイでは実際のデータベースに置き換える
db = SqliteDatabase('leapcell_users.db')

class User(Model):
    username = CharField()
    email = CharField(unique=True)
    plan_type = CharField()

    class Meta:
        database = db

db.connect()
db.create_tables([User])

# Model.create() を使って新しいユーザーを作成
new_user = User.create(username='test_user', email='test@example.com', plan_type='basic')
```

`User.create()` メソッドは、モデルのフィールドに一致するキーワード引数を取り、新しいレコードを挿入し、作成されたモデルインスタンスを返します。

### (2) 一括挿入
大量のデータ挿入（例えば、ユーザーを Leapcell に移行する場合）には：

```python
user_data = [
    {'username': 'user1', 'email': 'user1@example.com', 'plan_type': 'pro'},
    {'username': 'user2', 'email': 'user2@example.com', 'plan_type': 'basic'},
    {'username': 'user3', 'email': 'user3@example.com', 'plan_type': 'enterprise'}
]

# insert_many() を使って一括挿入
with db.atomic():
    User.insert_many(user_data).execute()
```

`insert_many()` は辞書のリストを受け取り、1つのデータベース操作で複数のレコードを挿入します。これは `create()` を反復するよりもはるかに効率的です。

### (3) レコードの更新
Leapcell でユーザーのサブスクリプションプランを更新するには：

```python
# 単一のユーザーを更新
user_to_update = User.get(User.username == 'test_user')
user_to_update.plan_type = 'pro'
user_to_update.save()

# 一括更新：すべての basic ユーザーを pro にアップグレード
query = User.update(plan_type='pro').where(User.plan_type == 'basic')
query.execute()
```

単一インスタンスの更新には `save()` を、一括操作には `Model.update().where()` を使います。

### (4) レコードの削除
プラットフォームからユーザーを削除するには：

```python
# 単一のユーザーを削除
user_to_delete = User.get(User.username == 'user1')
user_to_delete.delete_instance()

# 複数のユーザーを削除（例えば、非アクティブなアカウント）
query = User.delete().where(User.is_deleted == True)
query.execute()
```

個々のレコードの削除には `delete_instance()` を、条件付き削除には `Model.delete().where()` を使います。

### (5) レコードの照会
1. **単一レコード照会**：`get()` または `get_by_id()` を使って特定のユーザーを取得します：

```python
# 主キーで照会
user = User.get_by_id(1)
print(user.username, user.email, user.plan_type)

# 他のフィールドで照会
user = User.get(User.email == 'test@example.com')
```

条件に一致するレコードがない場合、これらのメソッドは `DoesNotExist` を発生させます。

2. **複数レコード照会**：`select()` を使ってレコードを取得し、反復処理します：

```python
# すべてのユーザーを反復処理
for user in User.select():
    print(user.username, user.email)

# スライシングとインデックス指定
users_subset = User.select()[:5]
for user in users_subset:
    print(user.username)
```

Peewee はデフォルトで照会結果をキャッシュします。大規模なデータセットをメモリ効率的に処理するには `Select.iterator()` を使います。

3. **レコードのフィルタリング**：Peewee は様々なフィルタリングメソッドをサポートします：

```python
# 単純なフィルタ
pro_users = User.select().where(User.plan_type == 'pro')

# ビット演算子を使った複雑な条件
active_pro_users = User.select().where((User.plan_type == 'pro') & (User.is_active == True))

# IN 照会
specific_emails = ['user1@example.com', 'user2@example.com']
matching_users = User.select().where(User.email.in_(specific_emails))
```

4. **レコードのソート**：`order_by()` を使って結果をソートします：

```python
# ユーザー名で昇順にソート
sorted_users = User.select().order_by(User.username)

# 登録時間で降順にソート（registered_at フィールドを想定）
recent_users = User.select().order_by(-User.registered_at)

# 複数フィールドでソート
multi_sorted_users = User.select().order_by(User.plan_type, User.username)
```

5. **ページネーションとカウント**：大規模なデータセットを処理する際に必須です：

```python
# 結果をページング（2ページ目、1ページに10レコード）
paged_users = User.select().order_by(User.id).paginate(2, 10)
for user in paged_users:
    print(user.username)

# ユーザーの合計数をカウント
user_count = User.select().count()
print(f"Total users: {user_count}")

# プランタイプごとのユーザー数をカウント
pro_user_count = User.select().where(User.plan_type == 'pro').count()
```

6. **集計とスカラー照会**：集計関数を使ってデータを分析します：

```python
from peewee import fn

# プランタイプごとのユーザー数をカウント
query = (User
        .select(User.plan_type, fn.Count(User.id).alias('count'))
        .group_by(User.plan_type))

for result in query:
    print(result.plan_type, result.count)

# スカラー値を取得（例えば、最大ユーザー ID）
max_id = User.select(fn.Max(User.id)).scalar()
```

7. **ウィンドウ関数**：結果セット全体で計算を実行します：

```python
from peewee import Window, fn

# 各プランタイプ内での登録順位を計算
query = User.select(
    User.username,
    User.plan_type,
    fn.RANK().over(
        order_by=[User.registered_at],
        partition_by=[User.plan_type]
    ).alias('registration_rank')
)

for user in query:
    print(user.username, user.plan_type, user.registration_rank)
```

8. **ウィンドウ定義の再利用**：ウィンドウオブジェクトを使って複雑な照会を単純化します：

```python
# 再利用可能なウィンドウ定義
win = Window(order_by=[User.registered_at], partition_by=[User.plan_type])
query = User.select(
    User.username,
    User.plan_type,
    fn.RANK().over(win).alias('rank1'),
    fn.DENSE_RANK().over(win).alias('rank2')
).window(win)

# 複数のウィンドウ定義
win1 = Window(order_by=[User.registered_at]).alias('win1')
win2 = Window(partition_by=[User.plan_type]).alias('win2')
query = User.select(
    User.username,
    User.plan_type,
    fn.SUM(User.login_count).over(win1).alias('total_logins'),
    fn.AVG(User.login_count).over(win2).alias('avg_logins')
).window(win1, win2)
```

9. **フレームタイプ：RANGE vs ROWS vs GROUPS**：ウィンドウ関数の計算を制御します：

```python
# ROWS フレームタイプの例
class Sample(Model):
    counter = IntegerField()
    value = FloatField()

    class Meta:
        database = db

db.create_tables([Sample])

# テストデータを挿入
data = [(1, 10), (1, 20), (2, 1), (2, 3), (3, 100)]
Sample.insert_many(data, fields=[Sample.counter, Sample.value]).execute()

# ROWS フレームで累計合計を計算
query = Sample.select(
    Sample.counter,
    Sample.value,
    fn.SUM(Sample.value).over(
        order_by=[Sample.id],
        frame_type=Window.ROWS
    ).alias('rsum')
)

for sample in query:
    print(sample.counter, sample.value, sample.rsum)
```

10. **タプルまたは辞書としてデータを取得**：効率化のためにモデルのインスタンス化をスキップします：

```python
# 結果を辞書として返す
query = User.select(User.username, User.plan_type).dicts()
for user_dict in query:
    print(user_dict['username'], user_dict['plan_type'])

# 結果をタプルとして返す
query = User.select(User.username, User.plan_type).tuples()
for user_tuple in query:
    print(user_tuple[0], user_tuple[1])
```

11. **RETURNING 句**：修正されたデータを取得（PostgreSQL）：

```python
from peewee import PostgresqlDatabase

# PostgreSQL 接続
db = PostgresqlDatabase('leapcell_db', user='user', password='password', host='localhost', port=5432)

class User(Model):
    username = CharField()
    email = CharField(unique=True)
    plan_type = CharField()

    class Meta:
        database = db

# RETURNING を使って更新
query = (User
        .update(plan_type='enterprise')
        .where(User.username == 'test_user')
        .returning(User))

for updated_user in query.execute():
    print(updated_user.username, updated_user.plan_type)
```

12. **共通テーブル式（CTEs）**：複雑な照会を単純化します：

```python
from peewee import CTE

class UserActivity(Model):
    user = ForeignKeyField(User)
    action_time = DateTimeField()

    class Meta:
        database = db

db.create_tables([UserActivity])

# 平均アクティビティ間隔を計算する CTE
cte = (UserActivity
       .select(UserActivity.user,
                fn.AVG(fn.JULIANDAY(UserActivity.action_time) - fn.JULIANDAY(fn.LAG(UserActivity.action_time).over(order_by=[UserActivity.user, UserActivity.action_time]))).alias('avg_interval'))
       .group_by(UserActivity.user)
       .cte('user_activity_intervals'))

# 平均間隔が1日未満のユーザーを照会
query = (User
        .select(User.username)
        .join(cte, on=(User.id == cte.c.user))
        .where(cte.c.avg_interval < 1)
        .with_cte(cte))

for user in query:
    print(user.username)
```

## II. Peewee と SQLAlchemy の比較

### (1) 学習曲線
Peewee の API は直感的で初心者に優しく、照会構文は自然言語に似ています。SQLAlchemy は機能豊富ですが、その複雑さにより学習曲線が急峻で、特に高度なマッピングとトランザクション管理です。

### (2) 性能
単純な照会では、両方の ORM は同様の性能を発揮します。ただし、Peewee の軽量な設計と簡潔な SQL 生成により、大規模なデータセットを含む複雑な操作で優位になります。SQLAlchemy の広範な機能セットによるオーバーヘッドは、特定のシナリオで性能に影響を与える可能性があります。

### (3) ユースケース
Peewee は、迅速なプロトタイピングと軽量なアプリケーションで優れており、シンプルさとスピードが優先される場合に適しています。Leapcell のようなプラットフォームのモジュールに最適です。SQLAlchemy は、複雑な関係と高度なトランザクション要件を持つ大規模なエンタープライズアプリケーションにより適しています。

## III. 結論
Peewee はデータベース操作にストリームライン化されたアプローチを提供しており、効率性と使いやすさを重視する開発者にとって最適な選択肢となります。このチュートリアルでは、Leapcell のユーザーサービスエコシステムに触発された例を通じて、CRUD 操作、集計、および高度な照会における Peewee の機能を実証しました。SQLAlchemy と比較すると、Peewee は学習曲線が低く、パフォーマンスに優れ、迅速な開発に適している点で際立っています。アジリティと軽量設計が重要なプロジェクトでは、Peewee を選択してください。

# Peewee Query Tutorial: 効率的な Python ORM

## はじめに
Python のデータベース開発において、オブジェクトリレーションマッピング（ORM）ツールはデータベースとのやり取りを簡素化します。軽量でありながら強力な ORM である Peewee は、開発者にエレガントで効率的な方法でデータベースを照会する手段を提供します。ローカルで展開されようと、Leapcell のようなクラウドプラットフォームで展開されようと、Peewee は卓越したパフォーマンスを発揮します。この記事では、Peewee の照会機能を探り、SQLAlchemy との比較を行い、Leapcell のエコシステムからの実践的な例を通じてその利点を示します。

## Peewee の基本的な照会操作

### レコードの作成
Leapcell プラットフォームでユーザーデータを管理するには、モデルを定義する必要があります：

```python
from peewee import *

# データベースに接続（Leapcell では設定が自動検出可能）
db = SqliteDatabase('leapcell_users.db')

class BaseModel(Model):
    class Meta:
        database = db

class User(BaseModel):
    username = CharField(unique=True)
    email = CharField()
    created_at = DateTimeField(default=datetime.datetime.now)
    is_active = BooleanField(default=True)

class Service(BaseModel):
    name = CharField()
    user = ForeignKeyField(User, backref='services')
    status = CharField(default='pending')
    deployed_at = DateTimeField(null=True)
```

Leapcell で新しいユーザーを作成するには：

```python
# Leapcell でユーザーを作成
try:
    user = User.create(username='alice', email='alice@example.com')
    print(f"User {user.username} created successfully")
except IntegrityError:
    print("Username already exists")
```

### 一括挿入
複数のユーザーを Leapcell にインポートする：

```python
# Leapcell API からの大量ユーザーデータをシミュレート
users_data = [
    {'username': 'bob', 'email': 'bob@example.com'},
    {'username': 'charlie', 'email': 'charlie@example.com'},
    {'username': 'david', 'email': 'david@example.com'}
]

# Leapcell のデータベースに一括挿入
with db.atomic():
    User.insert_many(users_data).execute()
```

### レコードの更新
Leapcell でサービスステータスを更新する：

```python
# Leapcell でサービスステータスを更新
service = Service.get(Service.name == 'web-app' and Service.user == user)
service.status = 'running'
service.deployed_at = datetime.datetime.now()
service.save()
```

### レコードの削除
Leapcell でサービスを削除する：

```python
# Leapcell でサービスを削除
service = Service.get(Service.name == 'old-service' and Service.user == user)
service.delete_instance()
```

### レコードの照会
Leapcell でユーザーとそのサービスを取得する：

```python
# Leapcell でアクティブなユーザーとその実行中のサービスを照会
query = (User
        .select(User, Service)
        .join(Service, JOIN.LEFT_OUTER)
        .where(User.is_active == True)
        .order_by(User.username))

for user in query:
    print(f"User: {user.username}")
    if user.services:
        for service in user.services:
            print(f"  Service: {service.name} ({service.status})")
    else:
        print("  No services")
```

## Peewee の高度な照会技術

### 集計照会
Leapcell でユーザーごとのサービス数をカウントする：

```python
# Leapcell でユーザーごとのサービス数をカウント
query = (User
        .select(User, fn.Count(Service.id).alias('service_count'))
        .join(Service, JOIN.LEFT_OUTER)
        .group_by(User)
        .order_by(SQL('service_count').desc()))

for user in query:
    print(f"User: {user.username}, Services: {user.service_count}")
```

### ウィンドウ関数
Leapcell でデプロイ順序を分析する：

```python
# ウィンドウ関数を使ってデプロイ順序を分析
query = (Service
        .select(
            Service.name,
            Service.deployed_at,
            fn.RANK().over(order_by=[Service.deployed_at.desc()]).alias('deploy_rank')
        )
        .where(Service.deployed_at.is_null(False)))

for service in query:
    print(f"Service: {service.name}, Deployment Rank: {service.deploy_rank}")
```

### 共通テーブル式
Leapcell で CTE を使った複雑な照会：

```python
# 過去30日間のアクティブなユーザーとそのサービスを照会
active_users_cte = (User
                   .select(User.id)
                   .where(User.last_login > datetime.datetime.now() - datetime.timedelta(days=30))
                   .cte('active_users'))

query = (Service
        .select(Service, User.username)
        .join(User)
        .with_cte(active_users_cte)
        .where(User.id == active_users_cte.c.id))

for service in query:
    print(f"Active User {service.user.username}'s Service: {service.name}")
```

## なぜ Peewee が ORM として輝くのか

### 軽量設計
Peewee の最小限のアーキテクチャはオーバーヘッドを削減し、Leapcell のようなクラウドプラットフォームに理想的です。そのシンプルさは、SQLAlchemy のようなより重い ORM と比較して、開発とデプロイを高速化します。

### パフォーマンスの優位性
Peewee は効率的な SQL を生成し、高ボリュームの操作で卓越します。例えば、その一括挿入機能は SQLAlchemy のデフォルトアプローチを上回り、Leapcell でのアプリケーションのスケーリングに不可欠です。

### 柔軟なデータベースサポート
Peewee は SQLite、PostgreSQL、MySQL などをシームレスにサポートします。この柔軟性により、一貫した API を維持しながら、Leapcell の多様なデータベースオファリングとの互換性が保証されます。

### 直感的な API
Peewee の Python らしい構文は開発者の期待に沿っており、ボイラープレートコードを削減します。このシンプルさは Leapcell での開発サイクルを加速し、チームが ORM の複雑さではなく機能に集中することを可能にします。

## 結論
Peewee は、シンプルさ、パフォーマンス、柔軟性を兼ね備えた多目的な ORM であり、これらの品質は Leapcell のようなクラウドベースのサービスに不可欠です。実践的な例を通じて、このチュートリアルは基本的なシナリオと高度なシナリオの両方での Peewee の機能を実証しました。SQLAlchemy と比較すると、Peewee は速度、スケーラビリティ、および開発者の生産性を優先するプロジェクトの好ましい選択肢として浮上します。

# [Leapcell: 最高のサーバーレス Web ホスティング](https://leapcell.io/)

最後に、Python サービスをデプロイするための完璧なプラットフォームをお勧めします：**[Leapcell](https://leapcell.io/)**

[![brandpic7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/423a343f-8bce-4fb9-bb73-6166de094169.png)](https://leapcell.io/)


### 🚀 好きな言語で構築  
JavaScript、Python、Go、または Rust で簡単に開発します。  

### 🌍 無制限のプロジェクトを無料でデプロイ  
使用した分だけ支払う — リクエストがなければ料金は発生しません。  

### ⚡ 従量制課金、隠れたコストは一切ありません  
アイドル料金はなく、シームレスなスケーラビリティが提供されます。  

[![Frame3-withpadding2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/8a8a51ae-2882-43d3-988d-cc3de447baee.png)](https://leapcell.io/)


[📖 ドキュメントを探索する](https://docs.leapcell.io/)  

🔹 Twitter でフォロー：[@LeapcellHQ](https://x.com/LeapcellHQ)