claudient 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/.claude-plugin/plugin.json +42 -0
  2. package/CONTEXT.md +58 -0
  3. package/README.md +165 -0
  4. package/agents/build-resolvers/de/python-resolver.md +64 -0
  5. package/agents/build-resolvers/de/typescript-resolver.md +65 -0
  6. package/agents/build-resolvers/es/python-resolver.md +64 -0
  7. package/agents/build-resolvers/es/typescript-resolver.md +65 -0
  8. package/agents/build-resolvers/fr/python-resolver.md +64 -0
  9. package/agents/build-resolvers/fr/typescript-resolver.md +65 -0
  10. package/agents/build-resolvers/nl/python-resolver.md +64 -0
  11. package/agents/build-resolvers/nl/typescript-resolver.md +65 -0
  12. package/agents/build-resolvers/python-resolver.md +62 -0
  13. package/agents/build-resolvers/typescript-resolver.md +63 -0
  14. package/agents/core/architect.md +64 -0
  15. package/agents/core/code-reviewer.md +78 -0
  16. package/agents/core/de/architect.md +66 -0
  17. package/agents/core/de/code-reviewer.md +80 -0
  18. package/agents/core/de/planner.md +63 -0
  19. package/agents/core/de/security-reviewer.md +93 -0
  20. package/agents/core/es/architect.md +66 -0
  21. package/agents/core/es/code-reviewer.md +80 -0
  22. package/agents/core/es/planner.md +63 -0
  23. package/agents/core/es/security-reviewer.md +93 -0
  24. package/agents/core/fr/architect.md +66 -0
  25. package/agents/core/fr/code-reviewer.md +80 -0
  26. package/agents/core/fr/planner.md +63 -0
  27. package/agents/core/fr/security-reviewer.md +93 -0
  28. package/agents/core/nl/architect.md +66 -0
  29. package/agents/core/nl/code-reviewer.md +80 -0
  30. package/agents/core/nl/planner.md +63 -0
  31. package/agents/core/nl/security-reviewer.md +93 -0
  32. package/agents/core/planner.md +61 -0
  33. package/agents/core/security-reviewer.md +91 -0
  34. package/guides/agent-orchestration.md +231 -0
  35. package/guides/de/agent-orchestration.md +174 -0
  36. package/guides/de/getting-started.md +164 -0
  37. package/guides/de/hooks-cookbook.md +160 -0
  38. package/guides/de/memory-management.md +153 -0
  39. package/guides/de/security.md +180 -0
  40. package/guides/de/skill-authoring.md +214 -0
  41. package/guides/de/token-optimization.md +156 -0
  42. package/guides/es/agent-orchestration.md +174 -0
  43. package/guides/es/getting-started.md +164 -0
  44. package/guides/es/hooks-cookbook.md +160 -0
  45. package/guides/es/memory-management.md +153 -0
  46. package/guides/es/security.md +180 -0
  47. package/guides/es/skill-authoring.md +214 -0
  48. package/guides/es/token-optimization.md +156 -0
  49. package/guides/fr/agent-orchestration.md +174 -0
  50. package/guides/fr/getting-started.md +164 -0
  51. package/guides/fr/hooks-cookbook.md +227 -0
  52. package/guides/fr/memory-management.md +169 -0
  53. package/guides/fr/security.md +180 -0
  54. package/guides/fr/skill-authoring.md +214 -0
  55. package/guides/fr/token-optimization.md +158 -0
  56. package/guides/getting-started.md +164 -0
  57. package/guides/hooks-cookbook.md +423 -0
  58. package/guides/memory-management.md +192 -0
  59. package/guides/nl/agent-orchestration.md +174 -0
  60. package/guides/nl/getting-started.md +164 -0
  61. package/guides/nl/hooks-cookbook.md +160 -0
  62. package/guides/nl/memory-management.md +153 -0
  63. package/guides/nl/security.md +180 -0
  64. package/guides/nl/skill-authoring.md +214 -0
  65. package/guides/nl/token-optimization.md +156 -0
  66. package/guides/security.md +229 -0
  67. package/guides/skill-authoring.md +226 -0
  68. package/guides/token-optimization.md +169 -0
  69. package/hooks/lifecycle/cost-tracker.md +49 -0
  70. package/hooks/lifecycle/cost-tracker.sh +59 -0
  71. package/hooks/lifecycle/pre-compact-save.md +56 -0
  72. package/hooks/lifecycle/pre-compact-save.sh +37 -0
  73. package/hooks/lifecycle/session-start.md +50 -0
  74. package/hooks/lifecycle/session-start.sh +47 -0
  75. package/hooks/post-tool-use/audit-log.md +53 -0
  76. package/hooks/post-tool-use/audit-log.sh +53 -0
  77. package/hooks/post-tool-use/prettier.md +53 -0
  78. package/hooks/post-tool-use/prettier.sh +49 -0
  79. package/hooks/pre-tool-use/block-dangerous.md +48 -0
  80. package/hooks/pre-tool-use/block-dangerous.sh +76 -0
  81. package/hooks/pre-tool-use/git-push-confirm.md +46 -0
  82. package/hooks/pre-tool-use/git-push-confirm.sh +36 -0
  83. package/mcp/configs/github.json +11 -0
  84. package/mcp/configs/postgres.json +11 -0
  85. package/mcp/de/recommended-servers.md +170 -0
  86. package/mcp/es/recommended-servers.md +170 -0
  87. package/mcp/fr/recommended-servers.md +170 -0
  88. package/mcp/nl/recommended-servers.md +170 -0
  89. package/mcp/recommended-servers.md +168 -0
  90. package/package.json +45 -0
  91. package/prompts/project-starters/de/fastapi-project.md +62 -0
  92. package/prompts/project-starters/de/nextjs-project.md +82 -0
  93. package/prompts/project-starters/es/fastapi-project.md +62 -0
  94. package/prompts/project-starters/es/nextjs-project.md +82 -0
  95. package/prompts/project-starters/fastapi-project.md +60 -0
  96. package/prompts/project-starters/fr/fastapi-project.md +62 -0
  97. package/prompts/project-starters/fr/nextjs-project.md +82 -0
  98. package/prompts/project-starters/nextjs-project.md +80 -0
  99. package/prompts/project-starters/nl/fastapi-project.md +62 -0
  100. package/prompts/project-starters/nl/nextjs-project.md +82 -0
  101. package/prompts/system-prompts/ai-product.md +80 -0
  102. package/prompts/system-prompts/data-pipeline.md +76 -0
  103. package/prompts/system-prompts/de/ai-product.md +82 -0
  104. package/prompts/system-prompts/de/data-pipeline.md +78 -0
  105. package/prompts/system-prompts/de/saas-backend.md +71 -0
  106. package/prompts/system-prompts/es/ai-product.md +82 -0
  107. package/prompts/system-prompts/es/data-pipeline.md +78 -0
  108. package/prompts/system-prompts/es/saas-backend.md +71 -0
  109. package/prompts/system-prompts/fr/ai-product.md +82 -0
  110. package/prompts/system-prompts/fr/data-pipeline.md +78 -0
  111. package/prompts/system-prompts/fr/saas-backend.md +71 -0
  112. package/prompts/system-prompts/nl/ai-product.md +82 -0
  113. package/prompts/system-prompts/nl/data-pipeline.md +78 -0
  114. package/prompts/system-prompts/nl/saas-backend.md +71 -0
  115. package/prompts/system-prompts/saas-backend.md +69 -0
  116. package/prompts/task-specific/changelog.md +81 -0
  117. package/prompts/task-specific/de/changelog.md +83 -0
  118. package/prompts/task-specific/de/debugging.md +78 -0
  119. package/prompts/task-specific/de/pr-description.md +69 -0
  120. package/prompts/task-specific/debugging.md +76 -0
  121. package/prompts/task-specific/es/changelog.md +83 -0
  122. package/prompts/task-specific/es/debugging.md +78 -0
  123. package/prompts/task-specific/es/pr-description.md +69 -0
  124. package/prompts/task-specific/fr/changelog.md +83 -0
  125. package/prompts/task-specific/fr/debugging.md +78 -0
  126. package/prompts/task-specific/fr/pr-description.md +69 -0
  127. package/prompts/task-specific/nl/changelog.md +83 -0
  128. package/prompts/task-specific/nl/debugging.md +78 -0
  129. package/prompts/task-specific/nl/pr-description.md +69 -0
  130. package/prompts/task-specific/pr-description.md +67 -0
  131. package/rules/common/coding-style.md +45 -0
  132. package/rules/common/de/coding-style.md +47 -0
  133. package/rules/common/de/git.md +48 -0
  134. package/rules/common/de/performance.md +40 -0
  135. package/rules/common/de/security.md +45 -0
  136. package/rules/common/de/testing.md +45 -0
  137. package/rules/common/es/coding-style.md +47 -0
  138. package/rules/common/es/git.md +48 -0
  139. package/rules/common/es/performance.md +40 -0
  140. package/rules/common/es/security.md +45 -0
  141. package/rules/common/es/testing.md +45 -0
  142. package/rules/common/fr/coding-style.md +47 -0
  143. package/rules/common/fr/git.md +48 -0
  144. package/rules/common/fr/performance.md +40 -0
  145. package/rules/common/fr/security.md +45 -0
  146. package/rules/common/fr/testing.md +45 -0
  147. package/rules/common/git.md +46 -0
  148. package/rules/common/nl/coding-style.md +47 -0
  149. package/rules/common/nl/git.md +48 -0
  150. package/rules/common/nl/performance.md +40 -0
  151. package/rules/common/nl/security.md +45 -0
  152. package/rules/common/nl/testing.md +45 -0
  153. package/rules/common/performance.md +38 -0
  154. package/rules/common/security.md +43 -0
  155. package/rules/common/testing.md +43 -0
  156. package/rules/language-specific/de/go.md +48 -0
  157. package/rules/language-specific/de/python.md +38 -0
  158. package/rules/language-specific/de/typescript.md +51 -0
  159. package/rules/language-specific/es/go.md +48 -0
  160. package/rules/language-specific/es/python.md +38 -0
  161. package/rules/language-specific/es/typescript.md +51 -0
  162. package/rules/language-specific/fr/go.md +48 -0
  163. package/rules/language-specific/fr/python.md +38 -0
  164. package/rules/language-specific/fr/typescript.md +51 -0
  165. package/rules/language-specific/go.md +46 -0
  166. package/rules/language-specific/nl/go.md +48 -0
  167. package/rules/language-specific/nl/python.md +38 -0
  168. package/rules/language-specific/nl/typescript.md +51 -0
  169. package/rules/language-specific/python.md +36 -0
  170. package/rules/language-specific/typescript.md +49 -0
  171. package/scripts/cli.js +161 -0
  172. package/scripts/link-skills.sh +35 -0
  173. package/scripts/list-skills.sh +34 -0
  174. package/skills/ai-engineering/agent-construction.md +285 -0
  175. package/skills/ai-engineering/claude-api.md +248 -0
  176. package/skills/ai-engineering/de/agent-construction.md +287 -0
  177. package/skills/ai-engineering/de/claude-api.md +250 -0
  178. package/skills/ai-engineering/es/agent-construction.md +287 -0
  179. package/skills/ai-engineering/es/claude-api.md +250 -0
  180. package/skills/ai-engineering/fr/agent-construction.md +287 -0
  181. package/skills/ai-engineering/fr/claude-api.md +250 -0
  182. package/skills/ai-engineering/nl/agent-construction.md +287 -0
  183. package/skills/ai-engineering/nl/claude-api.md +250 -0
  184. package/skills/backend/dotnet/csharp.md +304 -0
  185. package/skills/backend/dotnet/de/csharp.md +306 -0
  186. package/skills/backend/dotnet/es/csharp.md +306 -0
  187. package/skills/backend/dotnet/fr/csharp.md +306 -0
  188. package/skills/backend/dotnet/nl/csharp.md +306 -0
  189. package/skills/backend/go/de/go.md +307 -0
  190. package/skills/backend/go/es/go.md +307 -0
  191. package/skills/backend/go/fr/go.md +307 -0
  192. package/skills/backend/go/go.md +305 -0
  193. package/skills/backend/go/nl/go.md +307 -0
  194. package/skills/backend/nodejs/de/nestjs.md +274 -0
  195. package/skills/backend/nodejs/de/nextjs.md +222 -0
  196. package/skills/backend/nodejs/es/nestjs.md +274 -0
  197. package/skills/backend/nodejs/es/nextjs.md +222 -0
  198. package/skills/backend/nodejs/fr/nestjs.md +274 -0
  199. package/skills/backend/nodejs/fr/nextjs.md +222 -0
  200. package/skills/backend/nodejs/nestjs.md +272 -0
  201. package/skills/backend/nodejs/nextjs.md +220 -0
  202. package/skills/backend/nodejs/nl/nestjs.md +274 -0
  203. package/skills/backend/nodejs/nl/nextjs.md +222 -0
  204. package/skills/backend/python/de/django.md +285 -0
  205. package/skills/backend/python/de/fastapi.md +244 -0
  206. package/skills/backend/python/django.md +283 -0
  207. package/skills/backend/python/es/django.md +285 -0
  208. package/skills/backend/python/es/fastapi.md +244 -0
  209. package/skills/backend/python/fastapi.md +242 -0
  210. package/skills/backend/python/fr/django.md +285 -0
  211. package/skills/backend/python/fr/fastapi.md +244 -0
  212. package/skills/backend/python/nl/django.md +285 -0
  213. package/skills/backend/python/nl/fastapi.md +244 -0
  214. package/skills/data-ml/dbt-data-pipelines.md +155 -0
  215. package/skills/data-ml/de/dbt-data-pipelines.md +157 -0
  216. package/skills/data-ml/de/pandas-polars.md +147 -0
  217. package/skills/data-ml/de/pytorch-tensorflow.md +171 -0
  218. package/skills/data-ml/es/dbt-data-pipelines.md +157 -0
  219. package/skills/data-ml/es/pandas-polars.md +147 -0
  220. package/skills/data-ml/es/pytorch-tensorflow.md +171 -0
  221. package/skills/data-ml/fr/dbt-data-pipelines.md +157 -0
  222. package/skills/data-ml/fr/pandas-polars.md +147 -0
  223. package/skills/data-ml/fr/pytorch-tensorflow.md +171 -0
  224. package/skills/data-ml/nl/dbt-data-pipelines.md +157 -0
  225. package/skills/data-ml/nl/pandas-polars.md +147 -0
  226. package/skills/data-ml/nl/pytorch-tensorflow.md +171 -0
  227. package/skills/data-ml/pandas-polars.md +145 -0
  228. package/skills/data-ml/pytorch-tensorflow.md +169 -0
  229. package/skills/database/de/graphql.md +181 -0
  230. package/skills/database/es/graphql.md +181 -0
  231. package/skills/database/fr/graphql.md +181 -0
  232. package/skills/database/graphql.md +179 -0
  233. package/skills/database/nl/graphql.md +181 -0
  234. package/skills/devops-infra/de/docker.md +133 -0
  235. package/skills/devops-infra/de/github-actions.md +179 -0
  236. package/skills/devops-infra/de/kubernetes.md +129 -0
  237. package/skills/devops-infra/de/terraform.md +130 -0
  238. package/skills/devops-infra/docker.md +131 -0
  239. package/skills/devops-infra/es/docker.md +133 -0
  240. package/skills/devops-infra/es/github-actions.md +179 -0
  241. package/skills/devops-infra/es/kubernetes.md +129 -0
  242. package/skills/devops-infra/es/terraform.md +130 -0
  243. package/skills/devops-infra/fr/docker.md +133 -0
  244. package/skills/devops-infra/fr/github-actions.md +179 -0
  245. package/skills/devops-infra/fr/kubernetes.md +129 -0
  246. package/skills/devops-infra/fr/terraform.md +130 -0
  247. package/skills/devops-infra/github-actions.md +177 -0
  248. package/skills/devops-infra/kubernetes.md +127 -0
  249. package/skills/devops-infra/nl/docker.md +133 -0
  250. package/skills/devops-infra/nl/github-actions.md +179 -0
  251. package/skills/devops-infra/nl/kubernetes.md +129 -0
  252. package/skills/devops-infra/nl/terraform.md +130 -0
  253. package/skills/devops-infra/terraform.md +128 -0
  254. package/skills/finance-payments/de/stripe.md +187 -0
  255. package/skills/finance-payments/es/stripe.md +187 -0
  256. package/skills/finance-payments/fr/stripe.md +187 -0
  257. package/skills/finance-payments/nl/stripe.md +187 -0
  258. package/skills/finance-payments/stripe.md +185 -0
  259. package/workflows/code-review.md +151 -0
  260. package/workflows/de/code-review.md +153 -0
  261. package/workflows/de/debugging-session.md +146 -0
  262. package/workflows/de/feature-development.md +155 -0
  263. package/workflows/de/new-project-bootstrap.md +175 -0
  264. package/workflows/de/refactor-safely.md +150 -0
  265. package/workflows/debugging-session.md +144 -0
  266. package/workflows/es/code-review.md +153 -0
  267. package/workflows/es/debugging-session.md +146 -0
  268. package/workflows/es/feature-development.md +155 -0
  269. package/workflows/es/new-project-bootstrap.md +175 -0
  270. package/workflows/es/refactor-safely.md +150 -0
  271. package/workflows/feature-development.md +153 -0
  272. package/workflows/fr/code-review.md +153 -0
  273. package/workflows/fr/debugging-session.md +146 -0
  274. package/workflows/fr/feature-development.md +155 -0
  275. package/workflows/fr/new-project-bootstrap.md +175 -0
  276. package/workflows/fr/refactor-safely.md +150 -0
  277. package/workflows/new-project-bootstrap.md +173 -0
  278. package/workflows/nl/code-review.md +153 -0
  279. package/workflows/nl/debugging-session.md +146 -0
  280. package/workflows/nl/feature-development.md +155 -0
  281. package/workflows/nl/new-project-bootstrap.md +175 -0
  282. package/workflows/nl/refactor-safely.md +150 -0
  283. package/workflows/refactor-safely.md +148 -0
