InvenTree:使用 Python API 脚本批量创建库位
本脚本用于批量创建库位 A1..19、B1..19 和 C1..19。
你可以根据需要进行自定义。
运行前需要一个包含你的 InvenTree 实例数据的 config.yaml 文件。
inventree_create_drawers.py
#!/usr/bin/env python3
"""在 InvenTree 中的 Apothekerschrank 库位下创建 Schublade 子库位。
本脚本通过 REST API 连接到 InvenTree 实例,在一个名为
"Apothekerschrank" 的父库位下创建一组抽屉("Schublade")子库位。
对于 A、B、C 三行中的每一行,创建编号为 1 到 19 的抽屉,
共计 57 个子库位。每个子库位都会被分配 "Schublade" 库位类型。
本脚本是幂等的:如果父库位下已存在同名子库位,则会跳过
而不是重复创建。
配置从同目录下的 ``config.yaml`` 读取:
.. code-block:: yaml
inventree:
server: https://inventree.example.com
token: your-api-token-here
依赖:
- Python 3.8+
- requests
- PyYAML
用法::
python3 create_locations.py
"""
import sys
import requests
import yaml
from pathlib import Path
# YAML 配置文件路径,应与本脚本位于同一目录。
CONFIG_PATH = Path(__file__).parent / "config.yaml"
def load_config():
"""从 ``config.yaml`` 加载 InvenTree 连接设置。
返回包含 ``server`` 和 ``token`` 键的字典。
"""
with open(CONFIG_PATH, "r") as f:
return yaml.safe_load(f)["inventree"]
class InvenTreeAPI:
"""InvenTree 的最小化 REST API 客户端。
封装 :mod:`requests`,提供基于 token 的认证以及
针对 InvenTree API 的 GET 和 POST 便捷方法。
"""
def __init__(self, server, token):
"""初始化 API 客户端。
:param server: InvenTree 实例的基础 URL,例如
``https://inventree.example.com``。
:param token: API 认证 token(可在 InvenTree Web 界面的
*Settings > API Keys* 中生成)。
"""
self.server = server.rstrip("/")
self.token = token
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Token {token}",
"Content-Type": "application/json",
})
def get(self, path, params=None):
"""执行 GET 请求并返回解析后的 JSON 响应。"""
r = self.session.get(f"{self.server}{path}", params=params)
r.raise_for_status()
return r.json()
def post(self, path, data):
"""执行带 JSON body 的 POST 请求并返回解析后的响应。"""
r = self.session.post(f"{self.server}{path}", json=data)
if r.status_code == 400:
print(f" 错误 400:{r.json()}")
r.raise_for_status()
return r.json()
def find_location_type(api, name):
"""按名称查找库位类型。
通过分页 API 调用遍历所有库位类型。
找到则返回主键(``pk``),否则返回 ``None``。
"""
offset = 0
while True:
data = api.get("/api/stock/location-type/", params={"limit": 100, "offset": offset})
results = data if isinstance(data, list) else data.get("results", [])
for t in results:
if t["name"] == name:
return t["pk"]
if isinstance(data, list) or not data.get("next"):
break
offset += 100
return None
def find_location(api, name):
"""在所有库位中按名称查找库位。
通过分页 API 调用遍历所有库位。
找到则返回主键(``pk``),否则返回 ``None``。
"""
offset = 0
while True:
data = api.get("/api/stock/location/", params={"limit": 100, "offset": offset})
for loc in data["results"]:
if loc["name"] == name:
return loc["pk"]
if not data["next"]:
break
offset += 100
return None
def find_child_location(api, parent_pk, name):
"""在指定父库位下按名称查找子库位。
查询 API 中所有 ``parent`` 匹配 ``parent_pk`` 的库位,
返回第一个匹配项的 ``pk``,否则返回 ``None``。
"""
results = api.get("/api/stock/location/", params={
"parent": parent_pk, "limit": 1000
})
loc_list = results if isinstance(results, list) else results.get("results", [])
for loc in loc_list:
if loc["name"] == name:
return loc["pk"]
return None
def main():
"""主入口:查找父库位、确保库位类型存在、创建子库位。"""
config = load_config()
api = InvenTreeAPI(config["server"], config["token"])
# --- 查找或创建 "Schublade" 库位类型 ---
# InvenTree 支持用户自定义库位类型,可分配给各个库位。
# 如果该类型已存在则直接复用。
schublade_type_pk = find_location_type(api, "Schublade")
if schublade_type_pk:
print(f"已找到库位类型 'Schublade'(pk={schublade_type_pk})")
else:
result = api.post("/api/stock/location-type/", {
"name": "Schublade",
"description": "Schublade im Apothekerschrank",
"icon": "ti:box:outline",
})
schublade_type_pk = result["pk"]
print(f"已创建库位类型 'Schublade'(pk={schublade_type_pk})")
# --- 查找 "Apothekerschrank" 父库位 ---
# 该库位必须已在 InvenTree 中存在。如果找不到,
# 脚本将以错误退出。
apotheker_pk = find_location(api, "Apothekerschrank")
if not apotheker_pk:
print("错误:未找到库位 'Apothekerschrank'")
sys.exit(1)
print(f"已找到库位 'Apothekerschrank'(pk={apotheker_pk})")
# --- 创建子库位:Schublade A1..A19、B1..B19、C1..C19 ---
# 每个子库位都作为 Apothekerschrank 库位的子项创建,
# 并分配 "Schublade" 库位类型。已存在的库位会被跳过,
# 以保持脚本的幂等性。
created = 0
skipped = 0
for letter in ["A", "B", "C"]:
for number in range(1, 20):
name = f"Schublade {letter}{number}"
existing = find_child_location(api, apotheker_pk, name)
if existing:
print(f" 跳过(已存在):{name}(pk={existing})")
skipped += 1
continue
result = api.post("/api/stock/location/", {
"name": name,
"parent": apotheker_pk,
"location_type": schublade_type_pk,
})
print(f" 已创建:{name}(pk={result['pk']})")
created += 1
print(f"\n完成:已创建 {created} 个,跳过 {skipped} 个")
if __name__ == "__main__":
main()Check out similar posts by category:
InvenTree
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow