cc-codeconductor 0.2.1

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 (93) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +319 -0
  3. package/dist/index.js +12741 -0
  4. package/package.json +51 -0
  5. package/policy.yml +124 -0
  6. package/presets/claude/CLAUDE.md +598 -0
  7. package/presets/claude/commands/cc/feature.md +115 -0
  8. package/presets/claude/commands/cc/fix.md +121 -0
  9. package/presets/claude/commands/cc/refactor.md +148 -0
  10. package/presets/claude/commands/cc/review.md +126 -0
  11. package/presets/claude/commands/cc/tdd-cycle.md +226 -0
  12. package/presets/claude/commands/cc/test-plan.md +138 -0
  13. package/presets/claude/settings.json +37 -0
  14. package/presets/claude/skills/api-versioning/SKILL.md +389 -0
  15. package/presets/claude/skills/django-orm/SKILL.md +455 -0
  16. package/presets/claude/skills/django-testing/SKILL.md +409 -0
  17. package/presets/claude/skills/jpa-postgres/SKILL.md +618 -0
  18. package/presets/claude/skills/python/SKILL.md +606 -0
  19. package/presets/claude/skills/python-django-stack/SKILL.md +492 -0
  20. package/presets/claude/skills/python-fastapi-stack/SKILL.md +457 -0
  21. package/presets/claude/skills/spring-boot-feature/SKILL.md +558 -0
  22. package/presets/claude/skills/spring-boot-kotlin/SKILL.md +400 -0
  23. package/presets/claude/skills/sqlalchemy/SKILL.md +466 -0
  24. package/presets/claude/skills/testing-strategy/SKILL.md +479 -0
  25. package/presets/codex/AGENTS.md +883 -0
  26. package/presets/codex/README.md +102 -0
  27. package/presets/codex/skills/api-versioning/SKILL.md +389 -0
  28. package/presets/codex/skills/django-orm/SKILL.md +455 -0
  29. package/presets/codex/skills/django-testing/SKILL.md +409 -0
  30. package/presets/codex/skills/jpa-postgres/SKILL.md +618 -0
  31. package/presets/codex/skills/python/SKILL.md +606 -0
  32. package/presets/codex/skills/python-django-stack/SKILL.md +492 -0
  33. package/presets/codex/skills/python-fastapi-stack/SKILL.md +457 -0
  34. package/presets/codex/skills/spring-boot-feature/SKILL.md +558 -0
  35. package/presets/codex/skills/spring-boot-kotlin/SKILL.md +400 -0
  36. package/presets/codex/skills/sqlalchemy/SKILL.md +466 -0
  37. package/presets/codex/skills/testing-strategy/SKILL.md +479 -0
  38. package/presets/opencode/README.md +191 -0
  39. package/presets/opencode/agents/architect.md +133 -0
  40. package/presets/opencode/agents/docs.md +113 -0
  41. package/presets/opencode/agents/implementer.md +116 -0
  42. package/presets/opencode/agents/orchestrator.md +248 -0
  43. package/presets/opencode/agents/repo-explorer.md +119 -0
  44. package/presets/opencode/agents/reviewer.md +130 -0
  45. package/presets/opencode/agents/task-coach.md +103 -0
  46. package/presets/opencode/agents/tester.md +269 -0
  47. package/presets/opencode/commands/cc-feature.md +114 -0
  48. package/presets/opencode/commands/cc-fix.md +123 -0
  49. package/presets/opencode/commands/cc-refactor.md +148 -0
  50. package/presets/opencode/commands/cc-review.md +141 -0
  51. package/presets/opencode/commands/cc-tdd-cycle.md +225 -0
  52. package/presets/opencode/commands/cc-test-plan.md +144 -0
  53. package/presets/opencode/opencode.jsonc +89 -0
  54. package/presets/opencode/prompts/v0.1.0/architect.md +213 -0
  55. package/presets/opencode/prompts/v0.1.0/docs.md +181 -0
  56. package/presets/opencode/prompts/v0.1.0/implementer.md +154 -0
  57. package/presets/opencode/prompts/v0.1.0/orchestrator.md +169 -0
  58. package/presets/opencode/prompts/v0.1.0/repo-explorer.md +102 -0
  59. package/presets/opencode/prompts/v0.1.0/reviewer.md +183 -0
  60. package/presets/opencode/prompts/v0.1.0/task-coach.md +142 -0
  61. package/presets/opencode/prompts/v0.1.0/tester.md +160 -0
  62. package/presets/opencode/prompts/v0.2.0/architect.md +219 -0
  63. package/presets/opencode/prompts/v0.2.0/docs.md +187 -0
  64. package/presets/opencode/prompts/v0.2.0/implementer.md +160 -0
  65. package/presets/opencode/prompts/v0.2.0/orchestrator.md +238 -0
  66. package/presets/opencode/prompts/v0.2.0/repo-explorer.md +108 -0
  67. package/presets/opencode/prompts/v0.2.0/reviewer.md +190 -0
  68. package/presets/opencode/prompts/v0.2.0/task-coach.md +153 -0
  69. package/presets/opencode/prompts/v0.2.0/tester.md +249 -0
  70. package/presets/opencode/skills/api-versioning/SKILL.md +388 -0
  71. package/presets/opencode/skills/astro/SKILL.md +313 -0
  72. package/presets/opencode/skills/code-review/SKILL.md +202 -0
  73. package/presets/opencode/skills/django-orm/SKILL.md +455 -0
  74. package/presets/opencode/skills/django-testing/SKILL.md +408 -0
  75. package/presets/opencode/skills/django-uv/SKILL.md +400 -0
  76. package/presets/opencode/skills/jpa-postgres/SKILL.md +617 -0
  77. package/presets/opencode/skills/nextjs-typescript/SKILL.md +385 -0
  78. package/presets/opencode/skills/python/SKILL.md +605 -0
  79. package/presets/opencode/skills/python-django-stack/SKILL.md +491 -0
  80. package/presets/opencode/skills/python-fastapi-stack/SKILL.md +456 -0
  81. package/presets/opencode/skills/security/SKILL.md +376 -0
  82. package/presets/opencode/skills/spring-boot-feature/SKILL.md +557 -0
  83. package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +399 -0
  84. package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +470 -0
  85. package/presets/opencode/skills/sqlalchemy/SKILL.md +465 -0
  86. package/presets/opencode/skills/testing-tdd/SKILL.md +586 -0
  87. package/src/presets/council/council.yml +59 -0
  88. package/src/presets/manifests/claude.yml +23 -0
  89. package/src/presets/manifests/codex.yml +12 -0
  90. package/src/presets/manifests/opencode.yml +18 -0
  91. package/src/presets/models/claude.yml +37 -0
  92. package/src/presets/models/codex.yml +38 -0
  93. package/src/presets/models/opencode.yml +37 -0
