cc-codeconductor 0.4.2 → 0.5.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 (172) hide show
  1. package/README.md +75 -4
  2. package/dist/index.js +2698 -230
  3. package/package.json +1 -1
  4. package/presets/agy/skills/evaluation/SKILL.md +6 -0
  5. package/presets/agy/skills/openspec/SKILL.md +32 -0
  6. package/presets/agy/workflows/cc-openspec.md +62 -0
  7. package/presets/agy/workflows/cc-pipeline.md +69 -0
  8. package/presets/agy/workflows/cc-scorecard.md +17 -0
  9. package/presets/claude/commands/cc/openspec.md +144 -0
  10. package/presets/claude/commands/cc/review.md +13 -2
  11. package/presets/claude/commands/cc/scorecard.md +65 -0
  12. package/presets/claude/skills/evaluation/SKILL.md +42 -0
  13. package/presets/claude/skills/openspec/SKILL.md +54 -0
  14. package/presets/codex/AGENTS.md +57 -0
  15. package/presets/cursor/.cursorignore +15 -0
  16. package/presets/cursor/AGENTS.md +504 -0
  17. package/presets/cursor/agents/architect.md +211 -0
  18. package/presets/cursor/agents/complexity-auditor.md +76 -0
  19. package/presets/cursor/agents/contract-builder.md +75 -0
  20. package/presets/cursor/agents/docs.md +180 -0
  21. package/presets/cursor/agents/goal-planner.md +71 -0
  22. package/presets/cursor/agents/implementer.md +161 -0
  23. package/presets/cursor/agents/orchestrator.md +377 -0
  24. package/presets/cursor/agents/repo-explorer.md +100 -0
  25. package/presets/cursor/agents/reviewer.md +237 -0
  26. package/presets/cursor/agents/security-reviewer.md +113 -0
  27. package/presets/cursor/agents/task-coach.md +145 -0
  28. package/presets/cursor/agents/tester.md +241 -0
  29. package/presets/cursor/commands/cc/api-contract.md +58 -0
  30. package/presets/cursor/commands/cc/db-migration.md +58 -0
  31. package/presets/cursor/commands/cc/feature.md +115 -0
  32. package/presets/cursor/commands/cc/fix.md +121 -0
  33. package/presets/cursor/commands/cc/openspec.md +144 -0
  34. package/presets/cursor/commands/cc/pagespeed.md +103 -0
  35. package/presets/cursor/commands/cc/refactor.md +148 -0
  36. package/presets/cursor/commands/cc/review.md +137 -0
  37. package/presets/cursor/commands/cc/scorecard.md +65 -0
  38. package/presets/cursor/commands/cc/tdd-cycle.md +226 -0
  39. package/presets/cursor/commands/cc/test-plan.md +138 -0
  40. package/presets/cursor/rules/behavioral-discipline.mdc +14 -0
  41. package/presets/cursor/rules/context-budget.mdc +12 -0
  42. package/presets/cursor/rules/orchestration.mdc +12 -0
  43. package/presets/cursor/rules/yagni-stdlib.mdc +11 -0
  44. package/presets/cursor/skills/android/SKILL.md +122 -0
  45. package/presets/cursor/skills/api-versioning/SKILL.md +394 -0
  46. package/presets/cursor/skills/astro/SKILL.md +322 -0
  47. package/presets/cursor/skills/auth-token-inspector/SKILL.md +33 -0
  48. package/presets/cursor/skills/code-review/SKILL.md +208 -0
  49. package/presets/cursor/skills/conductor-setup/SKILL.md +127 -0
  50. package/presets/cursor/skills/django-orm/SKILL.md +463 -0
  51. package/presets/cursor/skills/django-testing/SKILL.md +417 -0
  52. package/presets/cursor/skills/django-uv/SKILL.md +409 -0
  53. package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +54 -0
  54. package/presets/cursor/skills/evaluation/SKILL.md +8 -0
  55. package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +46 -0
  56. package/presets/cursor/skills/find-skills/SKILL.md +144 -0
  57. package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +49 -0
  58. package/presets/cursor/skills/jpa-postgres/SKILL.md +626 -0
  59. package/presets/cursor/skills/laravel-specialist/SKILL.md +267 -0
  60. package/presets/cursor/skills/laravel-specialist/references/eloquent.md +351 -0
  61. package/presets/cursor/skills/laravel-specialist/references/livewire.md +512 -0
  62. package/presets/cursor/skills/laravel-specialist/references/queues.md +423 -0
  63. package/presets/cursor/skills/laravel-specialist/references/routing.md +362 -0
  64. package/presets/cursor/skills/laravel-specialist/references/testing.md +522 -0
  65. package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +39 -0
  66. package/presets/cursor/skills/multi-agent-orchestration/README.md +144 -0
  67. package/presets/cursor/skills/multi-agent-orchestration/SKILL.md +579 -0
  68. package/presets/cursor/skills/multi-agent-orchestration/examples/framework_implementations.py +362 -0
  69. package/presets/cursor/skills/multi-agent-orchestration/examples/orchestration_patterns.py +411 -0
  70. package/presets/cursor/skills/multi-agent-orchestration/scripts/agent_communication.py +334 -0
  71. package/presets/cursor/skills/multi-agent-orchestration/scripts/benchmarking.py +341 -0
  72. package/presets/cursor/skills/multi-agent-orchestration/scripts/workflow_management.py +334 -0
  73. package/presets/cursor/skills/nextjs-typescript/SKILL.md +394 -0
  74. package/presets/cursor/skills/openspec/SKILL.md +52 -0
  75. package/presets/cursor/skills/pagespeed-insights/SKILL.md +445 -0
  76. package/presets/cursor/skills/pagespeed-insights/reference.md +50 -0
  77. package/presets/cursor/skills/pagespeed-perf/SKILL.md +281 -0
  78. package/presets/cursor/skills/php-pro/SKILL.md +210 -0
  79. package/presets/cursor/skills/php-pro/references/async-patterns.md +412 -0
  80. package/presets/cursor/skills/php-pro/references/laravel-patterns.md +377 -0
  81. package/presets/cursor/skills/php-pro/references/modern-php-features.md +323 -0
  82. package/presets/cursor/skills/php-pro/references/symfony-patterns.md +466 -0
  83. package/presets/cursor/skills/php-pro/references/testing-quality.md +466 -0
  84. package/presets/cursor/skills/python/SKILL.md +613 -0
  85. package/presets/cursor/skills/python-django-stack/SKILL.md +500 -0
  86. package/presets/cursor/skills/python-fastapi-stack/SKILL.md +464 -0
  87. package/presets/cursor/skills/security/SKILL.md +384 -0
  88. package/presets/cursor/skills/seo-analytics-injector/SKILL.md +44 -0
  89. package/presets/cursor/skills/spring-auth-auditor/SKILL.md +33 -0
  90. package/presets/cursor/skills/spring-boot-feature/SKILL.md +566 -0
  91. package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +408 -0
  92. package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +479 -0
  93. package/presets/cursor/skills/sqlalchemy/SKILL.md +473 -0
  94. package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +32 -0
  95. package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +28 -0
  96. package/presets/cursor/skills/testing-tdd/SKILL.md +592 -0
  97. package/presets/cursor/skills/workflow-orchestration-patterns/SKILL.md +98 -0
  98. package/presets/cursor/skills/workflow-orchestration-patterns/references/details.md +223 -0
  99. package/presets/laravel-tall/agents/architect.md +8 -0
  100. package/presets/laravel-tall/agents/implementer.md +12 -0
  101. package/presets/laravel-tall/laravel-tall.yml +38 -0
  102. package/presets/opencode/agents/architect.md +153 -61
  103. package/presets/opencode/agents/complexity-auditor.md +1 -0
  104. package/presets/opencode/agents/contract-builder.md +93 -0
  105. package/presets/opencode/agents/docs.md +125 -40
  106. package/presets/opencode/agents/goal-planner.md +82 -0
  107. package/presets/opencode/agents/implementer.md +107 -38
  108. package/presets/opencode/agents/orchestrator.md +90 -71
  109. package/presets/opencode/agents/repo-explorer.md +1 -2
  110. package/presets/opencode/agents/reviewer.md +164 -75
  111. package/presets/opencode/agents/security-reviewer.md +129 -0
  112. package/presets/opencode/agents/task-coach.md +110 -59
  113. package/presets/opencode/agents/tester.md +1 -2
  114. package/presets/opencode/commands/cc-openspec.md +61 -0
  115. package/presets/opencode/commands/cc-scorecard.md +16 -0
  116. package/presets/opencode/prompts/v0.4.0/architect.md +221 -0
  117. package/presets/opencode/prompts/v0.4.0/complexity-auditor.md +89 -0
  118. package/presets/opencode/prompts/v0.4.0/docs.md +189 -0
  119. package/presets/opencode/prompts/v0.4.0/implementer.md +162 -0
  120. package/presets/opencode/prompts/v0.4.0/orchestrator.md +348 -0
  121. package/presets/opencode/prompts/v0.4.0/repo-explorer.md +110 -0
  122. package/presets/opencode/prompts/v0.4.0/reviewer.md +225 -0
  123. package/presets/opencode/prompts/v0.4.0/task-coach.md +155 -0
  124. package/presets/opencode/prompts/v0.4.0/tester.md +251 -0
  125. package/presets/opencode/prompts/v0.5.0/architect.md +222 -0
  126. package/presets/opencode/prompts/v0.5.0/complexity-auditor.md +91 -0
  127. package/presets/opencode/prompts/v0.5.0/contract-builder.md +84 -0
  128. package/presets/opencode/prompts/v0.5.0/docs.md +190 -0
  129. package/presets/opencode/prompts/v0.5.0/goal-planner.md +80 -0
  130. package/presets/opencode/prompts/v0.5.0/implementer.md +171 -0
  131. package/presets/opencode/prompts/v0.5.0/orchestrator.md +388 -0
  132. package/presets/opencode/prompts/v0.5.0/repo-explorer.md +111 -0
  133. package/presets/opencode/prompts/v0.5.0/reviewer.md +248 -0
  134. package/presets/opencode/prompts/v0.5.0/security-reviewer.md +123 -0
  135. package/presets/opencode/prompts/v0.5.0/task-coach.md +156 -0
  136. package/presets/opencode/prompts/v0.5.0/tester.md +252 -0
  137. package/presets/opencode/skills/auth-token-inspector/SKILL.md +31 -0
  138. package/presets/opencode/skills/drizzle-schema-architect/SKILL.md +51 -0
  139. package/presets/opencode/skills/evaluation/SKILL.md +6 -0
  140. package/presets/opencode/skills/fastapi-pydantic-strict/SKILL.md +44 -0
  141. package/presets/opencode/skills/jpa-nplusone-detector/SKILL.md +46 -0
  142. package/presets/opencode/skills/livewire-alpine-bridge/SKILL.md +36 -0
  143. package/presets/opencode/skills/openspec/SKILL.md +50 -0
  144. package/presets/opencode/skills/seo-analytics-injector/SKILL.md +44 -0
  145. package/presets/opencode/skills/spring-auth-auditor/SKILL.md +30 -0
  146. package/presets/opencode/skills/tailwind-responsive-auditor/SKILL.md +30 -0
  147. package/presets/opencode/skills/tdd-mutation-tester/SKILL.md +28 -0
  148. package/presets/python-data-api/agents/architect.md +8 -0
  149. package/presets/python-data-api/agents/implementer.md +9 -0
  150. package/presets/python-data-api/python-data-api.yml +37 -0
  151. package/presets/spring-kotlin-jpa/agents/architect.md +8 -0
  152. package/presets/spring-kotlin-jpa/agents/implementer.md +9 -0
  153. package/presets/spring-kotlin-jpa/spring-kotlin-jpa.yml +38 -0
  154. package/presets/templates/BACKLOG.md +33 -0
  155. package/presets/templates/execution-profile.yml +6 -0
  156. package/presets/templates/model-comparison.md +11 -0
  157. package/presets/templates/regression-checklist.yml +10 -0
  158. package/presets/ts-next-drizzle/agents/architect.md +8 -0
  159. package/presets/ts-next-drizzle/agents/implementer.md +10 -0
  160. package/presets/ts-next-drizzle/ts-next-drizzle.yml +41 -0
  161. package/src/presets/manifests/agy.yml +2 -2
  162. package/src/presets/manifests/claude.yml +2 -2
  163. package/src/presets/manifests/codex.yml +2 -2
  164. package/src/presets/manifests/cursor.yml +19 -3
  165. package/src/presets/manifests/gemini.yml +2 -2
  166. package/src/presets/manifests/opencode.yml +2 -2
  167. package/src/presets/models/agy.yml +21 -0
  168. package/src/presets/models/claude.yml +18 -0
  169. package/src/presets/models/codex.yml +18 -0
  170. package/src/presets/models/cursor.yml +39 -9
  171. package/src/presets/models/gemini.yml +18 -0
  172. package/src/presets/models/opencode.yml +18 -0