@@ -0,0 +1,244 @@
1
+ > 🇫🇷 This is the French translation. [English version](../fastapi.md).
2
+
3
+ # Compétence FastAPI
4
+
5
+ ## Quand activer
6
+ - Construire une API Python REST ou async avec FastAPI
7
+ - Définir des modèles Pydantic de requête/réponse
8
+ - Configurer l'injection de dépendances avec `Depends`
9
+ - Rédiger des gestionnaires de routes async avec SQLAlchemy ou des drivers DB async
10
+ - Ajouter des middlewares (CORS, auth, logging, rate limiting)
11
+ - Configurer des tâches en arrière-plan ou des workers Celery
12
+ - Personnaliser les docs OpenAPI (tags, descriptions, schémas de réponse)
13
+ - Rédiger des tests d'intégration avec `TestClient` ou `AsyncClient`
14
+ - Structurer un projet FastAPI multi-modules
15
+
16
+ ## Quand NE PAS utiliser
17
+ - Projets Django/DRF — utiliser la compétence Django
18
+ - Bases de code uniquement synchrones où le surcoût async n'est pas justifié
19
+ - Scripts simples qui n'ont pas besoin de HTTP — utiliser du Python simple
20
+ - APIs gRPC ou GraphQL — transport et couche de schéma différents
21
+
22
+ ## Instructions
23
+
24
+ ### Structure du projet
25
+ ```
26
+ app/
27
+ ├── main.py # Fabrique d'application FastAPI
28
+ ├── core/
29
+ │ ├── config.py # Paramètres via pydantic-settings
30
+ │ └── security.py # JWT, utilitaires de hachage
31
+ ├── api/
32
+ │ ├── deps.py # Fonctions Depends() partagées
33
+ │ └── v1/
34
+ │ ├── router.py # Agrégateur APIRouter
35
+ │ └── endpoints/
36
+ │ ├── users.py
37
+ │ └── items.py
38
+ ├── models/ # Modèles ORM SQLAlchemy
39
+ ├── schemas/ # Schémas Pydantic de requête/réponse
40
+ ├── crud/ # Fonctions d'opérations DB (pas ORM, pas HTTP)
41
+ └── db/
42
+ ├── session.py # Fabrique AsyncSession
43
+ └── base.py # Import de la base déclarative
44
+ ```
45
+
46
+ ### Fabrique d'application
47
+ ```python
48
+ # main.py
49
+ from fastapi import FastAPI
50
+ from app.api.v1.router import api_router
51
+ from app.core.config import settings
52
+
53
+ def create_app() -> FastAPI:
54
+ app = FastAPI(
55
+ title=settings.PROJECT_NAME,
56
+ openapi_url=f"{settings.API_V1_STR}/openapi.json",
57
+ docs_url="/docs" if settings.ENVIRONMENT != "production" else None,
58
+ )
59
+ app.include_router(api_router, prefix=settings.API_V1_STR)
60
+ return app
61
+
62
+ app = create_app()
63
+ ```
64
+
65
+ ### Paramètres avec pydantic-settings
66
+ ```python
67
+ # core/config.py
68
+ from pydantic_settings import BaseSettings
69
+
70
+ class Settings(BaseSettings):
71
+ PROJECT_NAME: str = "MyAPI"
72
+ API_V1_STR: str = "/api/v1"
73
+ DATABASE_URL: str
74
+ SECRET_KEY: str
75
+ ENVIRONMENT: str = "development"
76
+ ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
77
+
78
+ class Config:
79
+ env_file = ".env"
80
+ case_sensitive = True
81
+
82
+ settings = Settings()
83
+ ```
84
+
85
+ ### Dépendance de session SQLAlchemy async
86
+ ```python
87
+ # db/session.py
88
+ from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
89
+
90
+ engine = create_async_engine(settings.DATABASE_URL, echo=False)
91
+ AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)
92
+
93
+ # api/deps.py
94
+ async def get_db() -> AsyncIterator[AsyncSession]:
95
+ async with AsyncSessionLocal() as session:
96
+ try:
97
+ yield session
98
+ await session.commit()
99
+ except Exception:
100
+ await session.rollback()
101
+ raise
102
+ ```
103
+
104
+ ### Gestionnaires de routes
105
+ ```python
106
+ # api/v1/endpoints/users.py
107
+ from fastapi import APIRouter, Depends, HTTPException, status
108
+ from sqlalchemy.ext.asyncio import AsyncSession
109
+ from app.api.deps import get_db, get_current_user
110
+ from app.crud import user as crud_user
111
+ from app.schemas.user import UserCreate, UserResponse
112
+
113
+ router = APIRouter(prefix="/users", tags=["users"])
114
+
115
+ @router.post("/", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
116
+ async def create_user(
117
+ payload: UserCreate,
118
+ db: AsyncSession = Depends(get_db),
119
+ ) -> UserResponse:
120
+ if await crud_user.get_by_email(db, email=payload.email):
121
+ raise HTTPException(status_code=400, detail="Email already registered")
122
+ return await crud_user.create(db, obj_in=payload)
123
+ ```
124
+
125
+ ### Injection de dépendances pour l'auth
126
+ ```python
127
+ # api/deps.py
128
+ from fastapi import Depends, HTTPException, status
129
+ from fastapi.security import OAuth2PasswordBearer
130
+ from jose import JWTError, jwt
131
+
132
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/token")
133
+
134
+ async def get_current_user(
135
+ token: str = Depends(oauth2_scheme),
136
+ db: AsyncSession = Depends(get_db),
137
+ ) -> User:
138
+ try:
139
+ payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
140
+ user_id: str = payload.get("sub")
141
+ if user_id is None:
142
+ raise credentials_exception
143
+ except JWTError:
144
+ raise HTTPException(status_code=401, detail="Invalid credentials")
145
+ user = await crud_user.get(db, id=user_id)
146
+ if user is None:
147
+ raise HTTPException(status_code=404, detail="User not found")
148
+ return user
149
+ ```
150
+
151
+ ### Tâches en arrière-plan
152
+ ```python
153
+ # Utiliser BackgroundTasks de FastAPI pour les tâches légères fire-and-forget (pas de résultat nécessaire)
154
+ @router.post("/send-email")
155
+ async def send_email_endpoint(
156
+ payload: EmailPayload,
157
+ background_tasks: BackgroundTasks,
158
+ ):
159
+ background_tasks.add_task(send_email, payload.to, payload.subject, payload.body)
160
+ return {"status": "queued"}
161
+
162
+ # Utiliser Celery pour : les réessais, le suivi des résultats, la planification, les tâches inter-services
163
+ ```
164
+
165
+ ### Middleware CORS
166
+ ```python
167
+ from fastapi.middleware.cors import CORSMiddleware
168
+
169
+ app.add_middleware(
170
+ CORSMiddleware,
171
+ allow_origins=settings.ALLOWED_ORIGINS, # Jamais ["*"] en production
172
+ allow_credentials=True,
173
+ allow_methods=["*"],
174
+ allow_headers=["*"],
175
+ )
176
+ ```
177
+
178
+ ### Gestionnaires d'exceptions personnalisés
179
+ ```python
180
+ from fastapi.responses import JSONResponse
181
+
182
+ @app.exception_handler(ValueError)
183
+ async def value_error_handler(request: Request, exc: ValueError) -> JSONResponse:
184
+ return JSONResponse(status_code=422, content={"detail": str(exc)})
185
+ ```
186
+
187
+ ### Tests
188
+ ```python
189
+ # tests/conftest.py
190
+ import pytest
191
+ from httpx import AsyncClient, ASGITransport
192
+ from app.main import app
193
+
194
+ @pytest.fixture
195
+ async def client() -> AsyncIterator[AsyncClient]:
196
+ async with AsyncClient(
197
+ transport=ASGITransport(app=app), base_url="http://test"
198
+ ) as ac:
199
+ yield ac
200
+
201
+ # tests/test_users.py
202
+ @pytest.mark.asyncio
203
+ async def test_create_user(client: AsyncClient, db_session):
204
+ resp = await client.post("/api/v1/users/", json={"email": "a@b.com", "password": "secret"})
205
+ assert resp.status_code == 201
206
+ assert resp.json()["email"] == "a@b.com"
207
+ ```
208
+
209
+ ### Patterns Pydantic courants
210
+ ```python
211
+ from pydantic import BaseModel, EmailStr, field_validator, model_validator
212
+
213
+ class UserCreate(BaseModel):
214
+ email: EmailStr
215
+ password: str
216
+
217
+ @field_validator("password")
218
+ @classmethod
219
+ def password_strength(cls, v: str) -> str:
220
+ if len(v) < 8:
221
+ raise ValueError("Password must be at least 8 characters")
222
+ return v
223
+
224
+ class UserResponse(BaseModel):
225
+ id: int
226
+ email: EmailStr
227
+
228
+ model_config = {"from_attributes": True} # remplace orm_mode = True
229
+ ```
230
+
231
+ ## Exemple
232
+
233
+ **Utilisateur :** Construire un endpoint FastAPI pour créer un article de blog, authentifié avec JWT, sauvegardant dans PostgreSQL avec SQLAlchemy async.
234
+
235
+ **Structure attendue :**
236
+ - `schemas/post.py` — `PostCreate(BaseModel)`, `PostResponse(BaseModel)` avec `from_attributes=True`
237
+ - `models/post.py` — modèle ORM `Post` avec `id`, `title`, `body`, `author_id` (FK vers User), `created_at`
238
+ - `crud/post.py` — fonction async `create(db, *, obj_in, author_id)`
239
+ - `api/v1/endpoints/posts.py` — `POST /posts/` avec `Depends(get_current_user)` et `Depends(get_db)`
240
+ - `api/v1/router.py` — inclure le router posts
241
+
242
+ ---
243
+
244
+ > **Travaillez avec nous :** Claudient est soutenu par [Uitbreiden](https://uitbreiden.com/) — nous construisons des produits IA et des solutions B2B avec des communautés de développeurs. [uitbreiden.com](https://uitbreiden.com/)
@@ -0,0 +1,285 @@
1
+ > 🇳🇱 Dit is de Nederlandse vertaling. [Engelse versie](../django.md).
2
+
3
+ # Django Skill
4
+
5
+ ## Wanneer te activeren
6
+ - Een Django-project bouwen met ORM-modellen, migraties en views
7
+ - Django REST Framework (DRF) serializers, viewsets en routers instellen
8
+ - Aangepaste model-managers of QuerySet-methoden schrijven
9
+ - Django-signalen gebruiken voor ontkoppelde neveneffecten
10
+ - Celery instellen voor async taken in een Django-project
11
+ - De Django-admin aanpassen
12
+ - Tests schrijven met `django.test.TestCase` of `pytest-django`
13
+
14
+ ## Wanneer NIET te gebruiken
15
+ - Async-first API's — gebruik in plaats daarvan de FastAPI skill
16
+ - Microservices die Django's ORM of admin niet nodig hebben
17
+ - Eenvoudige scripts of CLI's — gewone Python of Typer
18
+ - Als het project al FastAPI of Flask gebruikt
19
+
20
+ ## Instructies
21
+
22
+ ### Projectindeling
23
+ ```
24
+ project_name/
25
+ ├── manage.py
26
+ ├── config/
27
+ │ ├── settings/
28
+ │ │ ├── base.py
29
+ │ │ ├── development.py
30
+ │ │ └── production.py
31
+ │ ├── urls.py
32
+ │ └── wsgi.py
33
+ ├── apps/
34
+ │ └── users/
35
+ │ ├── models.py
36
+ │ ├── serializers.py
37
+ │ ├── views.py
38
+ │ ├── urls.py
39
+ │ ├── admin.py
40
+ │ ├── managers.py
41
+ │ └── tests/
42
+ └── requirements/
43
+ ├── base.txt
44
+ ├── development.txt
45
+ └── production.txt
46
+ ```
47
+
48
+ ### Instellingen splitsen
49
+ ```python
50
+ # config/settings/base.py
51
+ from pathlib import Path
52
+ BASE_DIR = Path(__file__).resolve().parent.parent.parent
53
+
54
+ INSTALLED_APPS = [
55
+ "django.contrib.admin",
56
+ "django.contrib.auth",
57
+ "django.contrib.contenttypes",
58
+ "rest_framework",
59
+ "apps.users",
60
+ ]
61
+
62
+ AUTH_USER_MODEL = "users.User" # Stel altijd een aangepast gebruikersmodel in vanaf dag één
63
+
64
+ # config/settings/production.py
65
+ from .base import *
66
+ DEBUG = False
67
+ ALLOWED_HOSTS = env.list("ALLOWED_HOSTS")
68
+ DATABASES = {"default": env.db("DATABASE_URL")}
69
+ ```
70
+
71
+ ### Aangepast User-model
72
+ ```python
73
+ # apps/users/models.py — instellen vóór eerste migratie, daarna nooit meer wijzigen
74
+ from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
75
+ from django.db import models
76
+ from .managers import UserManager
77
+
78
+ class User(AbstractBaseUser, PermissionsMixin):
79
+ email = models.EmailField(unique=True)
80
+ is_staff = models.BooleanField(default=False)
81
+ is_active = models.BooleanField(default=True)
82
+ created_at = models.DateTimeField(auto_now_add=True)
83
+
84
+ USERNAME_FIELD = "email"
85
+ REQUIRED_FIELDS = []
86
+ objects = UserManager()
87
+ ```
88
+
89
+ ### Aangepaste Manager
90
+ ```python
91
+ # apps/users/managers.py
92
+ from django.contrib.auth.base_user import BaseUserManager
93
+
94
+ class UserManager(BaseUserManager):
95
+ def create_user(self, email: str, password: str, **extra_fields):
96
+ if not email:
97
+ raise ValueError("Email is required")
98
+ email = self.normalize_email(email)
99
+ user = self.model(email=email, **extra_fields)
100
+ user.set_password(password)
101
+ user.save()
102
+ return user
103
+
104
+ def create_superuser(self, email: str, password: str, **extra_fields):
105
+ extra_fields.setdefault("is_staff", True)
106
+ extra_fields.setdefault("is_superuser", True)
107
+ return self.create_user(email, password, **extra_fields)
108
+
109
+ def active(self):
110
+ return self.get_queryset().filter(is_active=True)
111
+ ```
112
+
113
+ ### DRF Serializers
114
+ ```python
115
+ # apps/users/serializers.py
116
+ from rest_framework import serializers
117
+ from .models import User
118
+
119
+ class UserCreateSerializer(serializers.ModelSerializer):
120
+ password = serializers.CharField(write_only=True, min_length=8)
121
+
122
+ class Meta:
123
+ model = User
124
+ fields = ["id", "email", "password"]
125
+
126
+ def create(self, validated_data: dict) -> User:
127
+ return User.objects.create_user(**validated_data)
128
+
129
+ class UserSerializer(serializers.ModelSerializer):
130
+ class Meta:
131
+ model = User
132
+ fields = ["id", "email", "created_at"]
133
+ read_only_fields = ["id", "created_at"]
134
+ ```
135
+
136
+ ### DRF ViewSets
137
+ ```python
138
+ # apps/users/views.py
139
+ from rest_framework import viewsets, permissions, status
140
+ from rest_framework.decorators import action
141
+ from rest_framework.response import Response
142
+ from .models import User
143
+ from .serializers import UserSerializer, UserCreateSerializer
144
+
145
+ class UserViewSet(viewsets.ModelViewSet):
146
+ queryset = User.objects.active()
147
+ permission_classes = [permissions.IsAuthenticated]
148
+
149
+ def get_serializer_class(self):
150
+ if self.action == "create":
151
+ return UserCreateSerializer
152
+ return UserSerializer
153
+
154
+ def get_permissions(self):
155
+ if self.action == "create":
156
+ return [permissions.AllowAny()]
157
+ return super().get_permissions()
158
+
159
+ @action(detail=False, methods=["get"])
160
+ def me(self, request):
161
+ return Response(UserSerializer(request.user).data)
162
+ ```
163
+
164
+ ### Router-instelling
165
+ ```python
166
+ # apps/users/urls.py
167
+ from rest_framework.routers import DefaultRouter
168
+ from .views import UserViewSet
169
+
170
+ router = DefaultRouter()
171
+ router.register("users", UserViewSet, basename="user")
172
+ urlpatterns = router.urls
173
+ ```
174
+
175
+ ### Signalen
176
+ ```python
177
+ # apps/users/signals.py — gebruik signalen alleen voor werkelijk ontkoppelde neveneffecten
178
+ from django.db.models.signals import post_save
179
+ from django.dispatch import receiver
180
+ from .models import User
181
+
182
+ @receiver(post_save, sender=User)
183
+ def send_welcome_email(sender, instance: User, created: bool, **kwargs):
184
+ if created:
185
+ send_email_task.delay(instance.email, "welcome")
186
+
187
+ # apps/users/apps.py
188
+ class UsersConfig(AppConfig):
189
+ name = "apps.users"
190
+ def ready(self):
191
+ import apps.users.signals # noqa: F401
192
+ ```
193
+
194
+ ### Celery
195
+ ```python
196
+ # config/celery.py
197
+ from celery import Celery
198
+ import os
199
+
200
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
201
+ app = Celery("project_name")
202
+ app.config_from_object("django.conf:settings", namespace="CELERY")
203
+ app.autodiscover_tasks()
204
+
205
+ # apps/users/tasks.py
206
+ from config.celery import app
207
+
208
+ @app.task(bind=True, max_retries=3)
209
+ def send_email_task(self, to_email: str, template: str):
210
+ try:
211
+ # e-mail verzenden
212
+ pass
213
+ except Exception as exc:
214
+ raise self.retry(exc=exc, countdown=60)
215
+ ```
216
+
217
+ ### Admin-aanpassing
218
+ ```python
219
+ # apps/users/admin.py
220
+ from django.contrib import admin
221
+ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
222
+ from .models import User
223
+
224
+ @admin.register(User)
225
+ class UserAdmin(BaseUserAdmin):
226
+ list_display = ["email", "is_active", "is_staff", "created_at"]
227
+ list_filter = ["is_active", "is_staff"]
228
+ search_fields = ["email"]
229
+ ordering = ["-created_at"]
230
+ fieldsets = (
231
+ (None, {"fields": ("email", "password")}),
232
+ ("Permissions", {"fields": ("is_active", "is_staff", "is_superuser", "groups")}),
233
+ )
234
+ add_fieldsets = (
235
+ (None, {"fields": ("email", "password1", "password2")}),
236
+ )
237
+ ```
238
+
239
+ ### QuerySet-optimalisatie
240
+ ```python
241
+ # Gebruik altijd select_related voor FK-velden, prefetch_related voor M2M/reverse FK
242
+ posts = Post.objects.select_related("author").prefetch_related("tags").filter(published=True)
243
+
244
+ # Gebruik only() of defer() voor grote modellen wanneer je alleen specifieke velden nodig hebt
245
+ emails = User.objects.filter(is_active=True).only("email")
246
+
247
+ # Gebruik values() voor alleen-lezen aggregaties — slaat ORM-objectconstructie over
248
+ counts = Order.objects.values("status").annotate(count=Count("id"))
249
+ ```
250
+
251
+ ### Testen
252
+ ```python
253
+ # pytest-django-stijl
254
+ import pytest
255
+ from rest_framework.test import APIClient
256
+
257
+ @pytest.fixture
258
+ def api_client():
259
+ return APIClient()
260
+
261
+ @pytest.fixture
262
+ def authenticated_client(api_client, user):
263
+ api_client.force_authenticate(user=user)
264
+ return api_client
265
+
266
+ @pytest.mark.django_db
267
+ def test_create_user(api_client):
268
+ resp = api_client.post("/api/users/", {"email": "a@b.com", "password": "strongpass"})
269
+ assert resp.status_code == 201
270
+ assert resp.data["email"] == "a@b.com"
271
+ ```
272
+
273
+ ## Voorbeeld
274
+
275
+ **Gebruiker:** Voeg een `Post`-model toe aan een Django-project met DRF, inclusief lijst/aanmaken/ophalen-endpoints, gepagineerde resultaten en filter op `published=True`.
276
+
277
+ **Verwachte output:**
278
+ - `models.py` — `Post` met `title`, `body`, `author` (FK naar User), `published`, `created_at`
279
+ - `serializers.py` — `PostSerializer` met alleen-lezen `author` (genest), beschrijfbare `title`/`body`/`published`
280
+ - `views.py` — `PostViewSet` met `queryset` gefilterd op `published=True` voor niet-geauthenticeerde gebruikers, `IsAuthenticatedOrReadOnly`-toestemming, `PageNumberPagination`
281
+ - `urls.py` — router geregistreerd op `/api/posts/`
282
+
283
+ ---
284
+
285
+ > **Werk met ons:** Claudient wordt ondersteund door [Uitbreiden](https://uitbreiden.com/) — we bouwen AI-producten en B2B-oplossingen met ontwikkelaarsgemeenschappen. [uitbreiden.com](https://uitbreiden.com/)