@dv.nghiem/flowdeck 0.2.3 → 0.3.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 (100) hide show
  1. package/README.md +24 -41
  2. package/dist/hooks/memory-hook.d.ts +21 -0
  3. package/dist/hooks/memory-hook.d.ts.map +1 -0
  4. package/dist/hooks/orchestrator-guard-hook.d.ts +2 -1
  5. package/dist/hooks/orchestrator-guard-hook.d.ts.map +1 -1
  6. package/dist/hooks/todo-hook.d.ts +1 -7
  7. package/dist/hooks/todo-hook.d.ts.map +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +649 -310
  10. package/dist/services/memory-store.d.ts +40 -0
  11. package/dist/services/memory-store.d.ts.map +1 -0
  12. package/dist/services/telemetry.d.ts +1 -1
  13. package/dist/services/telemetry.d.ts.map +1 -1
  14. package/dist/tools/memory-search.d.ts +3 -0
  15. package/dist/tools/memory-search.d.ts.map +1 -0
  16. package/docs/commands/fd-doctor.md +21 -0
  17. package/docs/commands/fd-quick.md +33 -0
  18. package/docs/commands/fd-reflect.md +23 -0
  19. package/docs/commands/fd-status.md +31 -0
  20. package/docs/commands/fd-translate-intent.md +17 -0
  21. package/docs/commands.md +209 -271
  22. package/docs/configuration.md +2 -1
  23. package/docs/index.md +22 -28
  24. package/docs/memory.md +69 -0
  25. package/docs/quick-start.md +1 -1
  26. package/docs/workflows.md +72 -320
  27. package/package.json +1 -2
  28. package/src/commands/fd-deploy-check.md +189 -34
  29. package/src/commands/fd-discuss.md +44 -6
  30. package/src/commands/fd-fix-bug.md +47 -20
  31. package/src/commands/fd-map-codebase.md +66 -18
  32. package/src/commands/fd-multi-repo.md +130 -6
  33. package/src/commands/fd-new-feature.md +164 -21
  34. package/src/commands/fd-new-project.md +14 -1
  35. package/src/commands/fd-plan.md +66 -44
  36. package/src/commands/fd-quick.md +60 -0
  37. package/src/commands/fd-reflect.md +41 -2
  38. package/src/commands/fd-status.md +84 -0
  39. package/src/commands/fd-write-docs.md +55 -23
  40. package/src/rules/README.md +8 -7
  41. package/src/skills/agent-harness-construction/SKILL.md +227 -0
  42. package/src/skills/api-design/SKILL.md +5 -0
  43. package/src/skills/backend-patterns/SKILL.md +105 -0
  44. package/src/skills/clean-architecture/SKILL.md +85 -0
  45. package/src/skills/cqrs/SKILL.md +230 -0
  46. package/src/skills/ddd-architecture/SKILL.md +104 -0
  47. package/src/skills/django-patterns/SKILL.md +304 -0
  48. package/src/skills/django-tdd/SKILL.md +297 -0
  49. package/src/skills/event-driven-architecture/SKILL.md +152 -0
  50. package/src/skills/frontend-pattern/SKILL.md +159 -0
  51. package/src/skills/hexagonal-architecture/SKILL.md +80 -0
  52. package/src/skills/layered-architecture/SKILL.md +64 -0
  53. package/src/skills/postgres-patterns/SKILL.md +74 -0
  54. package/src/skills/python-patterns/SKILL.md +5 -0
  55. package/src/skills/saga-architecture/SKILL.md +113 -0
  56. package/dist/tools/run-parallel.d.ts +0 -4
  57. package/dist/tools/run-parallel.d.ts.map +0 -1
  58. package/docs/command-migration.md +0 -175
  59. package/docs/commands/fd-analyze-change.md +0 -107
  60. package/docs/commands/fd-dashboard.md +0 -11
  61. package/docs/commands/fd-evaluate-risk.md +0 -134
  62. package/docs/commands/fd-guarded-edit.md +0 -105
  63. package/docs/commands/fd-progress.md +0 -11
  64. package/docs/commands/fd-review-code.md +0 -29
  65. package/docs/commands/fd-roadmap.md +0 -10
  66. package/docs/commands/fd-settings.md +0 -10
  67. package/docs/parallel-execution.md +0 -227
  68. package/src/commands/fd-analyze-change.md +0 -57
  69. package/src/commands/fd-approve.md +0 -64
  70. package/src/commands/fd-blast-radius.md +0 -49
  71. package/src/commands/fd-dashboard.md +0 -57
  72. package/src/commands/fd-evaluate-risk.md +0 -62
  73. package/src/commands/fd-guarded-edit.md +0 -69
  74. package/src/commands/fd-impact-radar.md +0 -51
  75. package/src/commands/fd-learn.md +0 -36
  76. package/src/commands/fd-progress.md +0 -50
  77. package/src/commands/fd-regression-predict.md +0 -57
  78. package/src/commands/fd-review-code.md +0 -62
  79. package/src/commands/fd-review-route.md +0 -54
  80. package/src/commands/fd-roadmap.md +0 -46
  81. package/src/commands/fd-settings.md +0 -57
  82. package/src/commands/fd-test-gap.md +0 -54
  83. package/src/commands/fd-volatility-map.md +0 -64
  84. package/src/commands/fd-workspace-status.md +0 -34
  85. package/src/skills/parallel-execute/SKILL.md +0 -92
  86. package/src/workflows/debug-flow.md +0 -119
  87. package/src/workflows/deploy-check-flow.md +0 -98
  88. package/src/workflows/discuss-flow.md +0 -97
  89. package/src/workflows/execute-flow.md +0 -233
  90. package/src/workflows/execute-phase.md +0 -145
  91. package/src/workflows/fix-bug-flow.md +0 -210
  92. package/src/workflows/map-codebase-flow.md +0 -92
  93. package/src/workflows/multi-repo-flow.md +0 -226
  94. package/src/workflows/parallel-execution-flow.md +0 -236
  95. package/src/workflows/plan-flow.md +0 -126
  96. package/src/workflows/plan-phase.md +0 -101
  97. package/src/workflows/refactor-flow.md +0 -122
  98. package/src/workflows/review-code-flow.md +0 -105
  99. package/src/workflows/spec-driven-flow.md +0 -43
  100. package/src/workflows/write-docs-flow.md +0 -95