@@ -0,0 +1,463 @@
1
+ ---
2
+ id: django-orm
3
+ version: 1.0.0
4
+ name: Django ORM
5
+ description: >
6
+ Django ORM patterns for multi-tenant POS projects: efficient queries,
7
+ bulk operations, transactions, and multi-schema upload paths.
8
+ Trigger: When writing queryset logic, model saves, or DB-touching service code.
9
+
10
+ user-invokable: true
11
+ license: MIT
12
+ metadata:
13
+ author: lgzarturo
14
+ category: django
15
+
16
+ compatibility:
17
+ tools: [claude, codex, gemini, agy, opencode]
18
+ stacks:
19
+ languages: [python]
20
+ frameworks: [django, django-tenants]
21
+
22
+ risk:
23
+ level: medium
24
+ can_execute_shell: true
25
+ can_modify_files: true
26
+ requires_network: false
27
+
28
+ inputs:
29
+ - model files (models.py)
30
+ - service files (services.py)
31
+ - view files with queryset logic
32
+ - existing queryset or bulk-operation code
33
+
34
+ outputs:
35
+ - optimized queryset patterns with select_related / prefetch_related
36
+ - bulk_create and bulk_update implementations
37
+ - transaction-safe service methods
38
+ - annotated and aggregated querysets
39
+ - migration commands for multi-schema projects
40
+
41
+ quality:
42
+ reviewed_by: codeconductor-core
43
+ version: 0.1.0
44
+ paths:
45
+ - "**/*.py"
46
+ ---
47
+ ## When to Use
48
+
49
+ - Writing or reviewing queryset code in any `apps/*/`
50
+ - Adding aggregations, filters, or annotations to a queryset
51
+ - Writing model `save()` overrides or signals
52
+ - Implementing service-layer DB operations
53
+ - Optimizing queries that traverse FK relationships
54
+
55
+ ## Performance Fundamentals
56
+
57
+ ### The N+1 Problem
58
+
59
+ The most common Django mistake: iterating over a queryset and accessing FK
60
+ fields without `select_related`. Each access generates a separate DB query.
61
+
62
+ ```
63
+ # Initial query: 1 query
64
+ products = Product.objects.all()
65
+
66
+ # In the loop: N additional queries (one per product)
67
+ for p in products:
68
+ print(p.category.name) # category_id → SELECT * FROM category WHERE id = ?
69
+
70
+ # Total: 1 + N queries
71
+ ```
72
+
73
+ ### Solution: select_related and prefetch_related
74
+
75
+ | Method | Use | SQL Query |
76
+ | ---------------------------------- | -------------------------- | ------------------------ |
77
+ | `select_related` | FK one-to-one or ManyToOne | Automatic JOIN |
78
+ | `prefetch_related` | Reverse FK or ManyToMany | 2 separate queries |
79
+ | `prefetch_related` with `Prefetch` | Custom queryset | Filtered in nested query |
80
+
81
+ ```python
82
+ # FK traversal — use select_related
83
+ products = Product.objects.select_related("category", "tax")
84
+
85
+ # Multiple FK
86
+ products = Product.objects.select_related("category", "tax", "supplier")
87
+
88
+ # Reverse FK (one-to-many) — use prefetch_related
89
+ categories = Category.objects.prefetch_related("products")
90
+
91
+ # ManyToMany — prefetch_related
92
+ product = Product.objects.prefetch_related("tags").first()
93
+
94
+ # Custom prefetch with filter
95
+ from django.db.models import Prefetch
96
+
97
+ products = Product.objects.prefetch_related(
98
+ Prefetch(
99
+ "order_items",
100
+ queryset=OrderItem.objects.filter(order__status="paid")
101
+ )
102
+ )
103
+ ```
104
+
105
+ ## Query Patterns
106
+
107
+ ### 1. Efficient Filtering
108
+
109
+ ```python
110
+ # Basic filtering
111
+ Product.objects.filter(is_active=True, stock__gt=0)
112
+
113
+ # Filtering with Q objects — OR and negations
114
+ from django.db.models import Q
115
+
116
+ Product.objects.filter(
117
+ Q(is_active=True) & (Q(stock__gt=0) | Q(is_service=True))
118
+ )
119
+
120
+ # Exclusion
121
+ Product.objects.exclude(status="draft")
122
+
123
+ # Filter by nested FK
124
+ Order.objects.filter(employee__store=store)
125
+ ```
126
+
127
+ ### 2. Annotations and Aggregations
128
+
129
+ ```python
130
+ from django.db.models import Count, Sum, Avg, Max, Min, F, Q, Case, When, Value, CharField
131
+ from django.db.models.functions import Coalesce, Concat
132
+
133
+ # Conditional sum with Coalesce (avoids None)
134
+ Product.objects.annotate(
135
+ total_sold=Coalesce(
136
+ Sum(
137
+ "order_items__quantity",
138
+ filter=Q(
139
+ order_items__order__status__in=["paid", "shipped"],
140
+ order_items__order__created_at__gte=cutoff,
141
+ ),
142
+ ),
143
+ 0,
144
+ ),
145
+ )
146
+
147
+ # Use F for arithmetic operations
148
+ Product.objects.update(stock=F("stock") - 1)
149
+
150
+ # Annotation with Case/When for conditional logic
151
+ Product.objects.annotate(
152
+ status_flag=Case(
153
+ When(stock__lte=0, then=Value("out_of_stock")),
154
+ When(stock__lte=10, then=Value("low_stock")),
155
+ default=Value("available"),
156
+ output_field=CharField(),
157
+ ),
158
+ )
159
+
160
+ # Aggregation with filter
161
+ Store.objects.annotate(
162
+ paid_orders=Count("orders", filter=Q(orders__status="paid")),
163
+ )
164
+ ```
165
+
166
+ ### 3. Exists vs Count
167
+
168
+ **Golden rule**: For existence checks, use `Exists`, never `Count`.
169
+
170
+ ```python
171
+ from django.db.models import Exists, OuterRef, Count
172
+
173
+ # WRONG — loads full count
174
+ products = Product.objects.annotate(
175
+ order_count=Count("order_items")
176
+ ).filter(order_count__gt=0)
177
+
178
+ # CORRECT — short-circuits at first match
179
+ products = Product.objects.annotate(
180
+ has_orders=Exists(OrderItem.objects.filter(product=OuterRef("pk")))
181
+ ).filter(has_orders=True)
182
+
183
+ # With more complex subquery
184
+ from django.db.models import Subquery
185
+
186
+ latest_order = Order.objects.filter(
187
+ customer=OuterRef("customer_id")
188
+ ).order_by("-created_at")
189
+
190
+ Customer.objects.annotate(
191
+ last_order_date=Subquery(
192
+ latest_order.values("created_at")[:1]
193
+ )
194
+ )
195
+ ```
196
+
197
+ ### 4. Ordering
198
+
199
+ ```python
200
+ # Basic ordering
201
+ Product.objects.order_by("name")
202
+
203
+ # Ordering with NullsFirst/NullsLast
204
+ Product.objects.order_by(F("price").nulls_last())
205
+
206
+ # Ordering by annotation
207
+ Store.objects.annotate(
208
+ order_count=Count("orders")
209
+ ).order_by("-order_count")
210
+ ```
211
+
212
+ ## Write Operations
213
+
214
+ ### 5. Bulk Operations — Anti-N+1
215
+
216
+ **Never do `save()` inside a loop.** Build lists and use bulk operations:
217
+
218
+ ```python
219
+ # Get with lock once
220
+ products = {p.id: p for p in Product.objects.select_for_update().filter(id__in=ids)}
221
+
222
+ order_items_to_create = []
223
+ products_to_update = []
224
+
225
+ for item in cart:
226
+ product = products[item["id"]]
227
+ product.stock -= item["quantity"]
228
+ order_items_to_create.append(
229
+ OrderItem(order=order, product=product, quantity=item["quantity"], ...)
230
+ )
231
+ products_to_update.append(product)
232
+
233
+ # Bulk create and update
234
+ OrderItem.objects.bulk_create(order_items_to_create)
235
+ Product.objects.bulk_update(products_to_update, ["stock", "updated_at"])
236
+ ```
237
+
238
+ **Bulk operation table:**
239
+
240
+ | Method | Use case | Returns |
241
+ | ------------------------------------------- | -------------------------- | ----------------- |
242
+ | `bulk_create(items)` | Create multiple records | List of objects |
243
+ | `bulk_update(items, fields)` | Update multiple records | None (in-place) |
244
+ | `bulk_create(items, ignore_conflicts=True)` | Ignore duplicates | List (IDs only) |
245
+ | `update()` | Mass update without return | Count of affected |
246
+
247
+ ```python
248
+ # bulk_create with ignore_conflicts
249
+ Product.objects.bulk_create(new_products, ignore_conflicts=True)
250
+
251
+ # bulk update
252
+ Product.objects.filter(category=cat).update(is_active=False)
253
+
254
+ # update with F expressions
255
+ Product.objects.update(stock=F("stock") - 1)
256
+ ```
257
+
258
+ ### 6. Surgical Saves — update_fields
259
+
260
+ **Always pass `update_fields`** when saving a subset of fields:
261
+
262
+ ```python
263
+ # Update single field
264
+ product.stock -= 1
265
+ product.save(update_fields=["stock"])
266
+
267
+ # Update multiple fields
268
+ order.status = "paid"
269
+ order.paid_at = timezone.now()
270
+ order.save(update_fields=["status", "paid_at", "updated_at"])
271
+
272
+ # Don't use in migrations — that uses reconstructor
273
+ # DO use in application code
274
+ ```
275
+
276
+ **Why?** Prevents:
277
+
278
+ - Accidental image reprocessing
279
+ - Unnecessary signals
280
+ - Race conditions on unrelated fields
281
+
282
+ ### 7. Transactions
283
+
284
+ Use `transaction.atomic()` as a context manager, never as a decorator:
285
+
286
+ ```python
287
+ from django.db import transaction
288
+
289
+ # CORRECT — clear context, automatic error handling
290
+ with transaction.atomic():
291
+ order.save()
292
+ OrderItem.objects.bulk_create(items)
293
+ Product.objects.bulk_update(products, ["stock"])
294
+ cart.clear()
295
+
296
+ # With select_for_update inside the transaction
297
+ with transaction.atomic():
298
+ product = Product.objects.select_for_update().get(pk=product_id)
299
+ product.stock -= quantity
300
+ product.save(update_fields=["stock", "updated_at"])
301
+
302
+ # WRONG — decorator hides intent
303
+ @transaction.atomic
304
+ def create_order(...):
305
+ ...
306
+ ```
307
+
308
+ **Isolation levels:**
309
+
310
+ ```python
311
+ from django.db import transaction
312
+
313
+ # Serializable — maximum isolation
314
+ with transaction.atomic():
315
+ ...
316
+
317
+ # Read committed (default) — may cause dirty reads on Edge
318
+ ```
319
+
320
+ ## Multi-Tenant and Upload Paths
321
+
322
+ ### 8. Upload Paths with Schema
323
+
324
+ Every `FileField`/`ImageField` must include `connection.schema_name` to isolate
325
+ files per tenant:
326
+
327
+ ```python
328
+ import os
329
+ from django.db import connection
330
+
331
+ def get_upload_path(instance, filename):
332
+ schema = connection.schema_name
333
+ ext = filename.split(".")[-1].lower()
334
+ safe_name = f"{uuid4().hex}.{ext}"
335
+ return os.path.join(schema, "products", safe_name)
336
+
337
+ def get_thumb_path(instance, filename):
338
+ schema = connection.schema_name
339
+ return os.path.join(schema, "products", "thumbs", filename)
340
+
341
+ class Product(models.Model):
342
+ image = models.ImageField(upload_to=get_upload_path, storage=S3Storage())
343
+ thumbnail = models.ImageField(upload_to=get_thumb_path, blank=True)
344
+ ```
345
+
346
+ ## Advanced Queries
347
+
348
+ ### 9. Subqueries
349
+
350
+ ```python
351
+ from django.db.models import Subquery, OuterRef
352
+
353
+ # Subquery to get last value
354
+ latest_price = ProductPrice.objects.filter(
355
+ product=OuterRef("pk")
356
+ ).order_by("-valid_from").values("price")[:1]
357
+
358
+ Product.objects.annotate(current_price=Subquery(latest_price))
359
+
360
+ # Subquery with aggregate
361
+ order_total = OrderItem.objects.filter(
362
+ order=OuterRef("pk")
363
+ ).values("order").annotate(total=Sum("subtotal")).values("total")
364
+
365
+ Order.objects.annotate(order_total=Subquery(order_total))
366
+ ```
367
+
368
+ ### 10. Raw Queries — WHEN TO USE
369
+
370
+ Avoid raw queries unless necessary. Use when:
371
+
372
+ - Complex aggregation functions not supported by ORM
373
+ - Queries with multiple JOINs manually optimized
374
+ - Very complex queries where ORM generates inefficient SQL
375
+
376
+ ```python
377
+ # With safe parameters (never string interpolation!)
378
+ Product.objects.raw(
379
+ "SELECT * FROM catalog_product WHERE tsvector @@ plainto_tsquery(%s)",
380
+ [search_term]
381
+ )
382
+
383
+ # With cursor for complex cases
384
+ from django.db import connection
385
+
386
+ with connection.cursor() as cursor:
387
+ cursor.execute("SELECT ...", [params])
388
+ results = cursor.fetchall()
389
+ ```
390
+
391
+ ## Migration Commands
392
+
393
+ ```bash
394
+ # Always migrate both schemas
395
+ uv run python manage.py migrate_schemas --shared
396
+ uv run python manage.py migrate_schemas --tenant
397
+
398
+ # Check for pending migrations
399
+ make verifymigrations
400
+
401
+ # Create specific migration
402
+ uv run python manage.py makemigrations catalog --name add_thumbnail
403
+
404
+ # Shows SQL without running
405
+ uv run python manage.py migrate --fake catalog 0003
406
+ ```
407
+
408
+ ## Common Mistakes and How to Avoid Them
409
+
410
+ ### N+1 in templates
411
+
412
+ ```python
413
+ # WRONG — N queries
414
+ {% for product in products %}
415
+ {{ product.category.name }}
416
+ {% endfor %}
417
+
418
+ # CORRECT — 1 query with select_related
419
+ products = Product.objects.select_related("category")
420
+ ```
421
+
422
+ ### ForeignKey without related_name
423
+
424
+ ```python
425
+ # WRONG
426
+ class Order(models.Model):
427
+ customer = models.ForeignKey("users.User", on_delete=...)
428
+
429
+ # CORRECT — always add related_name
430
+ class Order(models.Model):
431
+ customer = models.ForeignKey(
432
+ "users.User",
433
+ on_delete=models.CASCADE,
434
+ related_name="orders",
435
+ )
436
+ ```
437
+
438
+ ### Queries in signals
439
+
440
+ ```python
441
+ # WRONG — signal makes additional query
442
+ @receiver(post_save, sender=Order)
443
+ def on_order_save(sender, instance, **kwargs):
444
+ customer = instance.customer # Additional query!
445
+ send_email(customer.email, ...)
446
+
447
+ # CORRECT — pass the object, not the ID
448
+ @receiver(post_save, sender=Order)
449
+ def on_order_save(sender, instance, created, **kwargs):
450
+ if created:
451
+ customer = instance.customer
452
+ send_email(customer.email, ...)
453
+ ```
454
+
455
+ ## Resources
456
+
457
+ - **Bulk write pattern**: `apps/orders/services.py`, `apps/pos/views.py` lines
458
+ 216-246
459
+ - **Annotation examples**: `apps/pos/views.py` lines 59-75
460
+ - **Exists usage**: `apps/catalog/views.py` line 112
461
+ - **Upload paths**: `apps/catalog/models.py`
462
+ - **Django ORM docs**:
463
+ <https://docs.djangoproject.com/en/5.2/topics/db/queries/>