# odms-odata 文档

一个轻量、现代的 Python OData V4 客户端库。

## 快速开始

```python
import asyncio
from odms_odata import ODataClient, ODataClientOptions

async def main():
    async with ODataClient(ODataClientOptions(base_url="https://...")) as client:
        response = await (
            client.for_("Products")
            .filter("Price gt 100")
            .order_by("Name")
            .top(10)
            .get_all_async()
        )
        for product in response.value:
            print(product)

asyncio.run(main())
```

## 文档目录

| 文档 | 说明 |
|------|------|
| [查询数据](querying.md) | `$filter`、`$select`、`$expand`、`$orderby`、`$skip/$top`、`$count`、`$search`、`$apply`、`$compute`、类型转换、链式执行 |
| [增删改查](crud.md) | 创建、读取、更新（PATCH）、替换（PUT）、删除、错误处理 |
| [ETag 与并发控制](etag-concurrency.md) | 基于 `If-Match` 的乐观并发与 `ODataConcurrencyError` |
| [批处理操作](batch.md) | 多操作合并请求、变更集（原子事务） |
| [函数与操作](functions-actions.md) | 绑定/非绑定 OData 函数（GET）与操作（POST） |
| [Delta 查询](delta.md) | 基于 `@odata.deltaLink` 的高效变更追踪 |
| [Singleton 实体](singletons.md) | 访问和更新单例资源（`/Me`、`/Company` 等） |
| [实体引用](references.md) | 通过 `$ref` 管理关系（添加、移除、设置、清除） |
| [异步长时操作](async-operations.md) | `Prefer: respond-async`、轮询、超时、取消 |
| [Cross-Join 查询](cross-join.md) | `$crossjoin` —— 跨实体集笛卡尔积查询 |
| [媒体实体与流](streams.md) | 通过 `$value` 上传/下载二进制内容 |
| [开放类型](open-types.md) | 使用 `ODataOpenType` 基类处理动态属性 |
| [TPH 继承](tph-inheritance.md) | 为 Table-Per-Hierarchy 层次结构自动注入 `@odata.type` |
| [服务元数据](metadata.md) | 服务文档、CSDL 元数据解析、缓存 |

## 安装

```bash
pip install odms-odata
```

本地开发（editable 模式，代码修改即时生效）：

```bash
pip install -e /path/to/odms-odata
```

## 核心概念

### ODataClient

主入口，每个服务创建一个实例并复用：

```python
client = ODataClient(ODataClientOptions(
    base_url="https://api.example.com/odata",
    timeout=60.0,
    retry_count=3,
))
# 推荐用 async with，或手动调用 await client.aclose()
```

### 查询构建器

用于构建 OData 查询的流式接口：

```python
query = (
    client.for_("Products")       # 实体集（可选传入 model=MyClass）
    .filter("Price gt 100")       # $filter
    .select("Id,Name,Price")      # $select
    .expand("Category")           # $expand
    .order_by("Name")             # $orderby
    .top(25)                      # $top
    .skip(0)                      # $skip
    .count()                      # $count=true
)

response = await client.get_async(query)      # 获取一页
response = await client.get_all_async(query)  # 获取所有页
```

### 类型化模型

将以下任意类型作为 `model` 参数传入，即可获得类型化结果：

- **Pydantic v2** `BaseModel` —— 使用 `model_validate()`
- **Pydantic v1** `BaseModel` —— 使用 `parse_obj()`
- 标准 **dataclass** —— 使用关键字构造函数
- 带有 `from_dict` 类方法的类
- `None`（默认）—— 返回普通 `dict`

### 鉴权

通过 `default_headers` 或 `configure_request` 回调注入请求头：

```python
# 静态 token
client = ODataClient(ODataClientOptions(
    base_url="https://...",
    default_headers={"Authorization": "Bearer <token>"},
))

# 动态 token（每次请求刷新）
def add_auth(headers: dict):
    headers["Authorization"] = f"Bearer {get_current_token()}"

client = ODataClient(ODataClientOptions(
    base_url="https://...",
    configure_request=add_auth,
))
```

## 参考实现

本项目对标的 C# 参考库位于：

```
doc/reference/PanoramicData.OData.Client/
```
