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,285 @@
1
+ > 🇪🇸 Esta es la traducción en español. [Versión en inglés](../django.md).
2
+
3
+ # Skill de Django
4
+
5
+ ## Cuándo activar
6
+ - Construir un proyecto Django con modelos ORM, migraciones y vistas
7
+ - Configurar serializadores, viewsets y routers de Django REST Framework (DRF)
8
+ - Escribir managers de modelos personalizados o métodos QuerySet
9
+ - Usar señales de Django para efectos secundarios desacoplados
10
+ - Configurar Celery para tareas asíncronas en un proyecto Django
11
+ - Personalizar el admin de Django
12
+ - Escribir pruebas con `django.test.TestCase` o `pytest-django`
13
+
14
+ ## Cuándo NO usar
15
+ - APIs async-first — usar el skill de FastAPI en su lugar
16
+ - Microservicios que no necesitan el ORM o el admin de Django
17
+ - Scripts o CLIs simples — Python puro o Typer
18
+ - Si el proyecto ya usa FastAPI o Flask
19
+
20
+ ## Instrucciones
21
+
22
+ ### Estructura del proyecto
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
+ ### División de configuración
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" # Siempre establece un modelo de usuario personalizado desde el primer día
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
+ ### Modelo de usuario personalizado
72
+ ```python
73
+ # apps/users/models.py — configurar antes de la primera migración, nunca cambiar después
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
+ ### Manager personalizado
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
+ ### Serializadores DRF
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
+ ### ViewSets DRF
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
+ ### Configuración del router
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
+ ### Señales
176
+ ```python
177
+ # apps/users/signals.py — usa señales solo para efectos secundarios verdaderamente desacoplados
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
+ # enviar correo
212
+ pass
213
+ except Exception as exc:
214
+ raise self.retry(exc=exc, countdown=60)
215
+ ```
216
+
217
+ ### Personalización del admin
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
+ ("Permisos", {"fields": ("is_active", "is_staff", "is_superuser", "groups")}),
233
+ )
234
+ add_fieldsets = (
235
+ (None, {"fields": ("email", "password1", "password2")}),
236
+ )
237
+ ```
238
+
239
+ ### Optimización de QuerySet
240
+ ```python
241
+ # Siempre select_related para campos FK, prefetch_related para M2M/FK inversa
242
+ posts = Post.objects.select_related("author").prefetch_related("tags").filter(published=True)
243
+
244
+ # Usa only() o defer() para modelos grandes cuando solo necesitas campos específicos
245
+ emails = User.objects.filter(is_active=True).only("email")
246
+
247
+ # Usa values() para agregaciones de solo lectura — omite la construcción de objetos ORM
248
+ counts = Order.objects.values("status").annotate(count=Count("id"))
249
+ ```
250
+
251
+ ### Testing
252
+ ```python
253
+ # Estilo pytest-django
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
+ ## Ejemplo
274
+
275
+ **Usuario:** Agregar un modelo `Post` a un proyecto Django con DRF, incluyendo endpoints de lista/create/retrieve, resultados paginados y filtro por `published=True`.
276
+
277
+ **Salida esperada:**
278
+ - `models.py` — `Post` con `title`, `body`, `author` (FK a User), `published`, `created_at`
279
+ - `serializers.py` — `PostSerializer` con `author` de solo lectura (anidado), `title`/`body`/`published` editables
280
+ - `views.py` — `PostViewSet` con `queryset` filtrado a `published=True` para usuarios no autenticados, permiso `IsAuthenticatedOrReadOnly`, `PageNumberPagination`
281
+ - `urls.py` — router registrado en `/api/posts/`
282
+
283
+ ---
284
+
285
+ > **Trabaja con nosotros:** Claudient está respaldado por [Uitbreiden](https://uitbreiden.com/) — construimos productos de IA y soluciones B2B con comunidades de desarrolladores. [uitbreiden.com](https://uitbreiden.com/)
@@ -0,0 +1,244 @@
1
+ > 🇪🇸 Esta es la traducción en español. [Versión en inglés](../fastapi.md).
2
+
3
+ # Skill de FastAPI
4
+
5
+ ## Cuándo activar
6
+ - Construir una API REST o asíncrona en Python con FastAPI
7
+ - Definir modelos de solicitud/respuesta con Pydantic
8
+ - Configurar inyección de dependencias con `Depends`
9
+ - Escribir manejadores de rutas asíncronos con SQLAlchemy o drivers de BD asíncronos
10
+ - Agregar middleware (CORS, autenticación, logging, limitación de tasa)
11
+ - Configurar tareas en segundo plano o workers de Celery
12
+ - Personalizar la documentación OpenAPI (etiquetas, descripciones, esquemas de respuesta)
13
+ - Escribir pruebas de integración con `TestClient` o `AsyncClient`
14
+ - Estructurar un proyecto FastAPI multi-módulo
15
+
16
+ ## Cuándo NO usar
17
+ - Proyectos Django/DRF — usar el skill de Django
18
+ - Codebases solo síncronos donde la sobrecarga asíncrona no está justificada
19
+ - Scripts simples que no necesitan HTTP — usar Python puro
20
+ - APIs gRPC o GraphQL — capa de transporte y esquema diferente
21
+
22
+ ## Instrucciones
23
+
24
+ ### Estructura del proyecto
25
+ ```
26
+ app/
27
+ ├── main.py # Factoría de la aplicación FastAPI
28
+ ├── core/
29
+ │ ├── config.py # Configuración mediante pydantic-settings
30
+ │ └── security.py # Utilidades JWT y hashing
31
+ ├── api/
32
+ │ ├── deps.py # Funciones Depends() compartidas
33
+ │ └── v1/
34
+ │ ├── router.py # Agregador APIRouter
35
+ │ └── endpoints/
36
+ │ ├── users.py
37
+ │ └── items.py
38
+ ├── models/ # Modelos ORM de SQLAlchemy
39
+ ├── schemas/ # Esquemas de solicitud/respuesta Pydantic
40
+ ├── crud/ # Funciones de operación en BD (no ORM, no HTTP)
41
+ └── db/
42
+ ├── session.py # Factoría AsyncSession
43
+ └── base.py # Importación de la base declarativa
44
+ ```
45
+
46
+ ### Factoría de la aplicación
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
+ ### Configuración con 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
+ ### Dependencia de sesión SQLAlchemy asíncrona
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
+ ### Manejadores de rutas
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
+ ### Inyección de dependencias para autenticación
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
+ ### Tareas en segundo plano
152
+ ```python
153
+ # Usa BackgroundTasks de FastAPI para tareas ligeras fire-and-forget (sin resultado necesario)
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
+ # Usa Celery para: reintentos, seguimiento de resultados, programación, tareas entre servicios
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, # Nunca ["*"] en producción
172
+ allow_credentials=True,
173
+ allow_methods=["*"],
174
+ allow_headers=["*"],
175
+ )
176
+ ```
177
+
178
+ ### Manejadores de excepciones personalizados
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
+ ### Testing
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
+ ### Patrones comunes de Pydantic
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} # reemplaza orm_mode = True
229
+ ```
230
+
231
+ ## Ejemplo
232
+
233
+ **Usuario:** Construir un endpoint FastAPI para crear una publicación de blog, autenticada con JWT, guardando en PostgreSQL con SQLAlchemy async.
234
+
235
+ **Estructura esperada:**
236
+ - `schemas/post.py` — `PostCreate(BaseModel)`, `PostResponse(BaseModel)` con `from_attributes=True`
237
+ - `models/post.py` — modelo ORM `Post` con `id`, `title`, `body`, `author_id` (FK a User), `created_at`
238
+ - `crud/post.py` — función asíncrona `create(db, *, obj_in, author_id)`
239
+ - `api/v1/endpoints/posts.py` — `POST /posts/` con `Depends(get_current_user)` y `Depends(get_db)`
240
+ - `api/v1/router.py` — incluir router de posts
241
+
242
+ ---
243
+
244
+ > **Trabaja con nosotros:** Claudient está respaldado por [Uitbreiden](https://uitbreiden.com/) — construimos productos de IA y soluciones B2B con comunidades de desarrolladores. [uitbreiden.com](https://uitbreiden.com/)