@@ -0,0 +1,230 @@
1
+ # CQRS (Command Query Responsibility Segregation)
2
+
3
+ ## When to Activate
4
+
5
+ Activate when:
6
+ - Designing read-heavy or write-heavy systems separately
7
+ - Implementing complex domain models with divergent read/write logic
8
+ - Building systems that need different data representations for reading vs. writing
9
+ - Scaling read and write workloads independently
10
+ - Implementing event sourcing alongside specialized read models
11
+
12
+ ## Steps
13
+
14
+ ### 1. Separate Command and Query Models
15
+
16
+ | Aspect | Command | Query |
17
+ |--------|---------|-------|
18
+ | Purpose | Modify state | Read state |
19
+ | Returns | Void / ACK | Data |
20
+ | Side Effects | Yes | No |
21
+ | Complexity | Business logic | Data shaping |
22
+
23
+ Commands and queries should use **different models** with different schemas optimized for their specific use case.
24
+
25
+ ### 2. Design Command Side
26
+
27
+ - Commands are **intent-based** (present tense: `PlaceOrder`, `UpdatePrice`)
28
+ - Validate business rules **before** executing
29
+ - Return success/failure, not data
30
+ - Keep command handlers small and focused
31
+
32
+ ```typescript
33
+ interface Command {
34
+ id: string; // Correlation ID
35
+ type: string; // Command type
36
+ payload: unknown; // Command data
37
+ metadata: {
38
+ userId: string;
39
+ timestamp: string;
40
+ correlationId: string;
41
+ };
42
+ }
43
+
44
+ interface CommandHandler<T extends Command> {
45
+ execute(command: T): Promise<CommandResult>;
46
+ }
47
+ ```
48
+
49
+ ### 3. Design Query Side
50
+
51
+ - Queries are **data-focused** (past/read tense: `GetUserOrders`, `FindActiveProducts`)
52
+ - Queries should be **side-effect free**
53
+ - Return **read-optimized** data structures (possibly denormalized)
54
+ - Support pagination, filtering, sorting
55
+
56
+ ```typescript
57
+ interface Query {
58
+ id: string;
59
+ type: string;
60
+ parameters: Record<string, unknown>;
61
+ pagination?: { page: number; limit: number };
62
+ sorting?: { field: string; direction: 'asc' | 'desc' }[];
63
+ }
64
+
65
+ interface QueryHandler<T extends Query> {
66
+ execute(query: T): Promise<QueryResult>;
67
+ }
68
+ ```
69
+
70
+ ### 4. Implement Synchronization
71
+
72
+ When commands and queries share data:
73
+
74
+ 1. **Synchronous** (same DB): Update the read model transactionally
75
+ 2. **Asynchronous** (event-driven): Project events to read models
76
+ 3. **Dual writes**: Update both models, handle eventual consistency
77
+
78
+ ```typescript
79
+ // Synchronous synchronization
80
+ async function placeOrder(command: PlaceOrderCommand): Promise<void> {
81
+ const order = Order.create(command.payload);
82
+
83
+ await this.transactionManager.execute(async (tx) => {
84
+ // Write to command model
85
+ await this.orderRepo.save(order, tx);
86
+
87
+ // Synchronize to read model
88
+ const readModel = {
89
+ orderId: order.id,
90
+ customerId: order.customerId,
91
+ status: order.status,
92
+ total: order.total,
93
+ placedAt: order.placedAt
94
+ };
95
+ await this.orderReadRepo.save(readModel, tx);
96
+ });
97
+ }
98
+ ```
99
+
100
+ ### 5. Handle Eventual Consistency
101
+
102
+ If read and write models are updated asynchronously:
103
+
104
+ - Document **expected consistency lag**
105
+ - Design UIs to handle stale data gracefully
106
+ - Implement **cache invalidation** strategies
107
+ - Use **version numbers** or timestamps for cache validation
108
+
109
+ ## Examples
110
+
111
+ ### Command Implementation
112
+
113
+ ```typescript
114
+ // commands/place-order.command.ts
115
+ interface PlaceOrderCommand {
116
+ orderId?: string; // Optional, generated if not provided
117
+ customerId: string;
118
+ items: OrderItem[];
119
+ paymentMethod: 'CARD' | 'PAYPAL';
120
+ }
121
+
122
+ class PlaceOrderCommandHandler implements CommandHandler<PlaceOrderCommand> {
123
+ async execute(command: PlaceOrderCommand): Promise<CommandResult> {
124
+ // 1. Validate command
125
+ const validation = this.validate(command);
126
+ if (!validation.success) {
127
+ return CommandResult.failure(validation.errors);
128
+ }
129
+
130
+ // 2. Check business invariants
131
+ const customer = await this.customerRepo.findById(command.customerId);
132
+ if (!customer.isActive) {
133
+ return CommandResult.failure('Customer account is not active');
134
+ }
135
+
136
+ // 3. Create aggregate
137
+ const order = Order.create({
138
+ id: command.orderId,
139
+ customerId: command.customerId,
140
+ items: command.items
141
+ });
142
+
143
+ // 4. Persist
144
+ await this.orderRepo.save(order);
145
+
146
+ // 5. Emit event for async processing
147
+ await this.eventBus.publish(OrderPlacedEvent.fromOrder(order));
148
+
149
+ return CommandResult.success({ orderId: order.id });
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### Query Implementation
155
+
156
+ ```typescript
157
+ // queries/get-order-details.query.ts
158
+ interface GetOrderDetailsQuery {
159
+ orderId: string;
160
+ includeItems?: boolean;
161
+ }
162
+
163
+ interface OrderDetailsReadModel {
164
+ orderId: string;
165
+ customerId: string;
166
+ customerName: string;
167
+ status: string;
168
+ total: number;
169
+ placedAt: string;
170
+ items?: OrderItemReadModel[];
171
+ }
172
+
173
+ class GetOrderDetailsQueryHandler implements QueryHandler<GetOrderDetailsQuery, OrderDetailsReadModel> {
174
+ async execute(query: GetOrderDetailsQuery): Promise<OrderDetailsReadModel> {
175
+ const order = await this.readModelRepo.findOrderWithDetails(query.orderId);
176
+
177
+ if (!order) {
178
+ throw new QueryNotFoundError('Order not found');
179
+ }
180
+
181
+ const result: OrderDetailsReadModel = {
182
+ orderId: order.orderId,
183
+ customerId: order.customerId,
184
+ customerName: order.customerName,
185
+ status: order.status,
186
+ total: order.total,
187
+ placedAt: order.placedAt
188
+ };
189
+
190
+ if (query.includeItems) {
191
+ result.items = await this.readModelRepo.findOrderItems(query.orderId);
192
+ }
193
+
194
+ return result;
195
+ }
196
+ }
197
+ ```
198
+
199
+ ### Mediator Pattern for CQRS
200
+
201
+ ```typescript
202
+ class CqrsMediator {
203
+ private commandHandlers: Map<string, CommandHandler<any>>;
204
+ private queryHandlers: Map<string, QueryHandler<any>>;
205
+
206
+ async send<T>(message: Command | Query): Promise<CommandResult | QueryResult> {
207
+ const handler = message instanceof Command
208
+ ? this.commandHandlers.get(message.type)
209
+ : this.queryHandlers.get(message.type);
210
+
211
+ if (!handler) {
212
+ throw new HandlerNotFoundError(message.type);
213
+ }
214
+
215
+ return handler.execute(message);
216
+ }
217
+ }
218
+
219
+ // Usage
220
+ const result = await mediator.send(new PlaceOrderCommand({ ... }));
221
+ const orderDetails = await mediator.send(new GetOrderDetailsQuery({ orderId: '123' }));
222
+ ```
223
+
224
+ ## Related Skills
225
+
226
+ - api-design
227
+ - event-driven-architecture
228
+ - backend-patterns
229
+ - event-sourcing
230
+ - hexagonal-architecture
@@ -0,0 +1,104 @@
1
+ # ddd-architecture
2
+
3
+ ## When to Activate
4
+ When modeling complex business domains where deep understanding of the problem space, ubiquitous language, and bounded contexts are critical for long-term maintainability.
5
+
6
+ ## Steps
7
+ 1. **Establish the bounded context** - Identify the explicit boundary within which a single model (ubiquitous language) holds.
8
+ 2. **Build the domain model** - Create entities, value objects, aggregates, and domain events that reflect real business concepts.
9
+ 3. **Define aggregates** - Group related entities and value objects under a root aggregate that enforces invariants.
10
+ 4. **Identify domain events** - Capture meaningful business occurrences that other parts of the system may need to react to.
11
+ 5. **Create domain services** - Model operations that don't naturally belong to a single entity or value object.
12
+ 6. **Define repository interfaces** - Create ports for persisting and retrieving aggregates (implementation is infrastructure).
13
+ 7. **Implement application services** - Orchestrate the domain model, handle transactions, and coordinate multiple aggregates.
14
+ 8. **Establish anti-corruption layers** - Translate between external systems (legacy, third-party) and your domain model.
15
+
16
+ ## Examples
17
+ ```typescript
18
+ // Value Object - Immutable concept with equality
19
+ class Money {
20
+ constructor(
21
+ public readonly amount: number,
22
+ public readonly currency: Currency
23
+ ) {}
24
+
25
+ static of(amount: number, currency: Currency): Money {
26
+ return new Money(Math.round(amount * 100) / 100, currency)
27
+ }
28
+
29
+ add(other: Money): Money {
30
+ if (this.currency !== other.currency) {
31
+ throw new Error('Currency mismatch')
32
+ }
33
+ return Money.of(this.amount + other.amount, this.currency)
34
+ }
35
+ }
36
+
37
+ // Aggregate Root - Enforces invariants for the aggregate
38
+ class Order extends AggregateRoot {
39
+ constructor(
40
+ private readonly id: OrderId,
41
+ private readonly customer: Customer,
42
+ private items: OrderItem[],
43
+ private status: OrderStatus
44
+ ) {
45
+ super()
46
+ this.validate()
47
+ }
48
+
49
+ private validate(): void {
50
+ if (this.items.length === 0) {
51
+ throw new DomainException('Order must have at least one item')
52
+ }
53
+ }
54
+
55
+ get total(): Money {
56
+ return this.items.reduce(
57
+ (sum, item) => sum.add(item.subtotal),
58
+ Money.of(0, Currency.USD)
59
+ )
60
+ }
61
+
62
+ // Business methods that enforce invariants
63
+ addItem(item: OrderItem): void {
64
+ if (this.status !== OrderStatus.DRAFT) {
65
+ throw new DomainException('Cannot add items to a non-draft order')
66
+ }
67
+ this.items.push(item)
68
+ this.addDomainEvent(new OrderItemAddedEvent(this.id, item))
69
+ }
70
+
71
+ submit(): void {
72
+ if (!this.canSubmit()) {
73
+ throw new DomainException('Order cannot be submitted')
74
+ }
75
+ this.status = OrderStatus.SUBMITTED
76
+ this.addDomainEvent(new OrderSubmittedEvent(this))
77
+ }
78
+
79
+ private canSubmit(): boolean {
80
+ return this.status === OrderStatus.DRAFT && this.items.length > 0
81
+ }
82
+ }
83
+
84
+ // Domain Event - Business facts that may trigger reactions
85
+ class OrderSubmittedEvent extends DomainEvent {
86
+ constructor(public readonly order: Order) {
87
+ super('order.submitted', order.id)
88
+ }
89
+ }
90
+
91
+ // Repository Interface (Port) - Persistence abstraction
92
+ interface OrderRepository {
93
+ findById(id: OrderId): Promise<Order | null>
94
+ findByCustomer(customerId: CustomerId): Promise<Order[]>
95
+ save(order: Order): Promise<void>
96
+ }
97
+ ```
98
+
99
+ ## Related Skills
100
+ - clean-architecture
101
+ - hexagonal-architecture
102
+ - layered-architecture
103
+ - saga-architecture
104
+ - backend-patterns
@@ -0,0 +1,304 @@
1
+ ---
2
+ name: django-patterns
3
+ description: Django patterns covering models, ORM queries, views, class-based views, middleware, URL routing, forms, and project structure. Activate when writing or reviewing Django code.
4
+ origin: FlowDeck
5
+ ---
6
+
7
+ # Django Patterns Skill
8
+
9
+ Idiomatic Django for production systems. Covers models, views, ORM patterns, and project layout.
10
+
11
+ ## When to Activate
12
+
13
+ Activate when:
14
+ - Writing new Django apps or services
15
+ - Reviewing Django code for correctness and idiom
16
+ - Designing model relationships and ORM queries
17
+ - Building views with class-based views or function-based views
18
+ - Configuring URL routing and middleware
19
+
20
+ ## Project Structure
21
+
22
+ ### Standard Layout
23
+
24
+ ```text
25
+ manage.py
26
+ mysite/
27
+ __init__.py
28
+ settings.py
29
+ urls.py
30
+ wsgi.py
31
+ myapp/
32
+ __init__.py
33
+ models.py
34
+ views.py
35
+ urls.py
36
+ admin.py
37
+ apps.py
38
+ ```
39
+
40
+ ### Decoupled Layout (Recommended)
41
+
42
+ ```text
43
+ manage.py
44
+ myapp/
45
+ __init__.py
46
+ models.py
47
+ views.py
48
+ urls.py
49
+ mysite/
50
+ __init__.py
51
+ settings.py
52
+ urls.py
53
+ wsgi.py
54
+ ```
55
+
56
+ Import apps as top-level modules without project prefix.
57
+
58
+ ## Models and ORM
59
+
60
+ ### Basic Model Definition
61
+
62
+ ```python
63
+ from django.db import models
64
+
65
+ class Reporter(models.Model):
66
+ full_name = models.CharField(max_length=70)
67
+
68
+ def __str__(self):
69
+ return self.full_name
70
+
71
+ class Article(models.Model):
72
+ pub_date = models.DateField()
73
+ headline = models.CharField(max_length=200)
74
+ content = models.TextField()
75
+ reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
76
+
77
+ def __str__(self):
78
+ return self.headline
79
+ ```
80
+
81
+ ### Field Types and Options
82
+
83
+ ```python
84
+ class Book(models.Model):
85
+ class Status(models.TextChoices):
86
+ DRAFT = 'draft', 'Draft'
87
+ PUBLISHED = 'published', 'Published'
88
+ ARCHIVED = 'archived', 'Archived'
89
+
90
+ title = models.CharField(max_length=200)
91
+ author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
92
+ isbn = models.CharField(max_length=13, unique=True)
93
+ published_date = models.DateField()
94
+ price = models.DecimalField(max_digits=10, decimal_places=2)
95
+ status = models.CharField(max_length=10, choices=Status.choices, default=Status.DRAFT)
96
+ tags = models.ManyToManyField('Tag', blank=True)
97
+
98
+ class Meta:
99
+ ordering = ['title']
100
+ indexes = [
101
+ models.Index(fields=['title', 'author']),
102
+ models.Index(fields=['published_date']),
103
+ ]
104
+ ```
105
+
106
+ ### QuerySet Operations
107
+
108
+ ```python
109
+ # Create
110
+ author = Author.objects.create(name="Jane Doe", email="jane@example.com")
111
+ book = Book.objects.create(title="Django Mastery", author=author, isbn="9781234567890")
112
+
113
+ # Read with filtering
114
+ published_books = Book.objects.filter(status=Book.Status.PUBLISHED)
115
+ expensive_books = Book.objects.filter(price__gte=30.00)
116
+
117
+ # Complex queries with Q objects
118
+ from django.db.models import Q, F, Count, Avg
119
+ books = Book.objects.filter(
120
+ Q(title__icontains='django') | Q(author__name__icontains='django')
121
+ ).exclude(status=Book.Status.ARCHIVED)
122
+
123
+ # Aggregations
124
+ author_stats = Author.objects.annotate(
125
+ book_count=Count('books'),
126
+ avg_price=Avg('books__price')
127
+ ).filter(book_count__gt=0)
128
+
129
+ # Update with F expressions
130
+ Book.objects.filter(pk=1).update(price=F('price') * 1.1)
131
+
132
+ # Select related (prevents N+1)
133
+ books_with_authors = Book.objects.select_related('author').all()
134
+ ```
135
+
136
+ ## Views
137
+
138
+ ### Class-Based Views
139
+
140
+ ```python
141
+ from django.http import HttpResponse
142
+ from django.views import View
143
+ from django.views.generic import ListView, DetailView, CreateView
144
+
145
+ class MyView(View):
146
+ def get(self, request, *args, **kwargs):
147
+ return HttpResponse("Hello, World!")
148
+
149
+ # URL routing
150
+ from django.urls import path
151
+ urlpatterns = [
152
+ path("about/", MyView.as_view()),
153
+ ]
154
+ ```
155
+
156
+ ### Generic Class-Based Views
157
+
158
+ ```python
159
+ class ArticleListView(ListView):
160
+ model = Article
161
+ template_name = "articles/list.html"
162
+ context_object_name = "articles"
163
+
164
+ def get_queryset(self):
165
+ return Article.objects.filter(status='published').select_related('author')
166
+
167
+ class ArticleDetailView(DetailView):
168
+ model = Article
169
+ template_name = "articles/detail.html"
170
+ context_object_name = "article"
171
+
172
+ class ArticleCreateView(CreateView):
173
+ model = Article
174
+ fields = ['title', 'content', 'author', 'status']
175
+ template_name = "articles/form.html"
176
+ success_url = reverse_lazy('article-list')
177
+ ```
178
+
179
+ ### Function-Based Views
180
+
181
+ ```python
182
+ from django.http import JsonResponse
183
+ from django.shortcuts import get_object_or_404
184
+
185
+ def article_detail(request, pk):
186
+ article = get_object_or_404(Article, pk=pk)
187
+ return JsonResponse({
188
+ 'id': article.id,
189
+ 'title': article.title,
190
+ 'content': article.content,
191
+ })
192
+ ```
193
+
194
+ ## URL Routing
195
+
196
+ ```python
197
+ from django.urls import path, include
198
+
199
+ urlpatterns = [
200
+ path("articles/", include("articles.urls")),
201
+ path("about/", AboutView.as_view(), name="about"),
202
+ ]
203
+
204
+ # In articles/urls.py
205
+ from django.urls import path
206
+ from .views import ArticleListView, ArticleDetailView
207
+
208
+ urlpatterns = [
209
+ path("", ArticleListView.as_view(), name="article-list"),
210
+ path("<int:pk>/", ArticleDetailView.as_view(), name="article-detail"),
211
+ ]
212
+ ```
213
+
214
+ ## Middleware
215
+
216
+ ### Function-Based Middleware
217
+
218
+ ```python
219
+ def simple_middleware(get_response):
220
+ def middleware(request):
221
+ # Code executed before view
222
+ response = get_response(request)
223
+ # Code executed after view
224
+ return response
225
+ return middleware
226
+ ```
227
+
228
+ ### Adding to Settings
229
+
230
+ ```python
231
+ MIDDLEWARE = [
232
+ 'django.middleware.security.SecurityMiddleware',
233
+ 'django.contrib.sessions.middleware.SessionMiddleware',
234
+ 'django.middleware.common.CommonMiddleware',
235
+ 'myapp.middleware.simple_middleware',
236
+ ]
237
+ ```
238
+
239
+ ## Forms
240
+
241
+ ### Model Forms
242
+
243
+ ```python
244
+ from django import forms
245
+ from .models import Article
246
+
247
+ class ArticleForm(forms.ModelForm):
248
+ class Meta:
249
+ model = Article
250
+ fields = ['title', 'content', 'author', 'status']
251
+
252
+ def clean_title(self):
253
+ title = self.cleaned_data['title']
254
+ if 'spam' in title.lower():
255
+ raise forms.ValidationError("No spam allowed")
256
+ return title
257
+ ```
258
+
259
+ ## Common Pitfalls
260
+
261
+ ### N+1 Query Problem
262
+
263
+ ```python
264
+ # Bad: causes N+1 queries
265
+ articles = Article.objects.all()
266
+ for article in articles:
267
+ print(article.author.name) # N additional queries
268
+
269
+ # Good: use select_related or prefetch_related
270
+ articles = Article.objects.select_related('author').all()
271
+ for article in articles:
272
+ print(article.author.name) # No additional queries
273
+ ```
274
+
275
+ ### Using Q Objects for Complex Queries
276
+
277
+ ```python
278
+ from django.db.models import Q
279
+
280
+ # OR conditions
281
+ Book.objects.filter(Q(title__icontains='django') | Q(author__name__icontains='django'))
282
+
283
+ # AND with exclusion
284
+ Book.objects.filter(status='published').exclude(Q(title__icontains='old'))
285
+ ```
286
+
287
+ ### Bulk Operations
288
+
289
+ ```python
290
+ # Create multiple objects efficiently
291
+ Book.objects.bulk_create([
292
+ Book(title='Book 1', author=author),
293
+ Book(title='Book 2', author=author),
294
+ ])
295
+
296
+ # Update multiple objects
297
+ Book.objects.filter(status='archived').update(status='published')
298
+ ```
299
+
300
+ ## Related Skills
301
+
302
+ - django-tdd
303
+ - python-patterns
304
+ - api-design