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