初心者向け！Quartで始めるPython非同期Webアプリの作り方

## 第1章: Quartの概要

Quartは、Pythonの非同期Webフレームワークです。FlaskのAPIと互換性がありながら、非同期処理をサポートしています。

```python
from quart import Quart

app = Quart(__name__)

@app.route('/')
async def hello():
    return 'Hello, Quart!'

if __name__ == '__main__':
    app.run()
```

このコードは、基本的なQuartアプリケーションを作成し、ルートURLにアクセスすると「Hello, Quart!」と表示します。

## 第2章: Quartのインストールと設定

Quartをインストールするには、以下のコマンドを使用します：

```bash
pip install quart
```

プロジェクトの構造は以下のようになります：

```
myproject/
    ├── app.py
    ├── requirements.txt
    └── venv/
```

`requirements.txt`ファイルには以下を記述します：

```
quart
```

## 第3章: ルーティングとビュー

Quartでは、デコレータを使用してルートを定義します。

```python
from quart import Quart

app = Quart(__name__)

@app.route('/')
async def index():
    return 'Welcome to the homepage'

@app.route('/about')
async def about():
    return 'About us page'

if __name__ == '__main__':
    app.run()
```

このコードは、ホームページと「About」ページの2つのルートを定義しています。

## 第4章: リクエストとレスポンス

Quartでは、`request`オブジェクトを使用してリクエストデータにアクセスし、様々な形式でレスポンスを返すことができます。

```python
from quart import Quart, request, jsonify

app = Quart(__name__)

@app.route('/echo', methods=['POST'])
async def echo():
    data = await request.get_json()
    return jsonify(data)

if __name__ == '__main__':
    app.run()
```

このコードは、POSTリクエストのJSONデータをそのまま返すエコーサービスを実装しています。

## 第5章: テンプレートの使用

Quartは、Jinja2テンプレートエンジンを使用してHTMLを生成します。

```python
from quart import Quart, render_template

app = Quart(__name__)

@app.route('/hello/<name>')
async def hello(name):
    return await render_template('hello.html', name=name)

if __name__ == '__main__':
    app.run()
```

`templates/hello.html`:

```html
<!DOCTYPE html>
<html>
<head>
    <title>Hello</title>
</head>
<body>
    <h1>Hello, {{ name }}!</h1>
</body>
</html>
```

## 第6章: 静的ファイルの提供

静的ファイル（CSS、JavaScript、画像など）は`static`フォルダに配置します。

```python
from quart import Quart, send_file

app = Quart(__name__)

@app.route('/logo')
async def logo():
    return await send_file('static/logo.png')

if __name__ == '__main__':
    app.run()
```

## 第7章: データベース連携

QuartでSQLAlchemyを使用してデータベースを操作する例を示します。

```python
from quart import Quart
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

app = Quart(__name__)
engine = create_engine('sqlite:///example.db')
Session = sessionmaker(bind=engine)
Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

Base.metadata.create_all(engine)

@app.route('/user/<name>')
async def add_user(name):
    session = Session()
    new_user = User(name=name)
    session.add(new_user)
    session.commit()
    return f'User {name} added'

if __name__ == '__main__':
    app.run()
```

## 第8章: フォーム処理

Quartでフォームデータを処理する方法を示します。

```python
from quart import Quart, request, render_template

app = Quart(__name__)

@app.route('/form', methods=['GET', 'POST'])
async def form():
    if request.method == 'POST':
        name = (await request.form)['name']
        return f'Hello, {name}!'
    return await render_template('form.html')

if __name__ == '__main__':
    app.run()
```

`templates/form.html`:

```html
<!DOCTYPE html>
<html>
<body>
    <form method="post">
        <input type="text" name="name">
        <input type="submit" value="Submit">
    </form>
</body>
</html>
```

## 第9章: エラーハンドリング

Quartでのエラーハンドリングの例を示します。

```python
from quart import Quart, abort

app = Quart(__name__)

@app.route('/user/<int:user_id>')
async def get_user(user_id):
    if user_id > 100:
        abort(404)
    return f'User {user_id}'

@app.errorhandler(404)
async def not_found(error):
    return 'Page not found', 404

if __name__ == '__main__':
    app.run()
```