@@ -0,0 +1,202 @@
1
+ ---
2
+ id: code-review
3
+ version: 1.0.0
4
+ name: Code Review
5
+ description: >
6
+ Provides a structured framework for conducting thorough code reviews covering correctness, security, architecture alignment, performance, and technical debt.
7
+
8
+ compatibility:
9
+ tools: [claude, codex, opencode]
10
+ stacks:
11
+ languages: []
12
+ frameworks: []
13
+
14
+ risk:
15
+ level: low
16
+ can_execute_shell: false
17
+ can_modify_files: true
18
+ requires_network: false
19
+
20
+ inputs: []
21
+
22
+ outputs: []
23
+
24
+ quality:
25
+ reviewed_by: codeconductor-core
26
+ version: 0.1.0
27
+ ---
28
+
29
+
30
+
31
+ # Code Review
32
+
33
+ ## Review Axes
34
+
35
+ Every finding must reference exactly one axis. This prevents vague feedback and
36
+ makes it actionable.
37
+
38
+ | Axis | What to check |
39
+ |------|--------------|
40
+ | **Correctness** | Does the logic handle all cases in the acceptance criteria? |
41
+ | **Security** | Injection vectors, secret exposure, auth bypasses, insecure defaults |
42
+ | **Architecture** | Does the code follow existing patterns and module boundaries? |
43
+ | **Performance** | N+1 queries, unnecessary allocations, blocking I/O in hot paths |
44
+ | **Error handling** | Are failure cases handled explicitly and safely? |
45
+ | **Test coverage** | Do tests verify the acceptance criteria, not just happy paths? |
46
+ | **Scope** | Are there changes outside the stated task boundary? |
47
+ | **Technical debt** | Does the implementation introduce debt without acknowledging it? |
48
+
49
+ ## Finding Categories
50
+
51
+ ### CRITICAL — must be resolved before merge
52
+
53
+ - Logic failures that violate acceptance criteria
54
+ - Security vulnerabilities (injection, auth bypass, secret exposure)
55
+ - Data loss or corruption risk
56
+ - Breaking changes to public API or shared contracts
57
+ - Missing error handling that causes silent failures
58
+
59
+ ### WARNING — should be resolved before merge
60
+
61
+ - Missing edge case coverage in tests
62
+ - Scope creep (changes outside the task boundary)
63
+ - Pattern inconsistency that will confuse future maintainers
64
+ - Performance issue that will degrade under load
65
+ - Missing input validation at system boundaries
66
+
67
+ ### SUGGESTION — optional improvement
68
+
69
+ - Naming clarity improvements
70
+ - Extracting a reusable abstraction
71
+ - Test readability
72
+ - Documentation gaps on non-obvious behavior
73
+
74
+ ## Review Report Format
75
+
76
+ ```markdown
77
+ ## Review Report
78
+
79
+ **Task**: [objective from task card]
80
+ **Verdict**: [approved | approved with warnings | blocked]
81
+
82
+ ---
83
+
84
+ ### CRITICAL
85
+
86
+ - [ ] [C1] `path/to/file:line` — [description]
87
+ Axis: [axis] | Evidence: `[quoted code]` | Required action: [what must change]
88
+
89
+ _(none)_ if no critical findings
90
+
91
+ ### WARNING
92
+
93
+ - [ ] [W1] `path/to/file:line` — [description]
94
+ Axis: [axis] | Recommended action: [what should change]
95
+
96
+ _(none)_ if no warning findings
97
+
98
+ ### SUGGESTION
99
+
100
+ - [ ] [S1] — [description] | Rationale: [brief reason]
101
+
102
+ _(none)_ if no suggestions
103
+
104
+ ### Summary
105
+
106
+ - Critical: [count] | Warning: [count] | Suggestion: [count]
107
+ - **Verdict justification**: [one sentence]
108
+ ```
109
+
110
+ **Verdict rules:**
111
+
112
+ - `blocked` — any CRITICAL finding present
113
+ - `approved with warnings` — no CRITICAL, at least one WARNING
114
+ - `approved` — no CRITICAL, no WARNING
115
+
116
+ ## What to Check — by Axis
117
+
118
+ ### Correctness
119
+
120
+ - Does every acceptance criterion have a corresponding code path?
121
+ - Are null/empty/zero values handled explicitly?
122
+ - Are boundary conditions checked (off-by-one, empty collections, max values)?
123
+ - Do conditional branches cover all cases (exhaustive when/switch)?
124
+
125
+ ### Security
126
+
127
+ ```text
128
+ Injection: SQL, command, LDAP, XML — is input sanitized or parameterized?
129
+ Auth: is the endpoint protected? are role checks correct?
130
+ Secrets: no hardcoded credentials, tokens, or keys in source
131
+ Headers: are security headers set (CSP, HSTS, X-Frame-Options)?
132
+ CORS: is the origin whitelist explicit, not wildcard?
133
+ Dependencies: are new dependencies from trusted sources?
134
+ ```
135
+
136
+ ### Performance
137
+
138
+ ```text
139
+ N+1 queries: does a loop call the database per iteration?
140
+ Eager loading: are JOINs or includes used where needed?
141
+ Pagination: are all list endpoints paginated?
142
+ Caching: is expensive computation cached at an appropriate layer?
143
+ Index: do new query filters have corresponding DB indexes?
144
+ ```
145
+
146
+ ### Error Handling
147
+
148
+ ```text
149
+ Are exceptions caught at the right layer (not swallowed silently)?
150
+ Is the error response format consistent with the rest of the API?
151
+ Is the original exception logged before translating to a user-facing error?
152
+ Are transient failures retried with backoff, or propagated immediately?
153
+ ```
154
+
155
+ ## What NOT to Flag
156
+
157
+ **Personal style preferences.** If the code follows the project's established
158
+ conventions, do not flag it because you would have written it differently.
159
+
160
+ **Trivial naming.** Variable names are a SUGGESTION at most, never a WARNING.
161
+ Do not block a PR over naming unless the name is genuinely misleading.
162
+
163
+ **Tests for trivial code.** Do not demand tests for getters, setters, or data
164
+ class construction. Test behavior, not boilerplate.
165
+
166
+ **Refactors not in scope.** If you notice an improvement opportunity outside
167
+ the task boundary, log it as a SUGGESTION. Do not block the PR for work that
168
+ was not requested.
169
+
170
+ **Framework defaults.** Do not second-guess framework conventions (Spring
171
+ dependency injection, Next.js file-based routing). Trust the framework.
172
+
173
+ ## Exact Diff Recommendations
174
+
175
+ When a finding requires a code change, provide the exact replacement:
176
+
177
+ ```text
178
+ # Current code (path/to/file:42)
179
+ val user = userRepository.findById(id)
180
+ return user.name // NPE if not found
181
+
182
+ # Recommended fix
183
+ val user = userRepository.findById(id)
184
+ ?: throw NotFoundException("User $id not found")
185
+ return user.name
186
+ ```
187
+
188
+ Vague instructions ("handle the null case") force the author to guess your
189
+ intent. Exact diffs remove ambiguity and speed up the review cycle.
190
+
191
+ ## Prioritization Order
192
+
193
+ When multiple findings exist, address them in this order:
194
+
195
+ 1. Security — security issues can silently compromise users or data
196
+ 2. Correctness — broken behavior defeats the purpose of the change
197
+ 3. Error handling — silent failures are hard to debug in production
198
+ 4. Test coverage — gaps mean regressions will be invisible
199
+ 5. Performance — degrade gradually; fix before it causes incidents
200
+ 6. Architecture — inconsistency compounds over time
201
+ 7. Technical debt — acknowledge and track; do not always block for it
202
+ 8. Suggestions — apply at author's discretion
@@ -0,0 +1,455 @@
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
+ compatibility:
11
+ tools: [claude, codex, opencode]
12
+ stacks:
13
+ languages: [python]
14
+ frameworks: [django, django-tenants]
15
+
16
+ risk:
17
+ level: medium
18
+ can_execute_shell: true
19
+ can_modify_files: true
20
+ requires_network: false
21
+
22
+ inputs:
23
+ - model files (models.py)
24
+ - service files (services.py)
25
+ - view files with queryset logic
26
+ - existing queryset or bulk-operation code
27
+
28
+ outputs:
29
+ - optimized queryset patterns with select_related / prefetch_related
30
+ - bulk_create and bulk_update implementations
31
+ - transaction-safe service methods
32
+ - annotated and aggregated querysets
33
+ - migration commands for multi-schema projects
34
+
35
+ quality:
36
+ reviewed_by: codeconductor-core
37
+ version: 0.1.0
38
+ ---
39
+ ## When to Use
40
+
41
+ - Writing or reviewing queryset code in any `apps/*/`
42
+ - Adding aggregations, filters, or annotations to a queryset
43
+ - Writing model `save()` overrides or signals
44
+ - Implementing service-layer DB operations
45
+ - Optimizing queries that traverse FK relationships
46
+
47
+ ## Performance Fundamentals
48
+
49
+ ### The N+1 Problem
50
+
51
+ The most common Django mistake: iterating over a queryset and accessing FK
52
+ fields without `select_related`. Each access generates a separate DB query.
53
+
54
+ ```
55
+ # Initial query: 1 query
56
+ products = Product.objects.all()
57
+
58
+ # In the loop: N additional queries (one per product)
59
+ for p in products:
60
+ print(p.category.name) # category_id → SELECT * FROM category WHERE id = ?
61
+
62
+ # Total: 1 + N queries
63
+ ```
64
+
65
+ ### Solution: select_related and prefetch_related
66
+
67
+ | Method | Use | SQL Query |
68
+ | ---------------------------------- | -------------------------- | ------------------------ |
69
+ | `select_related` | FK one-to-one or ManyToOne | Automatic JOIN |
70
+ | `prefetch_related` | Reverse FK or ManyToMany | 2 separate queries |
71
+ | `prefetch_related` with `Prefetch` | Custom queryset | Filtered in nested query |
72
+
73
+ ```python
74
+ # FK traversal — use select_related
75
+ products = Product.objects.select_related("category", "tax")
76
+
77
+ # Multiple FK
78
+ products = Product.objects.select_related("category", "tax", "supplier")
79
+
80
+ # Reverse FK (one-to-many) — use prefetch_related
81
+ categories = Category.objects.prefetch_related("products")
82
+
83
+ # ManyToMany — prefetch_related
84
+ product = Product.objects.prefetch_related("tags").first()
85
+
86
+ # Custom prefetch with filter
87
+ from django.db.models import Prefetch
88
+
89
+ products = Product.objects.prefetch_related(
90
+ Prefetch(
91
+ "order_items",
92
+ queryset=OrderItem.objects.filter(order__status="paid")
93
+ )
94
+ )
95
+ ```
96
+
97
+ ## Query Patterns
98
+
99
+ ### 1. Efficient Filtering
100
+
101
+ ```python
102
+ # Basic filtering
103
+ Product.objects.filter(is_active=True, stock__gt=0)
104
+
105
+ # Filtering with Q objects — OR and negations
106
+ from django.db.models import Q
107
+
108
+ Product.objects.filter(
109
+ Q(is_active=True) & (Q(stock__gt=0) | Q(is_service=True))
110
+ )
111
+
112
+ # Exclusion
113
+ Product.objects.exclude(status="draft")
114
+
115
+ # Filter by nested FK
116
+ Order.objects.filter(employee__store=store)
117
+ ```
118
+
119
+ ### 2. Annotations and Aggregations
120
+
121
+ ```python
122
+ from django.db.models import Count, Sum, Avg, Max, Min, F, Q, Case, When, Value, CharField
123
+ from django.db.models.functions import Coalesce, Concat
124
+
125
+ # Conditional sum with Coalesce (avoids None)
126
+ Product.objects.annotate(
127
+ total_sold=Coalesce(
128
+ Sum(
129
+ "order_items__quantity",
130
+ filter=Q(
131
+ order_items__order__status__in=["paid", "shipped"],
132
+ order_items__order__created_at__gte=cutoff,
133
+ ),
134
+ ),
135
+ 0,
136
+ ),
137
+ )
138
+
139
+ # Use F for arithmetic operations
140
+ Product.objects.update(stock=F("stock") - 1)
141
+
142
+ # Annotation with Case/When for conditional logic
143
+ Product.objects.annotate(
144
+ status_flag=Case(
145
+ When(stock__lte=0, then=Value("out_of_stock")),
146
+ When(stock__lte=10, then=Value("low_stock")),
147
+ default=Value("available"),
148
+ output_field=CharField(),
149
+ ),
150
+ )
151
+
152
+ # Aggregation with filter
153
+ Store.objects.annotate(
154
+ paid_orders=Count("orders", filter=Q(orders__status="paid")),
155
+ )
156
+ ```
157
+
158
+ ### 3. Exists vs Count
159
+
160
+ **Golden rule**: For existence checks, use `Exists`, never `Count`.
161
+
162
+ ```python
163
+ from django.db.models import Exists, OuterRef, Count
164
+
165
+ # WRONG — loads full count
166
+ products = Product.objects.annotate(
167
+ order_count=Count("order_items")
168
+ ).filter(order_count__gt=0)
169
+
170
+ # CORRECT — short-circuits at first match
171
+ products = Product.objects.annotate(
172
+ has_orders=Exists(OrderItem.objects.filter(product=OuterRef("pk")))
173
+ ).filter(has_orders=True)
174
+
175
+ # With more complex subquery
176
+ from django.db.models import Subquery
177
+
178
+ latest_order = Order.objects.filter(
179
+ customer=OuterRef("customer_id")
180
+ ).order_by("-created_at")
181
+
182
+ Customer.objects.annotate(
183
+ last_order_date=Subquery(
184
+ latest_order.values("created_at")[:1]
185
+ )
186
+ )
187
+ ```
188
+
189
+ ### 4. Ordering
190
+
191
+ ```python
192
+ # Basic ordering
193
+ Product.objects.order_by("name")
194
+
195
+ # Ordering with NullsFirst/NullsLast
196
+ Product.objects.order_by(F("price").nulls_last())
197
+
198
+ # Ordering by annotation
199
+ Store.objects.annotate(
200
+ order_count=Count("orders")
201
+ ).order_by("-order_count")
202
+ ```
203
+
204
+ ## Write Operations
205
+
206
+ ### 5. Bulk Operations — Anti-N+1
207
+
208
+ **Never do `save()` inside a loop.** Build lists and use bulk operations:
209
+
210
+ ```python
211
+ # Get with lock once
212
+ products = {p.id: p for p in Product.objects.select_for_update().filter(id__in=ids)}
213
+
214
+ order_items_to_create = []
215
+ products_to_update = []
216
+
217
+ for item in cart:
218
+ product = products[item["id"]]
219
+ product.stock -= item["quantity"]
220
+ order_items_to_create.append(
221
+ OrderItem(order=order, product=product, quantity=item["quantity"], ...)
222
+ )
223
+ products_to_update.append(product)
224
+
225
+ # Bulk create and update
226
+ OrderItem.objects.bulk_create(order_items_to_create)
227
+ Product.objects.bulk_update(products_to_update, ["stock", "updated_at"])
228
+ ```
229
+
230
+ **Bulk operation table:**
231
+
232
+ | Method | Use case | Returns |
233
+ | ------------------------------------------- | -------------------------- | ----------------- |
234
+ | `bulk_create(items)` | Create multiple records | List of objects |
235
+ | `bulk_update(items, fields)` | Update multiple records | None (in-place) |
236
+ | `bulk_create(items, ignore_conflicts=True)` | Ignore duplicates | List (IDs only) |
237
+ | `update()` | Mass update without return | Count of affected |
238
+
239
+ ```python
240
+ # bulk_create with ignore_conflicts
241
+ Product.objects.bulk_create(new_products, ignore_conflicts=True)
242
+
243
+ # bulk update
244
+ Product.objects.filter(category=cat).update(is_active=False)
245
+
246
+ # update with F expressions
247
+ Product.objects.update(stock=F("stock") - 1)
248
+ ```
249
+
250
+ ### 6. Surgical Saves — update_fields
251
+
252
+ **Always pass `update_fields`** when saving a subset of fields:
253
+
254
+ ```python
255
+ # Update single field
256
+ product.stock -= 1
257
+ product.save(update_fields=["stock"])
258
+
259
+ # Update multiple fields
260
+ order.status = "paid"
261
+ order.paid_at = timezone.now()
262
+ order.save(update_fields=["status", "paid_at", "updated_at"])
263
+
264
+ # Don't use in migrations — that uses reconstructor
265
+ # DO use in application code
266
+ ```
267
+
268
+ **Why?** Prevents:
269
+
270
+ - Accidental image reprocessing
271
+ - Unnecessary signals
272
+ - Race conditions on unrelated fields
273
+
274
+ ### 7. Transactions
275
+
276
+ Use `transaction.atomic()` as a context manager, never as a decorator:
277
+
278
+ ```python
279
+ from django.db import transaction
280
+
281
+ # CORRECT — clear context, automatic error handling
282
+ with transaction.atomic():
283
+ order.save()
284
+ OrderItem.objects.bulk_create(items)
285
+ Product.objects.bulk_update(products, ["stock"])
286
+ cart.clear()
287
+
288
+ # With select_for_update inside the transaction
289
+ with transaction.atomic():
290
+ product = Product.objects.select_for_update().get(pk=product_id)
291
+ product.stock -= quantity
292
+ product.save(update_fields=["stock", "updated_at"])
293
+
294
+ # WRONG — decorator hides intent
295
+ @transaction.atomic
296
+ def create_order(...):
297
+ ...
298
+ ```
299
+
300
+ **Isolation levels:**
301
+
302
+ ```python
303
+ from django.db import transaction
304
+
305
+ # Serializable — maximum isolation
306
+ with transaction.atomic():
307
+ ...
308
+
309
+ # Read committed (default) — may cause dirty reads on Edge
310
+ ```
311
+
312
+ ## Multi-Tenant and Upload Paths
313
+
314
+ ### 8. Upload Paths with Schema
315
+
316
+ Every `FileField`/`ImageField` must include `connection.schema_name` to isolate
317
+ files per tenant:
318
+
319
+ ```python
320
+ import os
321
+ from django.db import connection
322
+
323
+ def get_upload_path(instance, filename):
324
+ schema = connection.schema_name
325
+ ext = filename.split(".")[-1].lower()
326
+ safe_name = f"{uuid4().hex}.{ext}"
327
+ return os.path.join(schema, "products", safe_name)
328
+
329
+ def get_thumb_path(instance, filename):
330
+ schema = connection.schema_name
331
+ return os.path.join(schema, "products", "thumbs", filename)
332
+
333
+ class Product(models.Model):
334
+ image = models.ImageField(upload_to=get_upload_path, storage=S3Storage())
335
+ thumbnail = models.ImageField(upload_to=get_thumb_path, blank=True)
336
+ ```
337
+
338
+ ## Advanced Queries
339
+
340
+ ### 9. Subqueries
341
+
342
+ ```python
343
+ from django.db.models import Subquery, OuterRef
344
+
345
+ # Subquery to get last value
346
+ latest_price = ProductPrice.objects.filter(
347
+ product=OuterRef("pk")
348
+ ).order_by("-valid_from").values("price")[:1]
349
+
350
+ Product.objects.annotate(current_price=Subquery(latest_price))
351
+
352
+ # Subquery with aggregate
353
+ order_total = OrderItem.objects.filter(
354
+ order=OuterRef("pk")
355
+ ).values("order").annotate(total=Sum("subtotal")).values("total")
356
+
357
+ Order.objects.annotate(order_total=Subquery(order_total))
358
+ ```
359
+
360
+ ### 10. Raw Queries — WHEN TO USE
361
+
362
+ Avoid raw queries unless necessary. Use when:
363
+
364
+ - Complex aggregation functions not supported by ORM
365
+ - Queries with multiple JOINs manually optimized
366
+ - Very complex queries where ORM generates inefficient SQL
367
+
368
+ ```python
369
+ # With safe parameters (never string interpolation!)
370
+ Product.objects.raw(
371
+ "SELECT * FROM catalog_product WHERE tsvector @@ plainto_tsquery(%s)",
372
+ [search_term]
373
+ )
374
+
375
+ # With cursor for complex cases
376
+ from django.db import connection
377
+
378
+ with connection.cursor() as cursor:
379
+ cursor.execute("SELECT ...", [params])
380
+ results = cursor.fetchall()
381
+ ```
382
+
383
+ ## Migration Commands
384
+
385
+ ```bash
386
+ # Always migrate both schemas
387
+ uv run python manage.py migrate_schemas --shared
388
+ uv run python manage.py migrate_schemas --tenant
389
+
390
+ # Check for pending migrations
391
+ make verifymigrations
392
+
393
+ # Create specific migration
394
+ uv run python manage.py makemigrations catalog --name add_thumbnail
395
+
396
+ # Shows SQL without running
397
+ uv run python manage.py migrate --fake catalog 0003
398
+ ```
399
+
400
+ ## Common Mistakes and How to Avoid Them
401
+
402
+ ### N+1 in templates
403
+
404
+ ```python
405
+ # WRONG — N queries
406
+ {% for product in products %}
407
+ {{ product.category.name }}
408
+ {% endfor %}
409
+
410
+ # CORRECT — 1 query with select_related
411
+ products = Product.objects.select_related("category")
412
+ ```
413
+
414
+ ### ForeignKey without related_name
415
+
416
+ ```python
417
+ # WRONG
418
+ class Order(models.Model):
419
+ customer = models.ForeignKey("users.User", on_delete=...)
420
+
421
+ # CORRECT — always add related_name
422
+ class Order(models.Model):
423
+ customer = models.ForeignKey(
424
+ "users.User",
425
+ on_delete=models.CASCADE,
426
+ related_name="orders",
427
+ )
428
+ ```
429
+
430
+ ### Queries in signals
431
+
432
+ ```python
433
+ # WRONG — signal makes additional query
434
+ @receiver(post_save, sender=Order)
435
+ def on_order_save(sender, instance, **kwargs):
436
+ customer = instance.customer # Additional query!
437
+ send_email(customer.email, ...)
438
+
439
+ # CORRECT — pass the object, not the ID
440
+ @receiver(post_save, sender=Order)
441
+ def on_order_save(sender, instance, created, **kwargs):
442
+ if created:
443
+ customer = instance.customer
444
+ send_email(customer.email, ...)
445
+ ```
446
+
447
+ ## Resources
448
+
449
+ - **Bulk write pattern**: `apps/orders/services.py`, `apps/pos/views.py` lines
450
+ 216-246
451
+ - **Annotation examples**: `apps/pos/views.py` lines 59-75
452
+ - **Exists usage**: `apps/catalog/views.py` line 112
453
+ - **Upload paths**: `apps/catalog/models.py`
454
+ - **Django ORM docs**:
455
+ <https://docs.djangoproject.com/en/5.2/topics/db/queries/>