## 第10章: ミドルウェア

Quartでミドルウェアを使用する例を示します。

```python
from quart import Quart, request

app = Quart(__name__)

@app.before_request
async def before_request():
    print('Before request')

@app.after_request
async def after_request(response):
    print('After request')
    return response

@app.route('/')
async def index():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run()
```

## 第11章: WebSocket

QuartでWebSocketを使用する例を示します。

```python
from quart import Quart, websocket

app = Quart(__name__)

@app.websocket('/ws')
async def ws():
    while True:
        data = await websocket.receive()
        await websocket.send(f"Echo: {data}")

@app.route('/')
async def index():
    return await app.send_static_file('index.html')

if __name__ == '__main__':
    app.run()
```

`static/index.html`:

```html
<!DOCTYPE html>
<html>
<body>
    <script>
        var ws = new WebSocket('ws://' + location.host + '/ws');
        ws.onmessage = function(event) {
            console.log(event.data);
        };
        function sendMessage() {
            ws.send('Hello, WebSocket!');
        }
    </script>
    <button onclick="sendMessage()">Send Message</button>
</body>
</html>
```

## 第12章: ユニットテスト

Quartアプリケーションのユニットテストの例を示します。

```python
from quart import Quart
import pytest

app = Quart(__name__)

@app.route('/')
async def hello():
    return 'Hello, World!'

@pytest.mark.asyncio
async def test_hello():
    test_client = app.test_client()
    response = await test_client.get('/')
    assert response.status_code == 200
    assert await response.get_data(as_text=True) == 'Hello, World!'
```

## 第13章: 設定管理

Quartアプリケーションの設定を管理する方法を示します。

```python
from quart import Quart

app = Quart(__name__)

app.config.from_object('config.DevelopmentConfig')

@app.route('/')
async def index():
    return f"Database URL: {app.config['DATABASE_URL']}"

if __name__ == '__main__':
    app.run()
```

`config.py`:

```python
class Config:
    DEBUG = False
    TESTING = False
    DATABASE_URL = 'sqlite:///production.db'

class DevelopmentConfig(Config):
    DEBUG = True
    DATABASE_URL = 'sqlite:///development.db'

class TestingConfig(Config):
    TESTING = True
    DATABASE_URL = 'sqlite:///test.db'
```

## 第14章: ログイン認証

Quartでログイン認証を実装する例を示します。

```python
from quart import Quart, request, session, redirect, url_for
from werkzeug.security import generate_password_hash, check_password_hash

app = Quart(__name__)
app.secret_key = 'your-secret-key'

users = {'username': 'password'}

@app.route('/login', methods=['GET', 'POST'])
async def login():
    if request.method == 'POST':
        username = (await request.form)['username']
        password = (await request.form)['password']
        if username in users and check_password_hash(users[username], password):
            session['username'] = username
            return redirect(url_for('protected'))
    return '''
        <form method="post">
            <p><input type=text name=username>
            <p><input type=password name=password>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/protected')
async def protected():
    if 'username' not in session:
        return redirect(url_for('login'))
    return f'Logged in as {session["username"]}'

if __name__ == '__main__':
    app.run()
```

## 第15章: RESTful API

QuartでRESTful APIを実装する例を示します。

```python
from quart import Quart, jsonify, request

app = Quart(__name__)

todos = []

@app.route('/todos', methods=['GET'])
async def get_todos():
    return jsonify(todos)

@app.route('/todos', methods=['POST'])
async def create_todo():
    todo = await request.get_json()
    todos.append(todo)
    return jsonify(todo), 201

@app.route('/todos/<int:todo_id>', methods=['PUT'])
async def update_todo(todo_id):
    todo = await request.get_json()
    todos[todo_id] = todo
    return jsonify(todo)

@app.route('/todos/<int:todo_id>', methods=['DELETE'])
async def delete_todo(todo_id):
    todos.pop(todo_id)
    return '', 204

if __name__ == '__main__':
    app.run()
```

以上の15章で、Quartの基本から応用までを網羅しています。各章のコード例を実際に試してみることで、Quartの理解を深めることができるでしょう。